diff options
520 files changed, 38307 insertions, 19685 deletions
diff --git a/.ci/after_success.sh b/.ci/after_success.sh index e2602eac83..580b988061 100755 --- a/.ci/after_success.sh +++ b/.ci/after_success.sh @@ -7,4 +7,4 @@ if [[ -n "${CI_TARGET}" ]]; then exit fi -coveralls --gcov "$(which "${GCOV}")" --encoding iso-8859-1 || echo 'coveralls upload failed.' +[ "$USE_GCOV" = on ] && { coveralls --gcov "$(which "${GCOV}")" --encoding iso-8859-1 || echo 'coveralls upload failed.' ; } diff --git a/.ci/before_script.sh b/.ci/before_script.sh index 026ab0afc9..6babc582ea 100755 --- a/.ci/before_script.sh +++ b/.ci/before_script.sh @@ -19,6 +19,10 @@ if [[ -n "${LLVM_SYMBOLIZER}" ]] && [[ ! $(type -P "${LLVM_SYMBOLIZER}") ]]; the echo "\$LLVM_SYMBOLIZER: '${LLVM_SYMBOLIZER}' is not executable." exit 1 fi +if [ "${BUILD_32BIT}" = ON ] && [ "${BUILD_MINGW}" = ON ]; then + >&2 echo "32-bit MinGW builds not supported." + exit 1 +fi if [[ "${TRAVIS_OS_NAME}" == osx ]]; then # Adds user to a dummy group. diff --git a/.ci/common/build.sh b/.ci/common/build.sh index 3d517b9b09..06bdab707f 100644 --- a/.ci/common/build.sh +++ b/.ci/common/build.sh @@ -1,15 +1,13 @@ build_deps() { if [[ "${BUILD_32BIT}" == ON ]]; then - if [[ "${BUILD_MINGW}" == ON ]]; then - >&2 echo "32-bit MinGW builds not supported." - exit 1 - fi - DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} ${CMAKE_FLAGS_32BIT}" fi if [[ "${BUILD_MINGW}" == ON ]]; then DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} ${CMAKE_FLAGS_MINGW}" fi + if [[ "${FUNCTIONALTEST}" == "functionaltest-lua" ]]; then + DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} -DUSE_BUNDLED_LUA=ON" + fi rm -rf "${DEPS_BUILD_DIR}" @@ -42,11 +40,6 @@ build_nvim() { CMAKE_FLAGS="${CMAKE_FLAGS} -DCLANG_${CLANG_SANITIZER}=ON" fi if [[ "${BUILD_32BIT}" == ON ]]; then - if [[ "${BUILD_MINGW}" == ON ]]; then - >&2 echo "32-bit MinGW builds not supported." - exit 1 - fi - CMAKE_FLAGS="${CMAKE_FLAGS} ${CMAKE_FLAGS_32BIT}" fi if [[ "${BUILD_MINGW}" == ON ]]; then diff --git a/.ci/common/test.sh b/.ci/common/test.sh index c1bbd8dc9a..8c32b63ab2 100644 --- a/.ci/common/test.sh +++ b/.ci/common/test.sh @@ -53,7 +53,7 @@ run_unittests() { } run_functionaltests() { - if ! ${MAKE_CMD} -C "${BUILD_DIR}" functionaltest; then + if ! ${MAKE_CMD} -C "${BUILD_DIR}" ${FUNCTIONALTEST}; then asan_check "${LOG_DIR}" valgrind_check "${LOG_DIR}" exit 1 diff --git a/.ci/install.sh b/.ci/install.sh index ad3ef1b8b7..fb5d1c09c1 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -14,7 +14,9 @@ elif [[ "${BUILD_MINGW}" == ON ]]; then # binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64-dev mingw-w64-tools echo "Downloading MinGW..." - curl -sSL "http://downloads.sourceforge.net/project/mingw-w64/Toolchains%20targetting%20Win32/Personal%20Builds/rubenvb/gcc-4.8-release/i686-w64-mingw32-gcc-4.8.0-linux64_rubenvb.tar.xz" | tar xJf - -C "${HOME}/.local" + curl -sSL "https://github.com/neovim/deps/raw/master/opt/i686-w64-mingw32-gcc-4.8.0-linux64_rubenvb.tar.xz" \ + | tar xJf - -C "${HOME}/.local" + fi # Set CC to default to avoid compilation problems diff --git a/.ci/script.sh b/.ci/script.sh index 4d0666ec0e..c3c7b8dfa9 100755 --- a/.ci/script.sh +++ b/.ci/script.sh @@ -4,7 +4,7 @@ set -e set -o pipefail if [[ -n "${CI_TARGET}" ]]; then - make lint + make "${CI_TARGET}" exit 0 fi diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..b08a27f2a2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +charset = utf_8 + +[Makefile] +indent_style = tab +tab_width = 4 diff --git a/.gitignore b/.gitignore index 70899dfa8d..94bb54c661 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ tags /src/nvim/testdir/tiny.vim /src/nvim/testdir/test*.out /src/nvim/testdir/test.log +/src/nvim/testdir/messages /src/nvim/testdir/viminfo /src/nvim/testdir/test.ok /src/nvim/testdir/*.failed @@ -67,3 +68,5 @@ local.mk /runtime/doc/doctags /runtime/doc/errors.log +# clint errors, generated by `make lint` +/errors.json diff --git a/.travis.yml b/.travis.yml index 793053433f..985a5c5381 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,6 @@ env: - PATH="$(python2.7 -c 'import site; print(site.getuserbase())')/bin:$HOME/.local/mingw32/bin:$PATH" # LLVM symbolizer path. - LLVM_SYMBOLIZER="$(which llvm-symbolizer-3.6)" - # Force verification of DLOG macros. - - CFLAGS="-DMIN_LOG_LEVEL=0" # Build directory for Neovim. - BUILD_DIR="$TRAVIS_BUILD_DIR/build" # Build directory for third-party dependencies. @@ -28,9 +26,9 @@ env: - CMAKE_FLAGS="-DTRAVIS_CI_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX - -DUSE_GCOV=ON -DBUSTED_OUTPUT_TYPE=gtest - -DDEPS_PREFIX=$DEPS_BUILD_DIR/usr" + -DDEPS_PREFIX=$DEPS_BUILD_DIR/usr + -DMIN_LOG_LEVEL=0" - DEPS_CMAKE_FLAGS="-DDEPS_DOWNLOAD_DIR:PATH=$DEPS_DOWNLOAD_DIR" # Additional CMake flags for 32-bit builds. - CMAKE_FLAGS_32BIT="-DCMAKE_SYSTEM_LIBRARY_PATH=/lib32:/usr/lib32:/usr/local/lib32 @@ -55,41 +53,43 @@ env: # if the tests were successful, but don't have this information # available in before_cache (which is run before after_success). - SUCCESS_MARKER="$BUILD_DIR/.tests_successful" + # default target name for functional tests + - FUNCTIONALTEST=functionaltest matrix: include: - os: linux - env: CI_TARGET=clint + env: CI_TARGET=lint - os: linux compiler: gcc-5 - env: GCOV=gcov-5 + - os: linux + compiler: gcc-5 + env: FUNCTIONALTEST=functionaltest-lua - os: linux # Travis creates a cache per compiler. # Set a different value here to store 32-bit # dependencies in a separate cache. compiler: gcc-5 -m32 - env: GCOV=gcov-5 BUILD_32BIT=ON + env: BUILD_32BIT=ON - os: linux compiler: clang-3.6 - env: GCOV=llvm-cov-3.6 CLANG_SANITIZER=ASAN_UBSAN + env: GCOV=llvm-cov-3.6 CLANG_SANITIZER=ASAN_UBSAN CMAKE_FLAGS="$CMAKE_FLAGS -DUSE_GCOV=ON" - os: linux compiler: clang-3.6 - env: GCOV=llvm-cov-3.6 CLANG_SANITIZER=MSAN + env: CLANG_SANITIZER=MSAN - os: linux compiler: clang-3.6 env: CLANG_SANITIZER=TSAN - os: osx compiler: clang - env: GCOV=gcov - os: osx compiler: gcc-4.9 - env: GCOV=gcov-4.9 - os: linux env: BUILD_MINGW=ON fast_finish: true allow_failures: # TODO: Remove when all MSan errors have been fixed. - - env: GCOV=llvm-cov-3.6 CLANG_SANITIZER=MSAN + - env: CLANG_SANITIZER=MSAN before_install: .ci/before_install.sh install: .ci/install.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index f06d060560..98ffc77b15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,22 @@ cmake_minimum_required(VERSION 2.8.7) -project(NEOVIM) +project(nvim) + +if(POLICY CMP0059) + cmake_policy(SET CMP0059 OLD) # Needed until cmake 2.8.12. #4389 +endif() # Point CMake at any custom modules we may ship list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") # Prefer our bundled versions of dependencies. set(DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/.deps/usr" CACHE PATH "Path prefix for finding dependencies") -list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX}) -set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${DEPS_PREFIX}/lib/pkgconfig") +if(CMAKE_CROSSCOMPILING AND NOT UNIX) + list(INSERT CMAKE_FIND_ROOT_PATH 0 ${DEPS_PREFIX}) + list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX}/../host/bin) +else() + list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX}) + set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${DEPS_PREFIX}/lib/pkgconfig") +endif() # used for check_c_compiler_flag include(CheckCCompilerFlag) @@ -35,37 +44,35 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(USE_FNAME_CASE TRUE) endif() -# Set available build types for CMake GUIs. -# A different build type can still be set by -DCMAKE_BUILD_TYPE=... -set_property(CACHE CMAKE_BUILD_TYPE PROPERTY - STRINGS "Debug" "Dev" "Release" "MinSizeRel" "RelWithDebInfo") - # Set default build type. if(NOT CMAKE_BUILD_TYPE) message(STATUS "CMAKE_BUILD_TYPE not given, defaulting to 'Dev'.") set(CMAKE_BUILD_TYPE "Dev" CACHE STRING "Choose the type of build." FORCE) endif() -# Version tokens -# - In a git repo, these tokens are _ignored_. -# - If the current HEAD is tagged, the tag name is used. -# - Otherwise the result of `git describe` is used. -# - If not in a git repo (e.g. a tarball) these tokens set the version string. +# Set available build types for CMake GUIs. +# A different build type can still be set by -DCMAKE_BUILD_TYPE=... +set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "Debug" "Dev" "Release" "MinSizeRel" "RelWithDebInfo") + +# If not in a git repo (e.g., a tarball) these tokens define the complete +# version string, else it is combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) set(NVIM_VERSION_MINOR 1) -set(NVIM_VERSION_PATCH 1) +set(NVIM_VERSION_PATCH 5) +set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC NVIM_VERSION_COMMIT) -if(NVIM_VERSION_COMMIT) # is a git repo - git_get_exact_tag(NVIM_VERSION_MEDIUM) - if(NVIM_VERSION_MEDIUM) # is a tagged release - unset(NVIM_VERSION_COMMIT) - else() # is a dev build - git_describe(NVIM_VERSION_MEDIUM) - get_git_head_revision(GIT_REFSPEC NVIM_VERSION_COMMIT) - endif() +if(NVIM_VERSION_COMMIT) # is a git repo + git_describe(NVIM_VERSION_MEDIUM) + # `git describe` annotates the most recent tagged release; for pre-release + # builds we must replace that with the unreleased version. + string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+" + "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}" + NVIM_VERSION_MEDIUM + ${NVIM_VERSION_MEDIUM}) endif() set(NVIM_VERSION_BUILD_TYPE "${CMAKE_BUILD_TYPE}") @@ -150,7 +157,11 @@ if(${INIT_FLAGS_NAME}) set(CMAKE_REQUIRED_FLAGS "${${INIT_FLAGS_NAME}}") endif() +# Include <string.h> because some toolchains define _FORTIFY_SOURCE=2 in +# internal header files, which should in turn be #included by <string.h>. check_c_source_compiles(" +#include <string.h> + #if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 1 #error \"_FORTIFY_SOURCE > 1\" #endif @@ -199,11 +210,18 @@ if(MSVC) else() add_definitions(-Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99) + + # On FreeBSD 64 math.h uses unguarded C11 extension, which taints clang + # 3.4.1 used there. + if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + add_definitions(-Wno-c11-extensions) + endif() endif() if(MINGW) # Use POSIX compatible stdio in Mingw add_definitions(-D__USE_MINGW_ANSI_STDIO) + add_definitions(-D_WIN32_WINNT=0x0600) endif() # OpenBSD's GCC (4.2.1) doesn't have -Wvla @@ -212,12 +230,16 @@ if(HAS_WVLA_FLAG) add_definitions(-Wvla) endif() -check_c_compiler_flag(-fstack-protector-strong HAS_FSTACK_PROTECTOR_STRONG_FLAG) -check_c_compiler_flag(-fstack-protector HAS_FSTACK_PROTECTOR_FLAG) -if(HAS_FSTACK_PROTECTOR_STRONG_FLAG) - add_definitions(-fstack-protector-strong) -elseif(HAS_FSTACK_PROTECTOR_FLAG) - add_definitions(-fstack-protector --param ssp-buffer-size=4) +if(UNIX) + # -fstack-protector breaks non Unix builds even in Mingw-w64 + check_c_compiler_flag(-fstack-protector-strong HAS_FSTACK_PROTECTOR_STRONG_FLAG) + check_c_compiler_flag(-fstack-protector HAS_FSTACK_PROTECTOR_FLAG) + + if(HAS_FSTACK_PROTECTOR_STRONG_FLAG) + add_definitions(-fstack-protector-strong) + elseif(HAS_FSTACK_PROTECTOR_FLAG) + add_definitions(-fstack-protector --param ssp-buffer-size=4) + endif() endif() check_c_compiler_flag(-fdiagnostics-color=auto HAS_DIAG_COLOR_FLAG) @@ -246,6 +268,10 @@ if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined") + + # For O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags on older systems + # (pre POSIX.1-2008: glibc 2.11 and earlier). #4042 + add_definitions(-D_GNU_SOURCE) endif() if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "SunOS") @@ -271,7 +297,7 @@ include(CheckLibraryExists) find_package(LibUV REQUIRED) include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) -find_package(Msgpack REQUIRED) +find_package(Msgpack 1.0.0 REQUIRED) include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS}) find_package(LuaJit REQUIRED) @@ -328,13 +354,20 @@ endif() set(CMAKE_THREAD_PREFER_PTHREAD ON) find_package(Threads REQUIRED) +# Place targets in bin/ or lib/ for all build configurations set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +foreach(CFGNAME ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${CFGNAME} CFGNAME) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CFGNAME} ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CFGNAME} ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CFGNAME} ${CMAKE_BINARY_DIR}/lib) +endforeach() # Find Lua interpreter include(LuaHelpers) -set(LUA_DEPENDENCIES lpeg MessagePack bit) +set(LUA_DEPENDENCIES lpeg mpack bit) if(NOT LUA_PRG) foreach(CURRENT_LUA_PRG luajit lua) # If LUA_PRG is set find_program() will not search @@ -361,6 +394,7 @@ message(STATUS "Using the Lua interpreter ${LUA_PRG}.") # Setup busted. find_program(BUSTED_PRG busted) +find_program(BUSTED_LUA_PRG busted-lua) if(NOT BUSTED_OUTPUT_TYPE) set(BUSTED_OUTPUT_TYPE "utfTerminal") endif() @@ -377,6 +411,16 @@ install_helper( FILES ${MANPAGES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +# MIN_LOG_LEVEL for log.h +if(DEFINED MIN_LOG_LEVEL) + if(NOT MIN_LOG_LEVEL MATCHES "^[0-3]$") + message(FATAL_ERROR "MIN_LOG_LEVEL must be a number DEBUG (0), INFO (1), WARNING (2) or ERROR (3)") + endif() + message(STATUS "Log level set to ${MIN_LOG_LEVEL}") +else() + message(STATUS "Log level not specified, defaulting to INFO(1)") +endif() + # Go down the tree. add_subdirectory(src/nvim) @@ -401,7 +445,11 @@ if(BUSTED_PRG) if(POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() - get_target_property(TEST_LIBNVIM_PATH nvim-test LOCATION) + if(CMAKE_GENERATOR MATCHES "Visual Studio") + set(TEST_LIBNVIM_PATH ${CMAKE_BINARY_DIR}/lib/nvim-test.dll) + else() + get_target_property(TEST_LIBNVIM_PATH nvim-test LOCATION) + endif() configure_file( test/config/paths.lua.in @@ -458,6 +506,20 @@ if(BUSTED_PRG) DEPENDS ${BENCHMARK_PREREQS}) endif() +if(BUSTED_LUA_PRG) + add_custom_target(functionaltest-lua + COMMAND ${CMAKE_COMMAND} + -DBUSTED_PRG=${BUSTED_LUA_PRG} + -DNVIM_PRG=$<TARGET_FILE:nvim> + -DWORKING_DIR=${CMAKE_CURRENT_SOURCE_DIR} + -DBUSTED_OUTPUT_TYPE=${BUSTED_OUTPUT_TYPE} + -DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test + -DBUILD_DIR=${CMAKE_BINARY_DIR} + -DTEST_TYPE=functional + -P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake + DEPENDS ${FUNCTIONALTEST_PREREQS}) +endif() + if(LUACHECK_PRG) add_custom_target(testlint COMMAND ${CMAKE_COMMAND} @@ -465,3 +527,15 @@ if(LUACHECK_PRG) -DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test -P ${PROJECT_SOURCE_DIR}/cmake/RunTestsLint.cmake) endif() + +set(CPACK_PACKAGE_NAME "Neovim") +set(CPACK_PACKAGE_VENDOR "neovim.io") +set(CPACK_PACKAGE_VERSION ${NVIM_VERSION_MEDIUM}) +set(CPACK_PACKAGE_INSTALL_DIRECTORY "Neovim") +# Set toplevel directory/installer name as Neovim +set(CPACK_PACKAGE_FILE_NAME "Neovim") +set(CPACK_TOPLEVEL_TAG "Neovim") +set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") +set(CPACK_NSIS_MODIFY_PATH ON) +set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) +include(CPack) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aa88837290..1787a4322b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,15 +23,10 @@ If your issue isn't mentioned there: your problem persists. - If you're experiencing compile or runtime warnings/failures, try searching for the error message(s) you received (if any) on [Neovim's issue tracker][github-issues]. - - For build issues, see - [Troubleshooting#build-issues][wiki-troubleshooting-build-issues]. - - For runtime issues, see - [Troubleshooting#runtime-issues][wiki-troubleshooting-runtime-issues]. - If your issue isn't mentioned there, try reproducing it using `nvim` - with the smallest possible `vimrc` (or none at all via `nvim -u NONE`), - to rule out bugs in plugins you're using. - If you're using a plugin manager, comment out your plugins, then add - them back in one by one. +- For runtime issues, try reproducing it using `nvim` with the smallest + possible `vimrc` (or none at all via `nvim -u NONE`), to rule out bugs in + plugins you're using. If you're using a plugin manager, comment out your + plugins, then add them back in one by one. Include as much detail as possible; we generally need to know: @@ -112,10 +107,9 @@ happen to be modifying anyway. Fix anything that looks outright otherwise prefer to leave things as they are. For new code, run `make lint` (which runs [clint.py][clint]) to detect style -errors. Make sure that the file(s) you intend to be linted are not in -`clint-ignored-files.txt`. It's not perfect, so some warnings may be false -positives/negatives. To have `clint.py` ignore certain cases, put `// NOLINT` -at the end of the line. +errors. It's not perfect, so some warnings may be false positives/negatives. +To have `clint.py` ignore certain cases, put `// NOLINT` at the end of the +line. We also provide a configuration file for [`clang-format`][clang-format], which can be used to format code according to the style guidelines. Be aware that @@ -196,6 +190,4 @@ such as [`tig`][tig]. [wiki-contributing]: https://github.com/neovim/neovim/wiki/Contributing [wiki-faq]: https://github.com/neovim/neovim/wiki/FAQ [wiki-review-checklist]: https://github.com/neovim/neovim/wiki/Code-review-checklist -[wiki-troubleshooting-build-issues]: https://github.com/neovim/neovim/wiki/Troubleshooting#build-issues -[wiki-troubleshooting-runtime-issues]: https://github.com/neovim/neovim/wiki/Troubleshooting#runtime-issues [wiki-troubleshooting]: https://github.com/neovim/neovim/wiki/Troubleshooting diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..d9fd758177 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,16 @@ +- Neovim version: +- [ ] Vim behaves differently? Vim version: +- Operating system/version: +- Terminal name/version: +- `$TERM`: + +### Actual behaviour + +### Expected behaviour + +### Steps to reproduce using `nvim -u NORC` + +``` +nvim -u NORC + +``` @@ -45,6 +45,11 @@ ifneq (,$(USE_BUNDLED_DEPS)) BUNDLED_CMAKE_FLAG := -DUSE_BUNDLED=$(USE_BUNDLED_DEPS) endif +ifneq (,$(findstring functionaltest-lua,$(MAKECMDGOALS))) + BUNDLED_LUA_CMAKE_FLAG := -DUSE_BUNDLED_LUA=ON + $(shell [ -x .deps/usr/bin/lua ] || rm build/.ran-*) +endif + # For use where we want to make sure only a single job is run. This does issue # a warning, but we need to keep SCRIPTS argument. SINGLE_MAKE = export MAKEFLAGS= ; $(MAKE) @@ -74,7 +79,7 @@ build/.ran-third-party-cmake: ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),) mkdir -p .deps cd .deps && \ - cmake -G '$(BUILD_TYPE)' $(BUNDLED_CMAKE_FLAG) \ + cmake -G '$(BUILD_TYPE)' $(BUNDLED_CMAKE_FLAG) $(BUNDLED_LUA_CMAKE_FLAG) \ $(DEPS_CMAKE_FLAGS) ../third-party endif mkdir -p build @@ -86,7 +91,10 @@ oldtest: | nvim functionaltest: | nvim +$(BUILD_CMD) -C build functionaltest -testlint: | nvim +functionaltest-lua: | nvim + +$(BUILD_CMD) -C build functionaltest-lua + +testlint: | build/.ran-cmake deps $(BUILD_CMD) -C build testlint unittest: | nvim @@ -107,10 +115,12 @@ distclean: clean install: | nvim +$(BUILD_CMD) -C build install -lint: +clint: cmake -DLINT_PRG=./clint.py \ -DLINT_DIR=src \ -DLINT_SUPPRESS_URL="$(DOC_DOWNLOAD_URL_BASE)$(CLINT_ERRORS_FILE_PATH)" \ -P cmake/RunLint.cmake -.PHONY: test testlint functionaltest unittest lint clean distclean nvim libnvim cmake deps install +lint: clint testlint + +.PHONY: test testlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install @@ -191,6 +191,7 @@ _ERROR_CATEGORIES = [ 'readability/nul', 'readability/todo', 'readability/utf8', + 'readability/increment', 'runtime/arrays', 'runtime/int', 'runtime/invalid_increment', @@ -2291,6 +2292,11 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # there's too little whitespace, we get concerned. It's hard to tell, # though, so we punt on this one for now. TODO. + match = Search(r'(?:[^ (*/![])+(?<!\+\+|--)\*', line) + if match: + error(filename, linenum, 'whitespace/operators', 2, + 'Missing space before asterisk in %s' % match.group(0)) + # You should always have whitespace around binary operators. # # Check <= and >= first to avoid false positives with < and >, then @@ -2299,6 +2305,13 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around %s' % match.group(1)) + + # Boolean operators should be placed on the next line. + if Search(r'(?:&&|\|\|)$', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Boolean operator should be placed on the same line as the start ' + 'of its right operand') + # We allow no-spaces around << when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) # Also ignore using ns::operator<<; @@ -2552,7 +2565,7 @@ def CheckBraces(filename, clean_lines, linenum, error): # If should always have a brace for blockstart in ('if', 'while', 'for'): - if Match(r'\s*{0}[^{{]*$'.format(blockstart), line): + if Match(r'\s*{0}(?!\w)[^{{]*$'.format(blockstart), line): pos = line.find(blockstart) pos = line.find('(', pos) if pos > 0: @@ -3194,6 +3207,23 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, error(filename, linenum, 'readability/bool', 4, 'Use %s instead of %s.' % (token.lower(), token)) + # Detect preincrement/predecrement + match = Match(r'^\s*(?:\+\+|--)', line) + if match: + error(filename, linenum, 'readability/increment', 5, + 'Do not use preincrement in statements, ' + 'use postincrement instead') + # Detect preincrement/predecrement in for(;; preincrement) + match = Search(r';\s*(\+\+|--)', line) + if match: + end_pos, end_depth = FindEndOfExpressionInLine(line, match.start(1), 1, + '(', ')') + expr = line[match.start(1):end_pos] + if end_depth == 0 and ';' not in expr and ' = ' not in expr: + error(filename, linenum, 'readability/increment', 4, + 'Do not use preincrement in statements, including ' + 'for(;; action)') + def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, @@ -3316,7 +3346,7 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): _SetVerboseLevel(vlevel) try: - # Support the UNIX convention of using "-" for stdin. Note that + # Support the Unix convention of using "-" for stdin. Note that # we are not opening the file with universal newline support # (which codecs doesn't support anyway), so the resulting lines do # contain trailing '\r' characters if we are reading a file that @@ -3484,6 +3514,7 @@ def main(): if __name__ == '__main__': main() +# vim: ts=4 sts=4 sw=4 # Ignore "too complex" warnings when using pymode. # pylama:ignore=C901 diff --git a/cmake/FindMsgpack.cmake b/cmake/FindMsgpack.cmake index 7d88137915..015737d658 100644 --- a/cmake/FindMsgpack.cmake +++ b/cmake/FindMsgpack.cmake @@ -7,7 +7,9 @@ if(NOT MSGPACK_USE_BUNDLED) find_package(PkgConfig) if (PKG_CONFIG_FOUND) - pkg_check_modules(PC_MSGPACK QUIET msgpack) + pkg_search_module(PC_MSGPACK QUIET + msgpackc>=${Msgpack_FIND_VERSION} + msgpack>=${Msgpack_FIND_VERSION}) endif() else() set(PC_MSGPACK_INCLUDEDIR) @@ -19,19 +21,33 @@ endif() set(MSGPACK_DEFINITIONS ${PC_MSGPACK_CFLAGS_OTHER}) -find_path(MSGPACK_INCLUDE_DIR msgpack.h +find_path(MSGPACK_INCLUDE_DIR msgpack/version_master.h HINTS ${PC_MSGPACK_INCLUDEDIR} ${PC_MSGPACK_INCLUDE_DIRS} ${LIMIT_SEARCH}) -# If we're asked to use static linkage, add libmsgpack.a as a preferred library name. +if(MSGPACK_INCLUDE_DIR) + file(READ ${MSGPACK_INCLUDE_DIR}/msgpack/version_master.h msgpack_version_h) + string(REGEX REPLACE ".*MSGPACK_VERSION_MAJOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MAJOR "${msgpack_version_h}") + string(REGEX REPLACE ".*MSGPACK_VERSION_MINOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MINOR "${msgpack_version_h}") + string(REGEX REPLACE ".*MSGPACK_VERSION_REVISION +([0-9]+).*" "\\1" MSGPACK_VERSION_REVISION "${msgpack_version_h}") + set(MSGPACK_VERSION_STRING "${MSGPACK_VERSION_MAJOR}.${MSGPACK_VERSION_MINOR}.${MSGPACK_VERSION_REVISION}") +else() + set(MSGPACK_VERSION_STRING) +endif() + +# If we're asked to use static linkage, add libmsgpack{,c}.a as a preferred library name. if(MSGPACK_USE_STATIC) list(APPEND MSGPACK_NAMES + "${CMAKE_STATIC_LIBRARY_PREFIX}msgpackc${CMAKE_STATIC_LIBRARY_SUFFIX}" "${CMAKE_STATIC_LIBRARY_PREFIX}msgpack${CMAKE_STATIC_LIBRARY_SUFFIX}") endif() -list(APPEND MSGPACK_NAMES msgpack) +list(APPEND MSGPACK_NAMES msgpackc msgpack) find_library(MSGPACK_LIBRARY NAMES ${MSGPACK_NAMES} + # Check each directory for all names to avoid using headers/libraries from + # different places. + NAMES_PER_DIR HINTS ${PC_MSGPACK_LIBDIR} ${PC_MSGPACK_LIBRARY_DIRS} ${LIMIT_SEARCH}) @@ -43,6 +59,7 @@ set(MSGPACK_INCLUDE_DIRS ${MSGPACK_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set MSGPACK_FOUND to TRUE # if all listed variables are TRUE -find_package_handle_standard_args(Msgpack DEFAULT_MSG - MSGPACK_LIBRARY MSGPACK_INCLUDE_DIR) +find_package_handle_standard_args(Msgpack + REQUIRED_VARS MSGPACK_LIBRARY MSGPACK_INCLUDE_DIR + VERSION_VAR MSGPACK_VERSION_STRING) diff --git a/cmake/RunLint.cmake b/cmake/RunLint.cmake index 42ef7a86ad..306e938232 100644 --- a/cmake/RunLint.cmake +++ b/cmake/RunLint.cmake @@ -2,7 +2,11 @@ get_filename_component(LINT_DIR ${LINT_DIR} ABSOLUTE) get_filename_component(LINT_PREFIX ${LINT_DIR} PATH) set(LINT_SUPPRESS_FILE "${LINT_PREFIX}/errors.json") -file(GLOB_RECURSE LINT_FILES ${LINT_DIR}/*.c ${LINT_DIR}/*.h) +if(DEFINED ENV{LINT_FILE}) + file(GLOB_RECURSE LINT_FILES "$ENV{LINT_FILE}") +else() + file(GLOB_RECURSE LINT_FILES ${LINT_DIR}/*.c ${LINT_DIR}/*.h) +endif() set(LINT_ARGS) diff --git a/cmake/RunTestsLint.cmake b/cmake/RunTestsLint.cmake index cf5465803e..addc9ab35e 100644 --- a/cmake/RunTestsLint.cmake +++ b/cmake/RunTestsLint.cmake @@ -1,5 +1,7 @@ +set(IGNORE_FILES "${TEST_DIR}/*/preload.lua") + execute_process( - COMMAND ${LUACHECK_PRG} -q ${TEST_DIR} + COMMAND ${LUACHECK_PRG} -q ${TEST_DIR} --exclude-files ${IGNORE_FILES} WORKING_DIRECTORY ${TEST_DIR} ERROR_VARIABLE err RESULT_VARIABLE res diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index b780291264..e794a8c5b9 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -8,7 +8,6 @@ include(CheckCSourceCompiles) check_type_size("int" SIZEOF_INT) check_type_size("long" SIZEOF_LONG) check_type_size("intmax_t" SIZEOF_INTMAX_T) -check_type_size("off_t" SIZEOF_OFF_T) check_type_size("size_t" SIZEOF_SIZE_T) check_type_size("long long" SIZEOF_LONG_LONG) check_type_size("void *" SIZEOF_VOID_PTR) @@ -16,29 +15,21 @@ check_type_size("void *" SIZEOF_VOID_PTR) check_symbol_exists(_NSGetEnviron crt_externs.h HAVE__NSGETENVIRON) # Headers -check_include_files(dirent.h HAVE_DIRENT_H) -check_include_files(fcntl.h HAVE_FCNTL_H) check_include_files(iconv.h HAVE_ICONV_H) check_include_files(langinfo.h HAVE_LANGINFO_H) -check_include_files(libgen.h HAVE_LIBGEN_H) check_include_files(locale.h HAVE_LOCALE_H) check_include_files(pwd.h HAVE_PWD_H) check_include_files(strings.h HAVE_STRINGS_H) -check_include_files(stropts.h HAVE_STROPTS_H) -check_include_files(sys/param.h HAVE_SYS_PARAM_H) check_include_files(sys/wait.h HAVE_SYS_WAIT_H) if(NOT HAVE_SYS_WAIT_H AND UNIX) # See if_cscope.c - message(SEND_ERROR "header sys/wait.h is required for UNIX") + message(SEND_ERROR "header sys/wait.h is required for Unix") endif() check_include_files(sys/utsname.h HAVE_SYS_UTSNAME_H) check_include_files(utime.h HAVE_UTIME_H) -check_include_files(unistd.h HAVE_UNISTD_H) -check_include_files(utime.h HAVE_UTIME_H) # Functions check_function_exists(fseeko HAVE_FSEEKO) -check_function_exists(fsync HAVE_FSYNC) check_function_exists(getpwent HAVE_GETPWENT) check_function_exists(getpwnam HAVE_GETPWNAM) check_function_exists(getpwuid HAVE_GETPWUID) @@ -51,7 +42,10 @@ if(JEMALLOC_FOUND) set(HAVE_JEMALLOC 1) endif() -check_function_exists(putenv HAVE_PUTENV) +check_function_exists(_putenv_s HAVE_PUTENV_S) +if(WIN32 AND NOT HAVE_PUTENV_S) + message(SEND_ERROR "_putenv_s() function not found on your system.") +endif() check_function_exists(opendir HAVE_OPENDIR) check_function_exists(readlink HAVE_READLINK) check_function_exists(setenv HAVE_SETENV) @@ -73,6 +67,14 @@ if(HAVE_LANGINFO_H) check_symbol_exists(CODESET "langinfo.h" HAVE_NL_LANGINFO_CODESET) endif() +check_include_files("endian.h" HAVE_ENDIAN_H) +check_include_files("sys/endian.h" HAVE_SYS_ENDIAN_H) + +set(ENDIAN_INCLUDE_FILE "endian.h") +if(HAVE_SYS_ENDIAN_H AND NOT HAVE_ENDIAN_H) + set(ENDIAN_INCLUDE_FILE "sys/endian.h") +endif() + set(SI "#include <stdint.h>\n") set(MS "int main(int argc,char**argv)\n{\n uint64_t i=0x0102030405060708ULL;") set(ME "}") @@ -80,7 +82,7 @@ check_c_source_compiles(" #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 ${SI} - #include <endian.h> + #include <${ENDIAN_INCLUDE_FILE}> #ifndef be64toh # error No be64toh macros #endif @@ -98,10 +100,10 @@ endif() if (NOT "${HAVE_BE64TOH}") if (NOT "${CMAKE_CROSSCOMPILING}") # It is safe to make ORDER_BIG_ENDIAN not defined if - # - HAVE_BE64TOH is true. In this case be64toh will be used unconditionally in + # - HAVE_BE64TOH is true. In this case be64toh will be used unconditionally in # any case and ORDER_BIG_ENDIAN will not be examined. - # - CMAKE_CROSSCOMPILING *and* HAVE_BE64TOH are both false. In this case - # be64toh function which uses cycle and arithmetic operations is used which + # - CMAKE_CROSSCOMPILING *and* HAVE_BE64TOH are both false. In this case + # be64toh function which uses cycle and arithmetic operations is used which # will work regardless of endianess. Function is sub-optimal though. check_c_source_runs(" ${SI} diff --git a/config/config.h.in b/config/config.h.in index 7d901180b4..5c5b008f7e 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -5,7 +5,6 @@ #define SIZEOF_INT @SIZEOF_INT@ #define SIZEOF_LONG @SIZEOF_LONG@ -#define SIZEOF_OFF_T @SIZEOF_OFF_T@ #if @SIZEOF_VOID_PTR@ == 8 #define ARCH_64 @@ -13,23 +12,22 @@ #define ARCH_32 #endif +#define PROJECT_NAME "@PROJECT_NAME@" +#define LOCALE_INSTALL_DIR "@CMAKE_INSTALL_FULL_LOCALEDIR@" + #cmakedefine HAVE__NSGETENVIRON -#cmakedefine HAVE_CRT_EXTERNS_H -#cmakedefine HAVE_FCNTL_H #cmakedefine HAVE_FD_CLOEXEC #cmakedefine HAVE_FSEEKO -#cmakedefine HAVE_FSYNC #cmakedefine HAVE_GETPWENT #cmakedefine HAVE_GETPWNAM #cmakedefine HAVE_GETPWUID #cmakedefine HAVE_ICONV #cmakedefine HAVE_ICONV_H #cmakedefine HAVE_LANGINFO_H -#cmakedefine HAVE_LIBGEN_H #cmakedefine HAVE_LOCALE_H #cmakedefine HAVE_NL_LANGINFO_CODESET #cmakedefine HAVE_NL_MSG_CAT_CNTR -#cmakedefine HAVE_PUTENV +#cmakedefine HAVE_PUTENV_S #cmakedefine HAVE_PWD_H #cmakedefine HAVE_READLINK // TODO: add proper cmake check @@ -42,11 +40,8 @@ #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE_STRINGS_H #cmakedefine HAVE_STRNCASECMP -#cmakedefine HAVE_STROPTS_H -#cmakedefine HAVE_SYS_PARAM_H #cmakedefine HAVE_SYS_UTSNAME_H #cmakedefine HAVE_SYS_WAIT_H -#cmakedefine HAVE_UNISTD_H #cmakedefine HAVE_UTIME #cmakedefine HAVE_UTIME_H #cmakedefine HAVE_UTIMES @@ -62,5 +57,6 @@ #cmakedefine HAVE_BE64TOH #cmakedefine ORDER_BIG_ENDIAN +#define ENDIAN_INCLUDE_FILE <@ENDIAN_INCLUDE_FILE@> #endif // AUTO_CONFIG_H diff --git a/config/versiondef.h.in b/config/versiondef.h.in index 7f37ef72fb..c91bb29c90 100644 --- a/config/versiondef.h.in +++ b/config/versiondef.h.in @@ -5,7 +5,6 @@ #define NVIM_VERSION_MINOR @NVIM_VERSION_MINOR@ #define NVIM_VERSION_PATCH @NVIM_VERSION_PATCH@ #define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@" -#cmakedefine NVIM_VERSION_COMMIT "@NVIM_VERSION_COMMIT@" #cmakedefine NVIM_VERSION_MEDIUM "@NVIM_VERSION_MEDIUM@" #define NVIM_VERSION_CFLAGS "@NVIM_VERSION_CFLAGS@" diff --git a/contrib/YouCompleteMe/ycm_extra_conf.py b/contrib/YouCompleteMe/ycm_extra_conf.py index 12ad080143..7c54677c8f 100644 --- a/contrib/YouCompleteMe/ycm_extra_conf.py +++ b/contrib/YouCompleteMe/ycm_extra_conf.py @@ -9,7 +9,7 @@ def DirectoryOfThisScript(): def GetDatabase(): compilation_database_folder = os.path.join(DirectoryOfThisScript(), - '..', '..', 'build') + '..', 'build') if os.path.exists(compilation_database_folder): return ycm_core.CompilationDatabase(compilation_database_folder) return None diff --git a/contrib/local.mk.example b/contrib/local.mk.example index 22aac4fda7..a0b2d034e1 100644 --- a/contrib/local.mk.example +++ b/contrib/local.mk.example @@ -31,6 +31,11 @@ # # CMAKE_BUILD_TYPE := Debug +# By default, nvim's log level is INFO (1) (unless CMAKE_BUILD_TYPE is +# "Release", in which case logging is disabled). +# The log level must be a number DEBUG (0), INFO (1), WARNING (2) or ERROR (3). +# CMAKE_EXTRA_FLAGS += -DMIN_LOG_LEVEL=1 + # By default, nvim uses bundled versions of its required third-party # dependencies. # Uncomment these entries to instead use system-wide installations of diff --git a/uncrustify.cfg b/contrib/uncrustify.cfg index 11da34d59a..11da34d59a 100644 --- a/uncrustify.cfg +++ b/contrib/uncrustify.cfg diff --git a/man/nvim.1 b/man/nvim.1 index 968da04d52..2fa3ab8ff5 100644 --- a/man/nvim.1 +++ b/man/nvim.1 @@ -1,4 +1,4 @@ -.Dd November 11, 2015 +.Dd January 28, 2016 .Dt NVIM 1 .Os .Sh NAME @@ -75,7 +75,7 @@ Can be used to edit files starting with a hyphen .Pq Sq - . .It Fl -literal Interpret filenames literally, that is, do not expand wildcards. -Has no effect on UNIX-like systems, where the shell expands wildcards. +Has no effect on Unix-like systems, where the shell expands wildcards. .It Fl e Ex mode. See @@ -373,11 +373,24 @@ Used to set the 'shell' option, which determines the shell used by the command. .It Ev NVIM_TUI_ENABLE_CURSOR_SHAPE If defined, change the cursor shape to a vertical bar while in insert mode. -Requires that the host terminal support the DECSCUSR CSI escape sequence. -Has no effect in GUIs. -.It Ev NVIM_TUI_ENABLE_TRUE_COLOR -If defined, assume the host terminal supports 24 bit colors. +Requires that the host terminal supports the DECSCUSR CSI escape sequence. Has no effect in GUIs. +.Pp +Depending on the terminal emulator, using this option with +.Nm +under +.Xr tmux 1 +might require adding the following to +.Pa ~/.tmux.conf : +.Bd -literal -offset indent +set -ga terminal-overrides ',*:Ss=\eE[%p1%d q:Se=\eE[2 q' +.Ed +.Pp +See +.Ic terminal-overrides +in the +.Xr tmux 1 +manual page for more information. .El .Sh FILES .Bl -tag -width "~/.config/nvim/init.vim" diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index ef8e38b553..cad8da6ffb 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -26,24 +26,21 @@ foreach(DF ${DOCFILES}) list(APPEND BUILDDOCFILES ${GENERATED_RUNTIME_DIR}/doc/${F}) endforeach() -add_custom_command(OUTPUT ${BUILDDOCFILES} - COMMAND ${CMAKE_COMMAND} -E copy_directory +add_custom_command(OUTPUT copy_docfiles + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/runtime/doc ${GENERATED_RUNTIME_DIR}/doc - DEPENDS - ${DOCFILES}) +) add_custom_command(OUTPUT ${GENERATED_HELP_TAGS} - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${PROJECT_SOURCE_DIR}/runtime/doc ${GENERATED_RUNTIME_DIR}/doc COMMAND "${PROJECT_BINARY_DIR}/bin/nvim" -u NONE -i NONE - -es + -e --headless -c "helptags ++t ." -c quit DEPENDS - ${BUILDDOCFILES} + copy_docfiles nvim WORKING_DIRECTORY "${GENERATED_RUNTIME_DIR}/doc" ) diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index d481d9eaf0..0dfcc424e2 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -11,6 +11,8 @@ catch /E145:/ " Ignore the error in restricted mode endtry +" Load man page {page} from {section} +" call man#get_page([{section}, ]{page}) function man#get_page(...) abort let invoked_from_man = (&filetype ==# 'man') @@ -20,21 +22,14 @@ function man#get_page(...) abort elseif a:0 > 2 echoerr 'too many arguments' return - elseif a:0 == 2 - let [page, sect] = [a:2, 0 + a:1] - elseif type(1) == type(a:1) - let [page, sect] = ['<cword>', a:1] - else - let [page, sect] = [a:1, ''] endif - if page == '<cword>' - let page = expand('<cword>') - endif + let sect = get(a:000, 0) + let page = get(a:000, 1, sect) let [page, sect] = s:parse_page_and_section(sect, page) - if 0 + sect > 0 && s:find_page(sect, page) == 0 + if !empty(sect) && s:find_page(sect, page) == 0 let sect = '' endif @@ -54,9 +49,9 @@ function man#get_page(...) abort let thiswin = winnr() wincmd b if winnr() > 1 - exe "norm! " . thiswin . "\<C-W>w" + exec thiswin . 'wincmd w' while 1 - if &filetype == 'man' + if &filetype ==# 'man' break endif wincmd w @@ -75,14 +70,16 @@ function man#get_page(...) abort setlocal modifiable silent keepjumps norm! 1G"_dG - let $MANWIDTH = winwidth(0) + if empty($MANWIDTH) + let $MANWIDTH = winwidth(0) + endif silent exec 'r!/usr/bin/man '.s:cmd(sect, page).' | col -b' " Remove blank lines from top and bottom. - while getline(1) =~ '^\s*$' - silent keepjumps norm! gg"_dd + while getline(1) =~# '^\s*$' + silent keepjumps 1delete _ endwhile - while getline('$') =~ '^\s*$' - silent keepjumps norm! G"_dd + while getline('$') =~# '^\s*$' + silent keepjumps $delete _ endwhile setlocal nomodified setlocal filetype=man @@ -116,15 +113,11 @@ endfunction " Expects a string like 'access' or 'access(2)'. function s:parse_page_and_section(sect, str) abort try - let save_isk = &iskeyword - setlocal iskeyword-=(,) - let page = substitute(a:str, '(*\(\k\+\).*', '\1', '') - let sect = substitute(a:str, '\(\k\+\)(\([^()]*\)).*', '\2', '') - if sect == page || -1 == match(sect, '^[0-9 ]\+$') + let [page, sect] = matchlist(a:str, '\v\C([-.[:alnum:]_]+)%(\(([-.[:alnum:]_]+)\))?')[1:2] + if empty(sect) let sect = a:sect endif catch - let &l:iskeyword = save_isk echoerr 'man.vim: failed to parse: "'.a:str.'"' endtry @@ -132,7 +125,7 @@ function s:parse_page_and_section(sect, str) abort endfunction function s:cmd(sect, page) abort - if 0 + a:sect > 0 + if !empty(a:sect) return s:man_sect_arg.' '.a:sect.' '.a:page endif return a:page @@ -140,10 +133,5 @@ endfunction function s:find_page(sect, page) abort let where = system('/usr/bin/man '.s:man_find_arg.' '.s:cmd(a:sect, a:page)) - if where !~ "^/" - if matchstr(where, " [^ ]*$") !~ "^ /" - return 0 - endif - endif - return 1 + return (where =~# '^ */') endfunction diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim index e6022922fe..2e2697c57f 100644 --- a/runtime/autoload/msgpack.vim +++ b/runtime/autoload/msgpack.vim @@ -356,6 +356,8 @@ let s:MSGPACK_STANDARD_TYPES = { \type(''): 'binary', \type([]): 'array', \type({}): 'map', + \type(v:true): 'boolean', + \type(v:null): 'nil', \} "" @@ -379,7 +381,7 @@ endfunction "" " Dump boolean value. function s:msgpack_dump_boolean(v) abort - return a:v._VAL ? 'TRUE' : 'FALSE' + return (a:v is v:true || (a:v isnot v:false && a:v._VAL)) ? 'TRUE' : 'FALSE' endfunction "" @@ -395,7 +397,8 @@ endfunction "" " Dump floating-point value. function s:msgpack_dump_float(v) abort - return string(type(a:v) == type({}) ? a:v._VAL : a:v) + return substitute(string(type(a:v) == type({}) ? a:v._VAL : a:v), + \'\V\^\(-\)\?str2float(''\(inf\|nan\)'')\$', '\1\2', '') endfunction "" diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index 42439a57d3..53668b15be 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -1,10 +1,10 @@ " netrw.vim: Handles file transfer and remote directory listing across " AUTOLOAD SECTION -" Date: Jan 05, 2015 -" Version: 153 +" Date: Oct 23, 2015 +" Version: 154 " Maintainer: Charles E Campbell <NdrOchip@ScampbellPfamily.AbizM-NOSPAM> " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim -" Copyright: Copyright (C) 1999-2013 Charles E. Campbell {{{1 +" Copyright: Copyright (C) 1999-2015 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright " notice is copied with it. Like anything else that's free, @@ -13,7 +13,7 @@ " expressed or implied. By using this plugin, you agree that " in no event will the copyright holder be liable for any damages " resulting from the use of this software. -"redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore() +"redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore(,'~'.expand("<slnum>")) " " But be doers of the Word, and not only hearers, deluding your own selves {{{1 " (James 1:22 RSV) @@ -22,14 +22,15 @@ if &cp || exists("g:loaded_netrw") finish endif +" netrw requires vim having patch 213; netrw will benefit from vim's having patch#656, too if v:version < 704 || !has("patch213") if !exists("s:needpatch213") - echo "***sorry*** this version of netrw requires vim v7.4 with patch 213" + unsilent echomsg "***sorry*** this version of netrw requires vim v7.4 with patch 213" endif let s:needpatch213= 1 finish endif -let g:loaded_netrw = "v153" +let g:loaded_netrw = "v154" if !exists("s:NOTE") let s:NOTE = 0 let s:WARNING = 1 @@ -38,9 +39,9 @@ endif let s:keepcpo= &cpo setl cpo&vim -"let g:dechofuncname=1 +"let g:dechofuncname= 1 "DechoRemOn -"call Decho("doing autoload/netrw.vim version ".g:loaded_netrw) +"call Decho("doing autoload/netrw.vim version ".g:loaded_netrw,'~'.expand("<slnum>")) " ====================== " Netrw Variables: {{{1 @@ -54,7 +55,7 @@ setl cpo&vim " Usage: netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,"some message",error-number) " netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,["message1","message2",...],error-number) " (this function can optionally take a list of messages) -" May 15, 2014 : max errnum currently is 98 +" Oct 09, 2015 : max errnum currently is 102 fun! netrw#ErrorMsg(level,msg,errnum) " call Dfunc("netrw#ErrorMsg(level=".a:level." msg<".a:msg."> errnum=".a:errnum.") g:netrw_use_errorwindow=".g:netrw_use_errorwindow) @@ -70,7 +71,7 @@ fun! netrw#ErrorMsg(level,msg,errnum) else let level= "**note** (netrw) " endif -" call Decho("level=".level) +" call Decho("level=".level,'~'.expand("<slnum>")) if g:netrw_use_errorwindow " (default) netrw creates a one-line window to show error/warning @@ -78,14 +79,14 @@ fun! netrw#ErrorMsg(level,msg,errnum) " record current window number for NetrwRestorePosn()'s benefit let s:winBeforeErr= winnr() -" call Decho("s:winBeforeErr=".s:winBeforeErr) +" call Decho("s:winBeforeErr=".s:winBeforeErr,'~'.expand("<slnum>")) " getting messages out reliably is just plain difficult! " This attempt splits the current window, creating a one line window. if bufexists("NetrwMessage") && bufwinnr("NetrwMessage") > 0 -" call Decho("write to NetrwMessage buffer") +" call Decho("write to NetrwMessage buffer",'~'.expand("<slnum>")) exe bufwinnr("NetrwMessage")."wincmd w" -" call Decho("setl ma noro") +" call Decho("setl ma noro",'~'.expand("<slnum>")) setl ma noro if type(a:msg) == 3 for msg in a:msg @@ -96,13 +97,13 @@ fun! netrw#ErrorMsg(level,msg,errnum) endif NetrwKeepj $ else -" call Decho("create a NetrwMessage buffer window") +" call Decho("create a NetrwMessage buffer window",'~'.expand("<slnum>")) bo 1split sil! call s:NetrwEnew() sil! NetrwKeepj call s:NetrwSafeOptions() setl bt=nofile NetrwKeepj file NetrwMessage -" call Decho("setl ma noro") +" call Decho("setl ma noro",'~'.expand("<slnum>")) setl ma noro if type(a:msg) == 3 for msg in a:msg @@ -113,7 +114,7 @@ fun! netrw#ErrorMsg(level,msg,errnum) endif NetrwKeepj $ endif -" call Decho("wrote msg<".level.a:msg."> to NetrwMessage win#".winnr()) +" call Decho("wrote msg<".level.a:msg."> to NetrwMessage win#".winnr(),'~'.expand("<slnum>")) if &fo !~ '[ta]' syn clear syn match netrwMesgNote "^\*\*note\*\*" @@ -122,7 +123,7 @@ fun! netrw#ErrorMsg(level,msg,errnum) hi link netrwMesgWarning WarningMsg hi link netrwMesgError Error endif -" call Decho("setl noma ro bh=wipe") +" call Decho("setl noma ro bh=wipe",'~'.expand("<slnum>")) setl ro nomod noma bh=wipe else @@ -137,13 +138,13 @@ fun! netrw#ErrorMsg(level,msg,errnum) if type(a:msg) == 3 for msg in a:msg - echomsg level.msg + unsilent echomsg level.msg endfor else - echomsg level.a:msg + unsilent echomsg level.a:msg endif -" call Decho("echomsg ***netrw*** ".a:msg) +" call Decho("echomsg ***netrw*** ".a:msg,'~'.expand("<slnum>")) echohl None endif @@ -154,7 +155,7 @@ endfun " s:NetrwInit: initializes variables if they haven't been defined {{{2 " Loosely, varname = value. fun s:NetrwInit(varname,value) -" call Decho("varname<".a:varname."> value=".a:value) +" call Decho("varname<".a:varname."> value=".a:value,'~'.expand("<slnum>")) if !exists(a:varname) if type(a:value) == 0 exe "let ".a:varname."=".a:value @@ -345,7 +346,7 @@ if !exists("g:netrw_list_cmd") let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa" endif else -" call Decho(g:netrw_ssh_cmd." is not executable") +" call Decho(g:netrw_ssh_cmd." is not executable",'~'.expand("<slnum>")) let g:netrw_list_cmd= "" endif endif @@ -504,6 +505,7 @@ endif call s:NetrwInit("g:NetrwTopLvlMenu","Netrw.") call s:NetrwInit("g:netrw_win95ftp",1) call s:NetrwInit("g:netrw_winsize",50) +call s:NetrwInit("g:netrw_wiw",1) if g:netrw_winsize > 100|let g:netrw_winsize= 100|endif " --------------------------------------------------------------------- " Default values for netrw's script variables: {{{2 @@ -531,18 +533,18 @@ endif " Netrw Initialization: {{{1 " ====================== if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on") -" call Decho("installed beval events") +" call Decho("installed beval events",'~'.expand("<slnum>")) let &l:bexpr = "netrw#BalloonHelp()" au FileType netrw setl beval au WinLeave * if &ft == "netrw" && exists("s:initbeval")|let &beval= s:initbeval|endif au VimEnter * let s:initbeval= &beval "else " Decho -" if v:version < 700 | call Decho("did not install beval events: v:version=".v:version." < 700") | endif -" if !has("balloon_eval") | call Decho("did not install beval events: does not have balloon_eval") | endif -" if exists("s:initbeval") | call Decho("did not install beval events: s:initbeval exists") | endif -" if exists("g:netrw_nobeval") | call Decho("did not install beval events: g:netrw_nobeval exists") | endif -" if !has("syntax") | call Decho("did not install beval events: does not have syntax highlighting") | endif -" if exists("g:syntax_on") | call Decho("did not install beval events: g:syntax_on exists") | endif +" if v:version < 700 | call Decho("did not install beval events: v:version=".v:version." < 700","~".expand("<slnum>")) | endif +" if !has("balloon_eval") | call Decho("did not install beval events: does not have balloon_eval","~".expand("<slnum>")) | endif +" if exists("s:initbeval") | call Decho("did not install beval events: s:initbeval exists","~".expand("<slnum>")) | endif +" if exists("g:netrw_nobeval") | call Decho("did not install beval events: g:netrw_nobeval exists","~".expand("<slnum>")) | endif +" if !has("syntax") | call Decho("did not install beval events: does not have syntax highlighting","~".expand("<slnum>")) | endif +" if exists("g:syntax_on") | call Decho("did not install beval events: g:syntax_on exists","~".expand("<slnum>")) | endif endif au WinEnter * if &ft == "netrw"|call s:NetrwInsureWinVars()|endif @@ -560,7 +562,7 @@ endif " --------------------------------------------------------------------- " netrw#BalloonHelp: {{{2 if v:version >= 700 && has("balloon_eval") && has("syntax") && exists("g:syntax_on") && !exists("g:netrw_nobeval") -" call Decho("loading netrw#BalloonHelp()") +" call Decho("loading netrw#BalloonHelp()",'~'.expand("<slnum>")) fun! netrw#BalloonHelp() if &ft != "netrw" return "" @@ -572,7 +574,7 @@ if v:version >= 700 && has("balloon_eval") && has("syntax") && exists("g:syntax_ elseif getline(v:beval_lnum) =~ '^"\s*/' let mesg = "<cr>: edit/enter o: edit/enter in horiz window t: edit/enter in new tab v:edit/enter in vert window" elseif v:beval_text == "Sorted" || v:beval_text == "by" - let mesg = 's: sort by name, time, or file size r: reverse sorting order mt: mark target' + let mesg = 's: sort by name, time, file size, extension r: reverse sorting order mt: mark target' elseif v:beval_text == "Sort" || v:beval_text == "sequence" let mesg = "S: edit sorting sequence" elseif v:beval_text == "Hiding" || v:beval_text == "Showing" @@ -587,11 +589,11 @@ if v:version >= 700 && has("balloon_eval") && has("syntax") && exists("g:syntax_ return mesg endfun "else " Decho -" if v:version < 700 |call Decho("did not load netrw#BalloonHelp(): vim version ".v:version." < 700 -")|endif -" if !has("balloon_eval") |call Decho("did not load netrw#BalloonHelp(): does not have balloon eval") |endif -" if !has("syntax") |call Decho("did not load netrw#BalloonHelp(): syntax disabled") |endif -" if !exists("g:syntax_on") |call Decho("did not load netrw#BalloonHelp(): g:syntax_on n/a") |endif -" if exists("g:netrw_nobeval") |call Decho("did not load netrw#BalloonHelp(): g:netrw_nobeval exists") |endif +" if v:version < 700 |call Decho("did not load netrw#BalloonHelp(): vim version ".v:version." < 700 -","~".expand("<slnum>"))|endif +" if !has("balloon_eval") |call Decho("did not load netrw#BalloonHelp(): does not have balloon eval","~".expand("<slnum>")) |endif +" if !has("syntax") |call Decho("did not load netrw#BalloonHelp(): syntax disabled","~".expand("<slnum>")) |endif +" if !exists("g:syntax_on") |call Decho("did not load netrw#BalloonHelp(): g:syntax_on n/a","~".expand("<slnum>")) |endif +" if exists("g:netrw_nobeval") |call Decho("did not load netrw#BalloonHelp(): g:netrw_nobeval exists","~".expand("<slnum>")) |endif endif " ------------------------------------------------------------------------ @@ -613,9 +615,10 @@ endif " == 6: Texplore fun! netrw#Explore(indx,dosplit,style,...) " call Dfunc("netrw#Explore(indx=".a:indx." dosplit=".a:dosplit." style=".a:style.",a:1<".a:1.">) &modified=".&modified." modifiable=".&modifiable." a:0=".a:0." win#".winnr()." buf#".bufnr("%")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) if !exists("b:netrw_curdir") let b:netrw_curdir= getcwd() -" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used getcwd)") +" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used getcwd)",'~'.expand("<slnum>")) endif " record current file for Rexplore's benefit @@ -629,23 +632,24 @@ fun! netrw#Explore(indx,dosplit,style,...) if !exists("g:netrw_cygwin") && (has("win32") || has("win95") || has("win64") || has("win16")) let curdir= substitute(curdir,'\','/','g') endif -" call Decho("curdir<".curdir."> curfiledir<".curfiledir.">") +" call Decho("curdir<".curdir."> curfiledir<".curfiledir.">",'~'.expand("<slnum>")) " using completion, directories with spaces in their names (thanks, Bill Gates, for a truly dumb idea) " will end up with backslashes here. Solution: strip off backslashes that precede white space and " try Explore again. if a:0 > 0 " call Decho('considering retry: a:1<'.a:1.'>: '. - \ ((a:1 =~ "\\\s")? 'has backslash whitespace' : 'does not have backslash whitespace').', '. - \ ((filereadable(a:1))? 'is readable' : 'is not readable').', '. - \ ((isdirectory(a:1))? 'is a directory' : 'is not a directory')) - if a:1 =~ "\\\s" && !filereadable(a:1) && !isdirectory(a:1) -" call Decho("re-trying Explore with <".substitute(a:1,'\\\(\s\)','\1','g').">") + \ ((a:1 =~ "\\\s")? 'has backslash whitespace' : 'does not have backslash whitespace').', '. + \ ((filereadable(s:NetrwFile(a:1)))? 'is readable' : 'is not readable').', '. + \ ((isdirectory(s:NetrwFile(a:1))))? 'is a directory' : 'is not a directory', + \ '~'.expand("<slnum>")) + if a:1 =~ "\\\s" && !filereadable(s:NetrwFile(a:1)) && !isdirectory(s:NetrwFile(a:1)) +" call Decho("re-trying Explore with <".substitute(a:1,'\\\(\s\)','\1','g').">",'~'.expand("<slnum>")) call netrw#Explore(a:indx,a:dosplit,a:style,substitute(a:1,'\\\(\s\)','\1','g')) " call Dret("netrw#Explore : returning from retry") return " else " Decho -" call Decho("retry not needed") +" call Decho("retry not needed",'~'.expand("<slnum>")) endif endif @@ -660,7 +664,7 @@ fun! netrw#Explore(indx,dosplit,style,...) " -or- file has been modified AND file not hidden when abandoned " -or- Texplore used if a:dosplit || (&modified && &hidden == 0 && &bufhidden != "hide") || a:style == 6 -" call Decho("case dosplit=".a:dosplit." modified=".&modified." a:style=".a:style.": dosplit or file has been modified") +" call Decho("case dosplit=".a:dosplit." modified=".&modified." a:style=".a:style.": dosplit or file has been modified",'~'.expand("<slnum>")) call s:SaveWinVars() let winsz= g:netrw_winsize if a:indx > 0 @@ -668,119 +672,119 @@ fun! netrw#Explore(indx,dosplit,style,...) endif if a:style == 0 " Explore, Sexplore -" call Decho("style=0: Explore or Sexplore") +" call Decho("style=0: Explore or Sexplore",'~'.expand("<slnum>")) let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz if winsz == 0|let winsz= ""|endif exe "noswapfile ".winsz."wincmd s" -" call Decho("exe noswapfile ".winsz."wincmd s") +" call Decho("exe noswapfile ".winsz."wincmd s",'~'.expand("<slnum>")) elseif a:style == 1 "Explore!, Sexplore! -" call Decho("style=1: Explore! or Sexplore!") +" call Decho("style=1: Explore! or Sexplore!",'~'.expand("<slnum>")) let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz if winsz == 0|let winsz= ""|endif exe "keepalt noswapfile ".winsz."wincmd v" -" call Decho("exe keepalt noswapfile ".winsz."wincmd v") +" call Decho("exe keepalt noswapfile ".winsz."wincmd v",'~'.expand("<slnum>")) elseif a:style == 2 " Hexplore -" call Decho("style=2: Hexplore") +" call Decho("style=2: Hexplore",'~'.expand("<slnum>")) let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz if winsz == 0|let winsz= ""|endif exe "keepalt noswapfile bel ".winsz."wincmd s" -" call Decho("exe keepalt noswapfile bel ".winsz."wincmd s") +" call Decho("exe keepalt noswapfile bel ".winsz."wincmd s",'~'.expand("<slnum>")) elseif a:style == 3 " Hexplore! -" call Decho("style=3: Hexplore!") +" call Decho("style=3: Hexplore!",'~'.expand("<slnum>")) let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz if winsz == 0|let winsz= ""|endif exe "keepalt noswapfile abo ".winsz."wincmd s" -" call Decho("exe keepalt noswapfile abo ".winsz."wincmd s") +" call Decho("exe keepalt noswapfile abo ".winsz."wincmd s",'~'.expand("<slnum>")) elseif a:style == 4 " Vexplore -" call Decho("style=4: Vexplore") +" call Decho("style=4: Vexplore",'~'.expand("<slnum>")) let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz if winsz == 0|let winsz= ""|endif exe "keepalt noswapfile lefta ".winsz."wincmd v" -" call Decho("exe keepalt noswapfile lefta ".winsz."wincmd v") +" call Decho("exe keepalt noswapfile lefta ".winsz."wincmd v",'~'.expand("<slnum>")) elseif a:style == 5 " Vexplore! -" call Decho("style=5: Vexplore!") +" call Decho("style=5: Vexplore!",'~'.expand("<slnum>")) let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz if winsz == 0|let winsz= ""|endif exe "keepalt noswapfile rightb ".winsz."wincmd v" -" call Decho("exe keepalt noswapfile rightb ".winsz."wincmd v") +" call Decho("exe keepalt noswapfile rightb ".winsz."wincmd v",'~'.expand("<slnum>")) elseif a:style == 6 " Texplore call s:SaveBufVars() -" call Decho("style = 6: Texplore") +" call Decho("style = 6: Texplore",'~'.expand("<slnum>")) exe "keepalt tabnew ".fnameescape(curdir) -" call Decho("exe keepalt tabnew ".fnameescape(curdir)) +" call Decho("exe keepalt tabnew ".fnameescape(curdir),'~'.expand("<slnum>")) call s:RestoreBufVars() endif call s:RestoreWinVars() " else " Decho -" call Decho("case a:dosplit=".a:dosplit." AND modified=".&modified." AND a:style=".a:style." is not 6") +" call Decho("case a:dosplit=".a:dosplit." AND modified=".&modified." AND a:style=".a:style." is not 6",'~'.expand("<slnum>")) endif NetrwKeepj norm! 0 if a:0 > 0 -" call Decho("case [a:0=".a:0."] > 0: a:1<".a:1.">") +" call Decho("case [a:0=".a:0."] > 0: a:1<".a:1.">",'~'.expand("<slnum>")) if a:1 =~ '^\~' && (has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin)) -" call Decho("..case a:1<".a:1.">: starts with ~ and unix or cygwin") +" call Decho("..case a:1<".a:1.">: starts with ~ and unix or cygwin",'~'.expand("<slnum>")) let dirname= simplify(substitute(a:1,'\~',expand("$HOME"),'')) -" call Decho("..using dirname<".dirname."> (case: ~ && unix||cygwin)") +" call Decho("..using dirname<".dirname."> (case: ~ && unix||cygwin)",'~'.expand("<slnum>")) elseif a:1 == '.' -" call Decho("..case a:1<".a:1.">: matches .") +" call Decho("..case a:1<".a:1.">: matches .",'~'.expand("<slnum>")) let dirname= simplify(exists("b:netrw_curdir")? b:netrw_curdir : getcwd()) if dirname !~ '/$' let dirname= dirname."/" endif -" call Decho("..using dirname<".dirname."> (case: ".(exists("b:netrw_curdir")? "b:netrw_curdir" : "getcwd()").")") +" call Decho("..using dirname<".dirname."> (case: ".(exists("b:netrw_curdir")? "b:netrw_curdir" : "getcwd()").")",'~'.expand("<slnum>")) elseif a:1 =~ '\$' -" call Decho("..case a:1<".a:1.">: matches ending $") +" call Decho("..case a:1<".a:1.">: matches ending $",'~'.expand("<slnum>")) let dirname= simplify(expand(a:1)) -" call Decho("..using user-specified dirname<".dirname."> with $env-var") +" call Decho("..using user-specified dirname<".dirname."> with $env-var",'~'.expand("<slnum>")) elseif a:1 !~ '^\*\{1,2}/' && a:1 !~ '^\a\{3,}://' -" call Decho("..case a:1<".a:1.">: other, not pattern or filepattern") +" call Decho("..case a:1<".a:1.">: other, not pattern or filepattern",'~'.expand("<slnum>")) let dirname= simplify(a:1) -" call Decho("..using user-specified dirname<".dirname.">") +" call Decho("..using user-specified dirname<".dirname.">",'~'.expand("<slnum>")) else -" call Decho("..case a:1: pattern or filepattern") +" call Decho("..case a:1: pattern or filepattern",'~'.expand("<slnum>")) let dirname= a:1 endif else " clear explore -" call Decho("case a:0=".a:0.": clearing Explore list") +" call Decho("case a:0=".a:0.": clearing Explore list",'~'.expand("<slnum>")) call s:NetrwClearExplore() " call Dret("netrw#Explore : cleared list") return endif -" call Decho("dirname<".dirname.">") +" call Decho("dirname<".dirname.">",'~'.expand("<slnum>")) if dirname =~ '\.\./\=$' let dirname= simplify(fnamemodify(dirname,':p:h')) elseif dirname =~ '\.\.' || dirname == '.' let dirname= simplify(fnamemodify(dirname,':p')) endif -" call Decho("dirname<".dirname."> (after simplify)") +" call Decho("dirname<".dirname."> (after simplify)",'~'.expand("<slnum>")) if dirname =~ '^\*//' " starpat=1: Explore *//pattern (current directory only search for files containing pattern) -" call Decho("case starpat=1: Explore *//pattern") +" call Decho("case starpat=1: Explore *//pattern",'~'.expand("<slnum>")) let pattern= substitute(dirname,'^\*//\(.*\)$','\1','') let starpat= 1 -" call Decho("..Explore *//pat: (starpat=".starpat.") dirname<".dirname."> -> pattern<".pattern.">") +" call Decho("..Explore *//pat: (starpat=".starpat.") dirname<".dirname."> -> pattern<".pattern.">",'~'.expand("<slnum>")) if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif elseif dirname =~ '^\*\*//' " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) -" call Decho("case starpat=2: Explore **//pattern") +" call Decho("case starpat=2: Explore **//pattern",'~'.expand("<slnum>")) let pattern= substitute(dirname,'^\*\*//','','') let starpat= 2 -" call Decho("..Explore **//pat: (starpat=".starpat.") dirname<".dirname."> -> pattern<".pattern.">") +" call Decho("..Explore **//pat: (starpat=".starpat.") dirname<".dirname."> -> pattern<".pattern.">",'~'.expand("<slnum>")) elseif dirname =~ '/\*\*/' " handle .../**/.../filepat -" call Decho("case starpat=4: Explore .../**/.../filepat") +" call Decho("case starpat=4: Explore .../**/.../filepat",'~'.expand("<slnum>")) let prefixdir= substitute(dirname,'^\(.\{-}\)\*\*.*$','\1','') if prefixdir =~ '^/' || (prefixdir =~ '^\a:/' && (has("win32") || has("win95") || has("win64") || has("win16"))) let b:netrw_curdir = prefixdir @@ -789,30 +793,30 @@ fun! netrw#Explore(indx,dosplit,style,...) endif let dirname= substitute(dirname,'^.\{-}\(\*\*/.*\)$','\1','') let starpat= 4 -" call Decho("..pwd<".getcwd()."> dirname<".dirname.">") -" call Decho("..case Explore ../**/../filepat (starpat=".starpat.")") +" call Decho("..pwd<".getcwd()."> dirname<".dirname.">",'~'.expand("<slnum>")) +" call Decho("..case Explore ../**/../filepat (starpat=".starpat.")",'~'.expand("<slnum>")) elseif dirname =~ '^\*/' " case starpat=3: Explore */filepat (search in current directory for filenames matching filepat) let starpat= 3 -" call Decho("case starpat=3: Explore */filepat (starpat=".starpat.")") +" call Decho("case starpat=3: Explore */filepat (starpat=".starpat.")",'~'.expand("<slnum>")) elseif dirname=~ '^\*\*/' " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) let starpat= 4 -" call Decho("case starpat=4: Explore **/filepat (starpat=".starpat.")") +" call Decho("case starpat=4: Explore **/filepat (starpat=".starpat.")",'~'.expand("<slnum>")) else let starpat= 0 -" call Decho("case starpat=0: default") +" call Decho("case starpat=0: default",'~'.expand("<slnum>")) endif if starpat == 0 && a:indx >= 0 " [Explore Hexplore Vexplore Sexplore] [dirname] -" call Decho("case starpat==0 && a:indx=".a:indx.": dirname<".dirname.">, handles Explore Hexplore Vexplore Sexplore") +" call Decho("case starpat==0 && a:indx=".a:indx.": dirname<".dirname.">, handles Explore Hexplore Vexplore Sexplore",'~'.expand("<slnum>")) if dirname == "" let dirname= curfiledir -" call Decho("..empty dirname, using current file's directory<".dirname.">") +" call Decho("..empty dirname, using current file's directory<".dirname.">",'~'.expand("<slnum>")) endif if dirname =~ '^scp://' || dirname =~ '^ftp://' call netrw#Nread(2,dirname) @@ -820,7 +824,7 @@ fun! netrw#Explore(indx,dosplit,style,...) if dirname == "" let dirname= getcwd() elseif (has("win32") || has("win95") || has("win64") || has("win16")) && !g:netrw_cygwin - " Windows : check for a drive specifier, or else for a remote share name ('\\Foo' or '//Foo', + " Windows : check for a drive specifier, or else for a remote share name ('\\Foo' or '//Foo', " depending on whether backslashes have been converted to forward slashes by earlier code). if dirname !~ '^[a-zA-Z]:' && dirname !~ '^\\\\\w\+' && dirname !~ '^//\w\+' let dirname= b:netrw_curdir."/".dirname @@ -828,9 +832,10 @@ fun! netrw#Explore(indx,dosplit,style,...) elseif dirname !~ '^/' let dirname= b:netrw_curdir."/".dirname endif -" call Decho("..calling LocalBrowseCheck(dirname<".dirname.">)") +" call Decho("..calling LocalBrowseCheck(dirname<".dirname.">)",'~'.expand("<slnum>")) call netrw#LocalBrowseCheck(dirname) -" call Decho("win#".winnr()." buf#".bufnr("%")." modified=".&modified." modifiable=".&modifiable." readonly=".&readonly) +" call Decho(" modified=".&modified." modifiable=".&modifiable." readonly=".&readonly,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) endif if exists("w:netrw_bannercnt") " done to handle P08-Ingelrest. :Explore will _Always_ go to the line just after the banner. @@ -838,7 +843,7 @@ fun! netrw#Explore(indx,dosplit,style,...) exe w:netrw_bannercnt endif -" call Decho("curdir<".curdir.">") +" call Decho("curdir<".curdir.">",'~'.expand("<slnum>")) " --------------------------------------------------------------------- " Jan 24, 2013: not sure why the following was present. See P08-Ingelrest " if has("win32") || has("win95") || has("win64") || has("win16") @@ -854,26 +859,26 @@ fun! netrw#Explore(indx,dosplit,style,...) " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) elseif a:indx <= 0 " Nexplore, Pexplore, Explore: handle starpat -" call Decho("case a:indx<=0: Nexplore, Pexplore, <s-down>, <s-up> starpat=".starpat." a:indx=".a:indx) +" call Decho("case a:indx<=0: Nexplore, Pexplore, <s-down>, <s-up> starpat=".starpat." a:indx=".a:indx,'~'.expand("<slnum>")) if !mapcheck("<s-up>","n") && !mapcheck("<s-down>","n") && exists("b:netrw_curdir") -" call Decho("..set up <s-up> and <s-down> maps") +" call Decho("..set up <s-up> and <s-down> maps",'~'.expand("<slnum>")) let s:didstarstar= 1 nnoremap <buffer> <silent> <s-up> :Pexplore<cr> nnoremap <buffer> <silent> <s-down> :Nexplore<cr> endif if has("path_extra") -" call Decho("..starpat=".starpat.": has +path_extra") +" call Decho("..starpat=".starpat.": has +path_extra",'~'.expand("<slnum>")) if !exists("w:netrw_explore_indx") let w:netrw_explore_indx= 0 endif let indx = a:indx -" call Decho("..starpat=".starpat.": set indx= [a:indx=".indx."]") +" call Decho("..starpat=".starpat.": set indx= [a:indx=".indx."]",'~'.expand("<slnum>")) if indx == -1 " Nexplore -" call Decho("..case Nexplore with starpat=".starpat.": (indx=".indx.")") +" call Decho("..case Nexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("<slnum>")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or <s-down> improperly; see help for netrw-starstar",40) if has("clipboard") @@ -888,17 +893,17 @@ fun! netrw#Explore(indx,dosplit,style,...) if indx < 0 | let indx= 0 | endif if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif let curfile= w:netrw_explore_list[indx] -" call Decho("....indx=".indx." curfile<".curfile.">") +" call Decho("....indx=".indx." curfile<".curfile.">",'~'.expand("<slnum>")) while indx < w:netrw_explore_listlen && curfile == w:netrw_explore_list[indx] let indx= indx + 1 -" call Decho("....indx=".indx." (Nexplore while loop)") +" call Decho("....indx=".indx." (Nexplore while loop)",'~'.expand("<slnum>")) endwhile if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif -" call Decho("....Nexplore: indx= [w:netrw_explore_indx=".w:netrw_explore_indx."]=".indx) +" call Decho("....Nexplore: indx= [w:netrw_explore_indx=".w:netrw_explore_indx."]=".indx,'~'.expand("<slnum>")) elseif indx == -2 " Pexplore -" call Decho("case Pexplore with starpat=".starpat.": (indx=".indx.")") +" call Decho("case Pexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("<slnum>")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or <s-up> improperly; see help for netrw-starstar",41) if has("clipboard") @@ -913,30 +918,30 @@ fun! netrw#Explore(indx,dosplit,style,...) if indx < 0 | let indx= 0 | endif if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif let curfile= w:netrw_explore_list[indx] -" call Decho("....indx=".indx." curfile<".curfile.">") +" call Decho("....indx=".indx." curfile<".curfile.">",'~'.expand("<slnum>")) while indx >= 0 && curfile == w:netrw_explore_list[indx] let indx= indx - 1 -" call Decho("....indx=".indx." (Pexplore while loop)") +" call Decho("....indx=".indx." (Pexplore while loop)",'~'.expand("<slnum>")) endwhile if indx < 0 | let indx= 0 | endif -" call Decho("....Pexplore: indx= [w:netrw_explore_indx=".w:netrw_explore_indx."]=".indx) +" call Decho("....Pexplore: indx= [w:netrw_explore_indx=".w:netrw_explore_indx."]=".indx,'~'.expand("<slnum>")) else " Explore -- initialize " build list of files to Explore with Nexplore/Pexplore -" call Decho("..starpat=".starpat.": case Explore: initialize (indx=".indx.")") +" call Decho("..starpat=".starpat.": case Explore: initialize (indx=".indx.")",'~'.expand("<slnum>")) NetrwKeepj keepalt call s:NetrwClearExplore() let w:netrw_explore_indx= 0 if !exists("b:netrw_curdir") let b:netrw_curdir= getcwd() endif -" call Decho("....starpat=".starpat.": b:netrw_curdir<".b:netrw_curdir.">") +" call Decho("....starpat=".starpat.": b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) " switch on starpat to build the w:netrw_explore_list of files if starpat == 1 " starpat=1: Explore *//pattern (current directory only search for files containing pattern) -" call Decho("..case starpat=".starpat.": build *//pattern list (curdir-only srch for files containing pattern) &hls=".&hls) -" call Decho("....pattern<".pattern.">") +" call Decho("..case starpat=".starpat.": build *//pattern list (curdir-only srch for files containing pattern) &hls=".&hls,'~'.expand("<slnum>")) +" call Decho("....pattern<".pattern.">",'~'.expand("<slnum>")) try exe "NetrwKeepj noautocmd vimgrep /".pattern."/gj ".fnameescape(b:netrw_curdir)."/*" catch /^Vim\%((\a\+)\)\=:E480/ @@ -949,8 +954,8 @@ fun! netrw#Explore(indx,dosplit,style,...) elseif starpat == 2 " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) -" call Decho("..case starpat=".starpat.": build **//pattern list (recursive descent files containing pattern)") -" call Decho("....pattern<".pattern.">") +" call Decho("..case starpat=".starpat.": build **//pattern list (recursive descent files containing pattern)",'~'.expand("<slnum>")) +" call Decho("....pattern<".pattern.">",'~'.expand("<slnum>")) try exe "sil NetrwKeepj noautocmd keepalt vimgrep /".pattern."/gj "."**/*" catch /^Vim\%((\a\+)\)\=:E480/ @@ -971,24 +976,24 @@ fun! netrw#Explore(indx,dosplit,style,...) elseif starpat == 3 " starpat=3: Explore */filepat (search in current directory for filenames matching filepat) -" call Decho("..case starpat=".starpat.": build */filepat list (curdir-only srch filenames matching filepat) &hls=".&hls) +" call Decho("..case starpat=".starpat.": build */filepat list (curdir-only srch filenames matching filepat) &hls=".&hls,'~'.expand("<slnum>")) let filepat= substitute(dirname,'^\*/','','') let filepat= substitute(filepat,'^[%#<]','\\&','') -" call Decho("....b:netrw_curdir<".b:netrw_curdir.">") -" call Decho("....filepat<".filepat.">") +" call Decho("....b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) +" call Decho("....filepat<".filepat.">",'~'.expand("<slnum>")) let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".filepat),'\n')) if &hls | let keepregslash= s:ExplorePatHls(filepat) | endif elseif starpat == 4 " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) -" call Decho("..case starpat=".starpat.": build **/filepat list (recursive descent srch filenames matching filepat) &hls=".&hls) +" call Decho("..case starpat=".starpat.": build **/filepat list (recursive descent srch filenames matching filepat) &hls=".&hls,'~'.expand("<slnum>")) let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".dirname),'\n')) if &hls | let keepregslash= s:ExplorePatHls(dirname) | endif endif " switch on starpat to build w:netrw_explore_list let w:netrw_explore_listlen = len(w:netrw_explore_list) -" call Decho("....w:netrw_explore_list<".string(w:netrw_explore_list).">") -" call Decho("....w:netrw_explore_listlen=".w:netrw_explore_listlen) +" call Decho("....w:netrw_explore_list<".string(w:netrw_explore_list).">",'~'.expand("<slnum>")) +" call Decho("....w:netrw_explore_listlen=".w:netrw_explore_listlen,'~'.expand("<slnum>")) if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/') keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42) @@ -1004,22 +1009,22 @@ fun! netrw#Explore(indx,dosplit,style,...) " NetrwStatusLine support - for exploring support let w:netrw_explore_indx= indx -" call Decho("....w:netrw_explore_list<".join(w:netrw_explore_list,',')."> len=".w:netrw_explore_listlen) +" call Decho("....w:netrw_explore_list<".join(w:netrw_explore_list,',')."> len=".w:netrw_explore_listlen,'~'.expand("<slnum>")) " wrap the indx around, but issue a note if indx >= w:netrw_explore_listlen || indx < 0 -" call Decho("....wrap indx (indx=".indx." listlen=".w:netrw_explore_listlen.")") +" call Decho("....wrap indx (indx=".indx." listlen=".w:netrw_explore_listlen.")",'~'.expand("<slnum>")) let indx = (indx < 0)? ( w:netrw_explore_listlen - 1 ) : 0 let w:netrw_explore_indx= indx keepalt NetrwKeepj call netrw#ErrorMsg(s:NOTE,"no more files match Explore pattern",43) endif exe "let dirfile= w:netrw_explore_list[".indx."]" -" call Decho("....dirfile=w:netrw_explore_list[indx=".indx."]= <".dirfile.">") +" call Decho("....dirfile=w:netrw_explore_list[indx=".indx."]= <".dirfile.">",'~'.expand("<slnum>")) let newdir= substitute(dirfile,'/[^/]*$','','e') -" call Decho("....newdir<".newdir.">") +" call Decho("....newdir<".newdir.">",'~'.expand("<slnum>")) -" call Decho("....calling LocalBrowseCheck(newdir<".newdir.">)") +" call Decho("....calling LocalBrowseCheck(newdir<".newdir.">)",'~'.expand("<slnum>")) call netrw#LocalBrowseCheck(newdir) if !exists("w:netrw_liststyle") let w:netrw_liststyle= g:netrw_liststyle @@ -1033,10 +1038,10 @@ fun! netrw#Explore(indx,dosplit,style,...) let w:netrw_explore_bufnr = bufnr("%") let w:netrw_explore_line = line(".") keepalt NetrwKeepj call s:SetupNetrwStatusLine('%f %h%m%r%=%9*%{NetrwStatusLine()}') -" call Decho("....explore: mtchcnt=".w:netrw_explore_mtchcnt." bufnr=".w:netrw_explore_bufnr." line#".w:netrw_explore_line) +" call Decho("....explore: mtchcnt=".w:netrw_explore_mtchcnt." bufnr=".w:netrw_explore_bufnr." line#".w:netrw_explore_line,'~'.expand("<slnum>")) else -" call Decho("..your vim does not have +path_extra") +" call Decho("..your vim does not have +path_extra",'~'.expand("<slnum>")) if !exists("g:netrw_quiet") keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44) endif @@ -1050,7 +1055,7 @@ fun! netrw#Explore(indx,dosplit,style,...) endif else -" call Decho("..default case: Explore newdir<".dirname.">") +" call Decho("..default case: Explore newdir<".dirname.">",'~'.expand("<slnum>")) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && dirname =~ '/' sil! unlet w:netrw_treedict sil! unlet w:netrw_treetop @@ -1064,13 +1069,13 @@ fun! netrw#Explore(indx,dosplit,style,...) endif " visual display of **/ **// */ Exploration files -" call Decho("w:netrw_explore_indx=".(exists("w:netrw_explore_indx")? w:netrw_explore_indx : "doesn't exist")) -" call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "n/a").">") +" call Decho("w:netrw_explore_indx=".(exists("w:netrw_explore_indx")? w:netrw_explore_indx : "doesn't exist"),'~'.expand("<slnum>")) +" call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "n/a").">",'~'.expand("<slnum>")) if exists("w:netrw_explore_indx") && exists("b:netrw_curdir") -" call Decho("s:explore_prvdir<".(exists("s:explore_prvdir")? s:explore_prvdir : "-doesn't exist-")) +" call Decho("s:explore_prvdir<".(exists("s:explore_prvdir")? s:explore_prvdir : "-doesn't exist-"),'~'.expand("<slnum>")) if !exists("s:explore_prvdir") || s:explore_prvdir != b:netrw_curdir " only update match list when current directory isn't the same as before -" call Decho("only update match list when current directory not the same as before") +" call Decho("only update match list when current directory not the same as before",'~'.expand("<slnum>")) let s:explore_prvdir = b:netrw_curdir let s:explore_match = "" let dirlen = strlen(b:netrw_curdir) @@ -1079,7 +1084,7 @@ fun! netrw#Explore(indx,dosplit,style,...) endif let prvfname= "" for fname in w:netrw_explore_list -" call Decho("fname<".fname.">") +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) if fname =~ '^'.b:netrw_curdir if s:explore_match == "" let s:explore_match= '\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>' @@ -1095,7 +1100,7 @@ fun! netrw#Explore(indx,dosplit,style,...) endif let prvfname= fname endfor -" call Decho("explore_match<".s:explore_match.">") +" call Decho("explore_match<".s:explore_match.">",'~'.expand("<slnum>")) exe "2match netrwMarkFile /".s:explore_match."/" endif echo "<s-up>==Pexplore <s-down>==Nexplore" @@ -1104,7 +1109,7 @@ fun! netrw#Explore(indx,dosplit,style,...) if exists("s:explore_match") | unlet s:explore_match | endif if exists("s:explore_prvdir") | unlet s:explore_prvdir | endif echo " " -" call Decho("cleared explore match list") +" call Decho("cleared explore match list",'~'.expand("<slnum>")) endif " since Explore may be used to initialize netrw's browser, @@ -1130,14 +1135,14 @@ fun! netrw#Lexplore(count,rightside,...) " and a directory has been specified, explore with that " directory. let a1 = expand(a:1) -" call Decho("a:1<".a:1."> curwin#".curwin) +" call Decho("a:1<".a:1."> curwin#".curwin,'~'.expand("<slnum>")) exe "1wincmd w" if &ft == "netrw" -" call Decho("exe Explore ".fnameescape(a:1)) +" call Decho("exe Explore ".fnameescape(a:1),'~'.expand("<slnum>")) exe "Explore ".fnameescape(a1) exe curwin."wincmd w" if exists("t:netrw_lexposn") -" call Decho("forgetting t:netrw_lexposn") +" call Decho("forgetting t:netrw_lexposn",'~'.expand("<slnum>")) unlet t:netrw_lexposn endif " call Dret("netrw#Lexplore") @@ -1157,11 +1162,11 @@ fun! netrw#Lexplore(count,rightside,...) if lexwinnr > 0 " close down netrw explorer window -" call Decho("t:netrw_lexbufnr#".t:netrw_lexbufnr.": close down netrw window") +" call Decho("t:netrw_lexbufnr#".t:netrw_lexbufnr.": close down netrw window",'~'.expand("<slnum>")) exe lexwinnr."wincmd w" let g:netrw_winsize = -winwidth(0) let t:netrw_lexposn = netrw#SavePosn() -" call Decho("saving t:netrw_lexposn") +" call Decho("saving t:netrw_lexposn",'~'.expand("<slnum>")) close if lexwinnr < curwin let curwin= curwin - 1 @@ -1171,7 +1176,7 @@ fun! netrw#Lexplore(count,rightside,...) else " open netrw explorer window -" call Decho("t:netrw_lexbufnr<n/a>: open netrw explorer window") +" call Decho("t:netrw_lexbufnr<n/a>: open netrw explorer window",'~'.expand("<slnum>")) exe "1wincmd w" let keep_altv = g:netrw_altv let g:netrw_altv = 0 @@ -1180,16 +1185,16 @@ fun! netrw#Lexplore(count,rightside,...) let g:netrw_winsize = a:count endif let curfile= expand("%") -" call Decho("curfile<".curfile.">") +" call Decho("curfile<".curfile.">",'~'.expand("<slnum>")) exe (a:rightside? "botright" : "topleft")." vertical ".((g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize) . " new" if a:0 > 0 && a1 != "" -" call Decho("case 1: Explore ".a1) +" call Decho("case 1: Explore ".a1,'~'.expand("<slnum>")) exe "Explore ".fnameescape(a1) - elseif curfile =~ '^\a\+://' -" call Decho("case 2: Explore ".substitute(curfile,'[^/\\]*$','','')) + elseif curfile =~ '^\a\{3,}://' +" call Decho("case 2: Explore ".substitute(curfile,'[^/\\]*$','',''),'~'.expand("<slnum>")) exe "Explore ".substitute(curfile,'[^/\\]*$','','') else -" call Decho("case 3: Explore .") +" call Decho("case 3: Explore .",'~'.expand("<slnum>")) Explore . endif if a:count != 0 @@ -1199,7 +1204,7 @@ fun! netrw#Lexplore(count,rightside,...) let g:netrw_altv = keep_altv let t:netrw_lexbufnr = bufnr("%") if exists("t:netrw_lexposn") -" call Decho("restoring to t:netrw_lexposn") +" call Decho("restoring to t:netrw_lexposn",'~'.expand("<slnum>")) call netrw#RestorePosn(t:netrw_lexposn) unlet t:netrw_lexposn endif @@ -1229,14 +1234,14 @@ fun! netrw#Clean(sys) else let choice= confirm("Remove personal copy of netrw?","&Yes\n&No") endif -" call Decho("choice=".choice) +" call Decho("choice=".choice,'~'.expand("<slnum>")) let diddel= 0 let diddir= "" if choice == 1 for dir in split(&rtp,',') if filereadable(dir."/plugin/netrwPlugin.vim") -" call Decho("removing netrw-related files from ".dir) +" call Decho("removing netrw-related files from ".dir,'~'.expand("<slnum>")) if s:NetrwDelete(dir."/plugin/netrwPlugin.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/plugin/netrwPlugin.vim",55) |endif if s:NetrwDelete(dir."/autoload/netrwFileHandlers.vim")|call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwFileHandlers.vim",55)|endif if s:NetrwDelete(dir."/autoload/netrwSettings.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwSettings.vim",55) |endif @@ -1269,8 +1274,8 @@ fun! netrw#MakeTgt(dname) " call Dfunc("netrw#MakeTgt(dname<".a:dname.">)") " simplify the target (eg. /abc/def/../ghi -> /abc/ghi) let svpos = netrw#SavePosn() - let s:netrwmftgt_islocal= (a:dname !~ '^\a\+://') -" call Decho("s:netrwmftgt_islocal=".s:netrwmftgt_islocal) + let s:netrwmftgt_islocal= (a:dname !~ '^\a\{3,}://') +" call Decho("s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>")) if s:netrwmftgt_islocal let netrwmftgt= simplify(a:dname) else @@ -1283,7 +1288,7 @@ fun! netrw#MakeTgt(dname) let s:netrwmftgt= netrwmftgt endif if g:netrw_fastbrowse <= 1 - call s:NetrwRefresh((b:netrw_curdir !~ '\a\+://'),b:netrw_curdir) + call s:NetrwRefresh((b:netrw_curdir !~ '\a\{3,}://'),b:netrw_curdir) endif call netrw#RestorePosn(svpos) " call Dret("netrw#MakeTgt") @@ -1309,40 +1314,40 @@ fun! netrw#Obtain(islocal,fname,...) " call Dret("netrw#Obtain") return endif -" call Decho("fnamelist<".string(fnamelist).">") +" call Decho("fnamelist<".string(fnamelist).">",'~'.expand("<slnum>")) if a:0 > 0 let tgtdir= a:1 else let tgtdir= getcwd() endif -" call Decho("tgtdir<".tgtdir.">") +" call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>")) if exists("b:netrw_islocal") && b:netrw_islocal " obtain a file from local b:netrw_curdir to (local) tgtdir -" call Decho("obtain a file from local ".b:netrw_curdir." to ".tgtdir) +" call Decho("obtain a file from local ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>")) if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir let topath= s:ComposePath(tgtdir,"") if (has("win32") || has("win95") || has("win64") || has("win16")) " transfer files one at time -" call Decho("transfer files one at a time") +" call Decho("transfer files one at a time",'~'.expand("<slnum>")) for fname in fnamelist -" call Decho("system(".g:netrw_localcopycmd." ".shellescape(fname)." ".shellescape(topath).")") - call system(g:netrw_localcopycmd." ".shellescape(fname)." ".shellescape(topath)) +" call Decho("system(".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath).")",'~'.expand("<slnum>")) + call system(g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath)) if v:shell_error != 0 call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80) -" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".shellescape(fname)." ".shellescape(topath)) +" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath)) return endif endfor else " transfer files with one command -" call Decho("transfer files with one command") - let filelist= join(map(deepcopy(fnamelist),"shellescape(v:val)")) -" call Decho("system(".g:netrw_localcopycmd." ".filelist." ".shellescape(topath).")") - call system(g:netrw_localcopycmd." ".filelist." ".shellescape(topath)) +" call Decho("transfer files with one command",'~'.expand("<slnum>")) + let filelist= join(map(deepcopy(fnamelist),"s:ShellEscape(v:val)")) +" call Decho("system(".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath).")",'~'.expand("<slnum>")) + call system(g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath)) if v:shell_error != 0 call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80) -" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".filelist." ".shellescape(topath)) +" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath)) return endif endif @@ -1354,7 +1359,7 @@ fun! netrw#Obtain(islocal,fname,...) else " obtain files from remote b:netrw_curdir to local tgtdir -" call Decho("obtain a file from remote ".b:netrw_curdir." to ".tgtdir) +" call Decho("obtain a file from remote ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>")) if type(a:fname) == 1 call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname) endif @@ -1362,7 +1367,7 @@ fun! netrw#Obtain(islocal,fname,...) if b:netrw_method == 4 " obtain file using scp -" call Decho("obtain via scp (method#4)") +" call Decho("obtain via scp (method#4)",'~'.expand("<slnum>")) if exists("g:netrw_port") && g:netrw_port != "" let useport= " ".g:netrw_scpport." ".g:netrw_port else @@ -1373,37 +1378,37 @@ fun! netrw#Obtain(islocal,fname,...) else let path= "" endif - let filelist= join(map(deepcopy(fnamelist),'shellescape(g:netrw_machine.":".path.v:val,1)')) - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.shellescape(useport,1)." ".filelist." ".shellescape(tgtdir,1)) + let filelist= join(map(deepcopy(fnamelist),'s:ShellEscape(g:netrw_machine.":".path.v:val,1)')) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".filelist." ".s:ShellEscape(tgtdir,1)) elseif b:netrw_method == 2 " obtain file using ftp + .netrc -" call Decho("obtain via ftp+.netrc (method #2)") +" call Decho("obtain via ftp+.netrc (method #2)",'~'.expand("<slnum>")) call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars() let tmpbufnr= bufnr("%") setl ff=unix if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("b:netrw_fname") && b:netrw_fname != "" call setline(line("$")+1,'cd "'.b:netrw_fname.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif for fname in fnamelist call setline(line("$")+1,'get "'.fname.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endfor if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)." ".shellescape(g:netrw_port,1)) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) else - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) endif " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' @@ -1415,56 +1420,56 @@ fun! netrw#Obtain(islocal,fname,...) elseif b:netrw_method == 3 " obtain with ftp + machine, id, passwd, and fname (ie. no .netrc) -" call Decho("obtain via ftp+mipf (method #3)") +" call Decho("obtain via ftp+mipf (method #3)",'~'.expand("<slnum>")) call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars() let tmpbufnr= bufnr("%") setl ff=unix if exists("g:netrw_port") && g:netrw_port != "" NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) else NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("g:netrw_uid") && g:netrw_uid != "" if exists("g:netrw_ftp") && g:netrw_ftp == 1 NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) if exists("s:netrw_passwd") && s:netrw_passwd != "" NetrwKeepj put ='\"'.s:netrw_passwd.'\"' endif -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) elseif exists("s:netrw_passwd") NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif endif if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("b:netrw_fname") && b:netrw_fname != "" NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif for fname in fnamelist NetrwKeepj call setline(line("$")+1,'get "'.fname.'"') endfor -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) " perform ftp: " -i : turns off interactive prompting from ftp @@ -1474,7 +1479,7 @@ fun! netrw#Obtain(islocal,fname,...) call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" -" call Decho("error<".getline(1).">") +" call Decho("error<".getline(1).">",'~'.expand("<slnum>")) if !exists("g:netrw_quiet") NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),5) endif @@ -1482,13 +1487,13 @@ fun! netrw#Obtain(islocal,fname,...) elseif b:netrw_method == 9 " obtain file using sftp -" call Decho("obtain via sftp (method #9)") +" call Decho("obtain via sftp (method #9)",'~'.expand("<slnum>")) if a:fname =~ '/' let localfile= substitute(a:fname,'^.*/','','') else let localfile= a:fname endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".shellescape(g:netrw_machine.":".b:netrw_fname,1).shellescape(localfile)." ".shellescape(tgtdir)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1).s:ShellEscape(localfile)." ".s:ShellEscape(tgtdir)) elseif !exists("b:netrw_method") || b:netrw_method < 0 " probably a badly formed url; protocol not recognized @@ -1544,19 +1549,19 @@ endfun " s:NetrwOptionRestore: restore options (based on prior s:NetrwOptionSave) {{{2 fun! s:NetrwOptionRestore(vt) " call Dfunc("s:NetrwOptionRestore(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$")) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) if !exists("{a:vt}netrw_optionsave") if exists("s:nbcd_curpos_{bufnr('%')}") -" call Decho("restoring previous position (s:nbcd_curpos_".bufnr('%')." exists)") +" call Decho("restoring previous position (s:nbcd_curpos_".bufnr('%')." exists)",'~'.expand("<slnum>")) NetrwKeepj call netrw#RestorePosn(s:nbcd_curpos_{bufnr('%')}) -" call Decho("win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$")) -" call Decho("unlet s:nbcd_curpos_".bufnr('%')) +" call Decho("win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$"),'~'.expand("<slnum>")) +" call Decho("unlet s:nbcd_curpos_".bufnr('%'),'~'.expand("<slnum>")) unlet s:nbcd_curpos_{bufnr('%')} else -" call Decho("no previous position") +" call Decho("no previous position",'~'.expand("<slnum>")) endif -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt) -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:NetrwOptionRestore : ".a:vt."netrw_optionsave doesn't exist") return endif @@ -1564,7 +1569,7 @@ fun! s:NetrwOptionRestore(vt) if exists("+acd") if exists("{a:vt}netrw_acdkeep") -" call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd) +" call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("<slnum>")) let curdir = getcwd() let &l:acd = {a:vt}netrw_acdkeep unlet {a:vt}netrw_acdkeep @@ -1575,9 +1580,6 @@ fun! s:NetrwOptionRestore(vt) endif if exists("{a:vt}netrw_aikeep") |let &l:ai = {a:vt}netrw_aikeep |unlet {a:vt}netrw_aikeep |endif if exists("{a:vt}netrw_awkeep") |let &l:aw = {a:vt}netrw_awkeep |unlet {a:vt}netrw_awkeep |endif - if g:netrw_liststyle != s:TREELIST - if exists("{a:vt}netrw_bhkeep") |let &l:bh = {a:vt}netrw_bhkeep |unlet {a:vt}netrw_bhkeep |endif - endif if exists("{a:vt}netrw_blkeep") |let &l:bl = {a:vt}netrw_blkeep |unlet {a:vt}netrw_blkeep |endif if exists("{a:vt}netrw_btkeep") |let &l:bt = {a:vt}netrw_btkeep |unlet {a:vt}netrw_btkeep |endif if exists("{a:vt}netrw_bombkeep") |let &l:bomb = {a:vt}netrw_bombkeep |unlet {a:vt}netrw_bombkeep |endif @@ -1632,7 +1634,7 @@ fun! s:NetrwOptionRestore(vt) unlet {a:vt}netrw_swfkeep endif endif - if exists("{a:vt}netrw_dirkeep") && isdirectory({a:vt}netrw_dirkeep) && g:netrw_keepdir + if exists("{a:vt}netrw_dirkeep") && isdirectory(s:NetrwFile({a:vt}netrw_dirkeep)) && g:netrw_keepdir let dirkeep = substitute({a:vt}netrw_dirkeep,'\\','/','g') if exists("{a:vt}netrw_dirkeep") call s:NetrwLcd(dirkeep) @@ -1644,29 +1646,29 @@ fun! s:NetrwOptionRestore(vt) endif if exists("{a:vt}netrw_regslash")|sil! let @/= {a:vt}netrw_regslash|unlet {a:vt}netrw_regslash|endif if exists("s:nbcd_curpos_{bufnr('%')}") -" call Decho("restoring previous position (s:nbcd_curpos_".bufnr('%')." exists)") +" call Decho("restoring previous position (s:nbcd_curpos_".bufnr('%')." exists)",'~'.expand("<slnum>")) NetrwKeepj call netrw#RestorePosn(s:nbcd_curpos_{bufnr('%')}) -" call Decho("unlet s:nbcd_curpos_".bufnr('%')) +" call Decho("unlet s:nbcd_curpos_".bufnr('%'),'~'.expand("<slnum>")) if exists("s:nbcd_curpos_".bufnr('%')) unlet s:nbcd_curpos_{bufnr('%')} endif else -" call Decho("no previous position") +" call Decho("no previous position",'~'.expand("<slnum>")) endif -" call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd) -" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")) -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") -" call Decho("diff=".&l:diff." win#".winnr()." w:netrw_diffkeep=".(exists("w:netrw_diffkeep")? w:netrw_diffkeep : "doesn't exist")) -" call Decho("ts=".&l:ts) +" call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("<slnum>")) +" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist"),'~'.expand("<slnum>")) +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) +" call Decho("diff=".&l:diff." win#".winnr()." w:netrw_diffkeep=".(exists("w:netrw_diffkeep")? w:netrw_diffkeep : "doesn't exist"),'~'.expand("<slnum>")) +" call Decho("ts=".&l:ts,'~'.expand("<slnum>")) " Moved the filetype detect here from NetrwGetFile() because remote files " were having their filetype detect-generated settings overwritten by " NetrwOptionRestore. if &ft != "netrw" -" call Decho("filetype detect (ft=".&ft.")") +" call Decho("filetype detect (ft=".&ft.")",'~'.expand("<slnum>")) filetype detect endif -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) " call Dret("s:NetrwOptionRestore : tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> modified=".&modified." modifiable=".&modifiable." readonly=".&readonly) endfun @@ -1680,8 +1682,8 @@ endfun " vt: normally its "w:" or "s:" (a variable type) fun! s:NetrwOptionSave(vt) " call Dfunc("s:NetrwOptionSave(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%")).">"." winnr($)=".winnr("$")." mod=".&mod." ma=".&ma) -" call Decho(a:vt."netrw_optionsave".(exists("{a:vt}netrw_optionsave")? ("=".{a:vt}netrw_optionsave) : " doesn't exist")) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt) +" call Decho(a:vt."netrw_optionsave".(exists("{a:vt}netrw_optionsave")? ("=".{a:vt}netrw_optionsave) : " doesn't exist"),'~'.expand("<slnum>")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) if !exists("{a:vt}netrw_optionsave") let {a:vt}netrw_optionsave= 1 @@ -1689,10 +1691,10 @@ fun! s:NetrwOptionSave(vt) " call Dret("s:NetrwOptionSave : options already saved") return endif -" call Decho("prior to save: fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." diff=".&l:diff) +" call Decho("prior to save: fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." diff=".&l:diff,'~'.expand("<slnum>")) " Save current settings and current directory -" call Decho("saving current settings and current directory") +" call Decho("saving current settings and current directory",'~'.expand("<slnum>")) let s:yykeep = @@ if exists("&l:acd")|let {a:vt}netrw_acdkeep = &l:acd|endif let {a:vt}netrw_aikeep = &l:ai @@ -1740,7 +1742,7 @@ fun! s:NetrwOptionSave(vt) let {a:vt}netrw_writekeep = &l:write " save a few selected netrw-related variables -" call Decho("saving a few selected netrw-related variables") +" call Decho("saving a few selected netrw-related variables",'~'.expand("<slnum>")) if g:netrw_keepdir let {a:vt}netrw_dirkeep = getcwd() endif @@ -1749,7 +1751,7 @@ fun! s:NetrwOptionSave(vt) endif sil! let {a:vt}netrw_regslash= @/ -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) " call Dret("s:NetrwOptionSave : tab#".tabpagenr()." win#".winnr()) endfun @@ -1759,8 +1761,8 @@ endfun " Use s:NetrwOptionRestore() to restore user settings fun! s:NetrwSafeOptions() " call Dfunc("s:NetrwSafeOptions() win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%"))."> winnr($)=".winnr("$")) -" call Decho("win#".winnr()."'s ft=".&ft) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("win#".winnr()."'s ft=".&ft,'~'.expand("<slnum>")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) if exists("+acd") | setl noacd | endif setl noai setl noaw @@ -1769,9 +1771,7 @@ fun! s:NetrwSafeOptions() setl bt=nofile setl noci setl nocin - if g:netrw_liststyle == s:TREELIST - setl bh=hide - endif + setl bh=hide setl cino= setl com= setl cpo-=a @@ -1793,14 +1793,14 @@ fun! s:NetrwSafeOptions() call s:NetrwCursor() " allow the user to override safe options -" call Decho("ft<".&ft."> ei=".&ei) +" call Decho("ft<".&ft."> ei=".&ei,'~'.expand("<slnum>")) if &ft == "netrw" -" call Decho("do any netrw FileType autocmds (doau FileType netrw)") +" call Decho("do any netrw FileType autocmds (doau FileType netrw)",'~'.expand("<slnum>")) sil! keepalt NetrwKeepj doau FileType netrw endif -" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." bh=".&l:bh." bt<".&bt.">") -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." bh=".&l:bh." bt<".&bt.">",'~'.expand("<slnum>")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) " call Dret("s:NetrwSafeOptions") endfun @@ -1857,7 +1857,7 @@ fun! netrw#NetRead(mode,...) " NetrwSafeOptions sets a buffer up for a netrw listing, which includes buflisting off. " However, this setting is not wanted for a remote editing session. The buffer should be "nofile", still. setl bl -" call Decho("(netrw#NetRead) buf#".bufnr("%")."<".bufname("%")."> bl=".&bl." bt=".&bt." bh=".&bh) +" call Decho("(netrw#NetRead) buf#".bufnr("%")."<".bufname("%")."> bl=".&bl." bt=".&bt." bh=".&bh,'~'.expand("<slnum>")) " NetRead: interpret mode into a readcmd {{{3 if a:mode == 0 " read remote file before current line @@ -1873,7 +1873,7 @@ fun! netrw#NetRead(mode,...) let readcmd = "r" endif let ichoice = (a:0 == 0)? 0 : 1 -" call Decho("readcmd<".readcmd."> ichoice=".ichoice) +" call Decho("readcmd<".readcmd."> ichoice=".ichoice,'~'.expand("<slnum>")) " NetRead: get temporary filename {{{3 let tmpfile= s:GetTempfile("") @@ -1886,13 +1886,13 @@ fun! netrw#NetRead(mode,...) " attempt to repeat with previous host-file-etc if exists("b:netrw_lastfile") && a:0 == 0 -" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">") +" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>")) let choice = b:netrw_lastfile let ichoice= ichoice + 1 else exe "let choice= a:" . ichoice -" call Decho("no lastfile: choice<" . choice . ">") +" call Decho("no lastfile: choice<" . choice . ">",'~'.expand("<slnum>")) if match(choice,"?") == 0 " give help @@ -1915,7 +1915,7 @@ fun! netrw#NetRead(mode,...) elseif match(choice,'^"') != -1 " Reconstruct Choice if choice starts with '"' -" call Decho("reconstructing choice") +" call Decho("reconstructing choice",'~'.expand("<slnum>")) if match(choice,'"$') != -1 " case "..." let choice= strpart(choice,1,strlen(choice)-2) @@ -1941,7 +1941,7 @@ fun! netrw#NetRead(mode,...) endif endif -" call Decho("choice<" . choice . ">") +" call Decho("choice<" . choice . ">",'~'.expand("<slnum>")) let ichoice= ichoice + 1 " NetRead: Determine method of read (ftp, rcp, etc) {{{3 @@ -1953,9 +1953,9 @@ fun! netrw#NetRead(mode,...) let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix " Check whether or not NetrwBrowse() should be handling this request -" call Decho("checking if NetrwBrowse() should handle choice<".choice."> with netrw_list_cmd<".g:netrw_list_cmd.">") +" call Decho("checking if NetrwBrowse() should handle choice<".choice."> with netrw_list_cmd<".g:netrw_list_cmd.">",'~'.expand("<slnum>")) if choice =~ "^.*[\/]$" && b:netrw_method != 5 && choice !~ '^https\=://' -" call Decho("yes, choice matches '^.*[\/]$'") +" call Decho("yes, choice matches '^.*[\/]$'",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwBrowse(0,choice) " call Dret("netrw#NetRead :3 getcwd<".getcwd().">") return @@ -1971,7 +1971,7 @@ fun! netrw#NetRead(mode,...) "......................................... " NetRead: (rcp) NetRead Method #1 {{{3 if b:netrw_method == 1 " read with rcp -" call Decho("read via rcp (method #1)") +" call Decho("read via rcp (method #1)",'~'.expand("<slnum>")) " ER: nothing done with g:netrw_uid yet? " ER: on Win2K" rcp machine[.user]:file tmpfile " ER: when machine contains '.' adding .user is required (use $USERNAME) @@ -1990,30 +1990,30 @@ fun! netrw#NetRead(mode,...) let uid_machine = g:netrw_machine endif endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".shellescape(uid_machine.":".b:netrw_fname,1)." ".shellescape(tmpfile,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice "......................................... " NetRead: (ftp + <.netrc>) NetRead Method #2 {{{3 elseif b:netrw_method == 2 " read with ftp + <.netrc> -" call Decho("read via ftp+.netrc (method #2)") +" call Decho("read via ftp+.netrc (method #2)",'~'.expand("<slnum>")) let netrw_fname= b:netrw_fname NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars() let filtbuf= bufnr("%") setl ff=unix NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline(line("$"))) +" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline(line("$"))) +" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) endif call setline(line("$")+1,'get "'.netrw_fname.'" '.tmpfile) -" call Decho("filter input: ".getline(line("$"))) +" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)." ".shellescape(g:netrw_port,1)) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) else - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) endif " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' @@ -2023,7 +2023,7 @@ fun! netrw#NetRead(mode,...) let &debug = debugkeep endif call s:SaveBufVars() - bd! + keepj bd! if bufname("%") == "" && getline("$") == "" && line('$') == 1 " needed when one sources a file in a nolbl setting window via ftp q! @@ -2036,43 +2036,43 @@ fun! netrw#NetRead(mode,...) " NetRead: (ftp + machine,id,passwd,filename) NetRead Method #3 {{{3 elseif b:netrw_method == 3 " read with ftp + machine, id, passwd, and fname " Construct execution string (four lines) which will be passed through filter -" call Decho("read via ftp+mipf (method #3)") +" call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>")) let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars() let filtbuf= bufnr("%") setl ff=unix if exists("g:netrw_port") && g:netrw_port != "" NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) else NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif if exists("g:netrw_uid") && g:netrw_uid != "" if exists("g:netrw_ftp") && g:netrw_ftp == 1 NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) if exists("s:netrw_passwd") NetrwKeepj put ='\"'.s:netrw_passwd.'\"' endif -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) elseif exists("s:netrw_passwd") NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif endif if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif NetrwKeepj put ='get \"'.netrw_fname.'\" '.tmpfile -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) " perform ftp: " -i : turns off interactive prompting from ftp @@ -2082,19 +2082,19 @@ fun! netrw#NetRead(mode,...) call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" -" call Decho("error<".getline(1).">") +" call Decho("error<".getline(1).">",'~'.expand("<slnum>")) if !exists("g:netrw_quiet") call netrw#ErrorMsg(s:ERROR,getline(1),5) endif endif - call s:SaveBufVars()|bd!|call s:RestoreBufVars() + call s:SaveBufVars()|keepj bd!|call s:RestoreBufVars() let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice "......................................... " NetRead: (scp) NetRead Method #4 {{{3 elseif b:netrw_method == 4 " read with scp -" call Decho("read via scp (method #4)") +" call Decho("read via scp (method #4)",'~'.expand("<slnum>")) if exists("g:netrw_port") && g:netrw_port != "" let useport= " ".g:netrw_scpport." ".g:netrw_port else @@ -2107,14 +2107,14 @@ fun! netrw#NetRead(mode,...) else let tmpfile_get = tmpfile endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".shellescape(g:netrw_machine.":".b:netrw_fname,1)." ".shellescape(tmpfile_get,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)." ".s:ShellEscape(tmpfile_get,1)) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice "......................................... " NetRead: (http) NetRead Method #5 (wget) {{{3 elseif b:netrw_method == 5 -" call Decho("read via http (method #5)") +" call Decho("read via http (method #5)",'~'.expand("<slnum>")) if g:netrw_http_cmd == "" if !exists("g:netrw_quiet") call netrw#ErrorMsg(s:ERROR,"neither the wget nor the fetch command is available",6) @@ -2125,34 +2125,34 @@ fun! netrw#NetRead(mode,...) if match(b:netrw_fname,"#") == -1 || exists("g:netrw_http_xcmd") " using g:netrw_http_cmd (usually elinks, links, curl, wget, or fetch) -" call Decho('using '.g:netrw_http_cmd.' (# not in b:netrw_fname<'.b:netrw_fname.">)") +" call Decho('using '.g:netrw_http_cmd.' (# not in b:netrw_fname<'.b:netrw_fname.">)",'~'.expand("<slnum>")) if exists("g:netrw_http_xcmd") - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".shellescape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)." ".g:netrw_http_xcmd." ".shellescape(tmpfile,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)." ".g:netrw_http_xcmd." ".s:ShellEscape(tmpfile,1)) else - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".shellescape(tmpfile,1)." ".shellescape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)) endif let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) else " wget/curl/fetch plus a jump to an in-page marker (ie. http://abc/def.html#aMarker) -" call Decho("wget/curl plus jump (# in b:netrw_fname<".b:netrw_fname.">)") +" call Decho("wget/curl plus jump (# in b:netrw_fname<".b:netrw_fname.">)",'~'.expand("<slnum>")) let netrw_html= substitute(b:netrw_fname,"#.*$","","") let netrw_tag = substitute(b:netrw_fname,"^.*#","","") -" call Decho("netrw_html<".netrw_html.">") -" call Decho("netrw_tag <".netrw_tag.">") - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".shellescape(tmpfile,1)." ".shellescape(b:netrw_http."://".g:netrw_machine.netrw_html,1)) +" call Decho("netrw_html<".netrw_html.">",'~'.expand("<slnum>")) +" call Decho("netrw_tag <".netrw_tag.">",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.netrw_html,1)) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) -" call Decho('<\s*a\s*name=\s*"'.netrw_tag.'"/') +" call Decho('<\s*a\s*name=\s*"'.netrw_tag.'"/','~'.expand("<slnum>")) exe 'NetrwKeepj norm! 1G/<\s*a\s*name=\s*"'.netrw_tag.'"/'."\<CR>" endif let b:netrw_lastfile = choice -" call Decho("setl ro") +" call Decho("setl ro",'~'.expand("<slnum>")) setl ro nomod "......................................... " NetRead: (dav) NetRead Method #6 {{{3 elseif b:netrw_method == 6 -" call Decho("read via cadaver (method #6)") +" call Decho("read via cadaver (method #6)",'~'.expand("<slnum>")) if !executable(g:netrw_dav_cmd) call netrw#ErrorMsg(s:ERROR,g:netrw_dav_cmd." is not executable",73) @@ -2160,7 +2160,7 @@ fun! netrw#NetRead(mode,...) return endif if g:netrw_dav_cmd =~ "curl" - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_dav_cmd." ".shellescape("dav://".g:netrw_machine.b:netrw_fname,1)." ".shellescape(tmpfile,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_dav_cmd." ".s:ShellEscape("dav://".g:netrw_machine.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) else " Construct execution string (four lines) which will be passed through filter let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) @@ -2180,7 +2180,7 @@ fun! netrw#NetRead(mode,...) " perform cadaver operation: NetrwKeepj norm! 1Gdd call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd) - bd! + keepj bd! endif let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice @@ -2188,8 +2188,8 @@ fun! netrw#NetRead(mode,...) "......................................... " NetRead: (rsync) NetRead Method #7 {{{3 elseif b:netrw_method == 7 -" call Decho("read via rsync (method #7)") - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".shellescape(g:netrw_machine.":".b:netrw_fname,1)." ".shellescape(tmpfile,1)) +" call Decho("read via rsync (method #7)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method) let b:netrw_lastfile = choice @@ -2197,7 +2197,7 @@ fun! netrw#NetRead(mode,...) " NetRead: (fetch) NetRead Method #8 {{{3 " fetch://[user@]host[:http]/path elseif b:netrw_method == 8 -" call Decho("read via fetch (method #8)") +" call Decho("read via fetch (method #8)",'~'.expand("<slnum>")) if g:netrw_fetch_cmd == "" if !exists("g:netrw_quiet") NetrwKeepj call netrw#ErrorMsg(s:ERROR,"fetch command not available",7) @@ -2210,32 +2210,32 @@ fun! netrw#NetRead(mode,...) else let netrw_option= "ftp" endif -" call Decho("read via fetch for ".netrw_option) +" call Decho("read via fetch for ".netrw_option,'~'.expand("<slnum>")) if exists("g:netrw_uid") && g:netrw_uid != "" && exists("s:netrw_passwd") && s:netrw_passwd != "" - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".shellescape(tmpfile,1)." ".shellescape(netrw_option."://".g:netrw_uid.':'.s:netrw_passwd.'@'.g:netrw_machine."/".b:netrw_fname,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_uid.':'.s:netrw_passwd.'@'.g:netrw_machine."/".b:netrw_fname,1)) else - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".shellescape(tmpfile,1)." ".shellescape(netrw_option."://".g:netrw_machine."/".b:netrw_fname,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_machine."/".b:netrw_fname,1)) endif let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method) let b:netrw_lastfile = choice -" call Decho("setl ro") +" call Decho("setl ro",'~'.expand("<slnum>")) setl ro nomod "......................................... " NetRead: (sftp) NetRead Method #9 {{{3 elseif b:netrw_method == 9 -" call Decho("read via sftp (method #9)") - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".shellescape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile) +" call Decho("read via sftp (method #9)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice "......................................... " NetRead: (file) NetRead Method #10 {{{3 elseif b:netrw_method == 10 && exists("g:netrw_file_cmd") -" " call Decho("read via ".b:netrw_file_cmd." (method #10)") - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_file_cmd." ".shellescape(b:netrw_fname,1)." ".tmpfile) +" " call Decho("read via ".b:netrw_file_cmd." (method #10)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_file_cmd." ".s:ShellEscape(b:netrw_fname,1)." ".tmpfile) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice @@ -2248,12 +2248,12 @@ fun! netrw#NetRead(mode,...) " NetRead: cleanup {{{3 if exists("b:netrw_method") -" call Decho("cleanup b:netrw_method and b:netrw_fname") +" call Decho("cleanup b:netrw_method and b:netrw_fname",'~'.expand("<slnum>")) unlet b:netrw_method unlet b:netrw_fname endif if s:FileReadable(tmpfile) && tmpfile !~ '.tar.bz2$' && tmpfile !~ '.tar.gz$' && tmpfile !~ '.zip' && tmpfile !~ '.tar' && readcmd != 't' && tmpfile !~ '.tar.xz$' && tmpfile !~ '.txz' -" call Decho("cleanup by deleting tmpfile<".tmpfile.">") +" call Decho("cleanup by deleting tmpfile<".tmpfile.">",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwDelete(tmpfile) endif NetrwKeepj call s:NetrwOptionRestore("w:") @@ -2285,21 +2285,21 @@ fun! netrw#NetWrite(...) range endif let curbufname= expand("%") -" call Decho("curbufname<".curbufname.">") +" call Decho("curbufname<".curbufname.">",'~'.expand("<slnum>")) if &binary " For binary writes, always write entire file. " (line numbers don't really make sense for that). " Also supports the writing of tar and zip files. -" call Decho("(write entire file) sil exe w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile)) +" call Decho("(write entire file) sil exe w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>")) exe "sil NetrwKeepj w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile) elseif g:netrw_cygwin " write (selected portion of) file to temporary let cygtmpfile= substitute(tmpfile,g:netrw_cygdrive.'/\(.\)','\1:','') -" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile)) +" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile),'~'.expand("<slnum>")) exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile) else " write (selected portion of) file to temporary -" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile)) +" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>")) exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile) endif @@ -2316,7 +2316,7 @@ fun! netrw#NetWrite(...) range " Process arguments: {{{4 " attempt to repeat with previous host-file-etc if exists("b:netrw_lastfile") && a:0 == 0 -" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">") +" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>")) let choice = b:netrw_lastfile let ichoice= ichoice + 1 else @@ -2364,7 +2364,7 @@ fun! netrw#NetWrite(...) range endif endif let ichoice= ichoice + 1 -" call Decho("choice<" . choice . "> ichoice=".ichoice) +" call Decho("choice<" . choice . "> ichoice=".ichoice,'~'.expand("<slnum>")) " Determine method of write (ftp, rcp, etc) {{{4 NetrwKeepj call s:NetrwMethod(choice) @@ -2378,13 +2378,13 @@ fun! netrw#NetWrite(...) range " ============================ if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 echo "(netrw) Processing your write request..." -" call Decho("(netrw) Processing your write request...") +" call Decho("(netrw) Processing your write request...",'~'.expand("<slnum>")) endif "......................................... " NetWrite: (rcp) NetWrite Method #1 {{{3 if b:netrw_method == 1 -" call Decho("write via rcp (method #1)") +" call Decho("write via rcp (method #1)",'~'.expand("<slnum>")) if s:netrw_has_nt_rcp == 1 if exists("g:netrw_uid") && ( g:netrw_uid != "" ) let uid_machine = g:netrw_machine .'.'. g:netrw_uid @@ -2398,36 +2398,36 @@ fun! netrw#NetWrite(...) range let uid_machine = g:netrw_machine endif endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".shellescape(tmpfile,1)." ".shellescape(uid_machine.":".b:netrw_fname,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)) let b:netrw_lastfile = choice "......................................... " NetWrite: (ftp + <.netrc>) NetWrite Method #2 {{{3 elseif b:netrw_method == 2 -" call Decho("write via ftp+.netrc (method #2)") +" call Decho("write via ftp+.netrc (method #2)",'~'.expand("<slnum>")) let netrw_fname = b:netrw_fname " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead let bhkeep = &l:bh let curbuf = bufnr("%") setl bh=hide - keepalt enew + keepj keepalt enew -" call Decho("filter input window#".winnr()) +" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) setl ff=unix NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline("$")) +" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) endif NetrwKeepj call setline(line("$")+1,'put "'.tmpfile.'" "'.netrw_fname.'"') -" call Decho("filter input: ".getline("$")) +" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)." ".shellescape(g:netrw_port,1)) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) else -" call Decho("filter input window#".winnr()) - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)) +" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) endif " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" @@ -2449,44 +2449,44 @@ fun! netrw#NetWrite(...) range " NetWrite: (ftp + machine, id, passwd, filename) NetWrite Method #3 {{{3 elseif b:netrw_method == 3 " Construct execution string (three or more lines) which will be passed through filter -" call Decho("read via ftp+mipf (method #3)") +" call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>")) let netrw_fname = b:netrw_fname let bhkeep = &l:bh " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead let curbuf = bufnr("%") setl bh=hide - keepalt enew + keepj keepalt enew setl ff=unix if exists("g:netrw_port") && g:netrw_port != "" NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) else NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif if exists("g:netrw_uid") && g:netrw_uid != "" if exists("g:netrw_ftp") && g:netrw_ftp == 1 NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) if exists("s:netrw_passwd") && s:netrw_passwd != "" NetrwKeepj put ='\"'.s:netrw_passwd.'\"' endif -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) elseif exists("s:netrw_passwd") && s:netrw_passwd != "" NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif endif NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline("$")) +" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) endif NetrwKeepj put ='put \"'.tmpfile.'\" \"'.netrw_fname.'\"' -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) " save choice/id/password for future use let b:netrw_lastfile = choice @@ -2513,23 +2513,23 @@ fun! netrw#NetWrite(...) range "......................................... " NetWrite: (scp) NetWrite Method #4 {{{3 elseif b:netrw_method == 4 -" call Decho("write via scp (method #4)") +" call Decho("write via scp (method #4)",'~'.expand("<slnum>")) if exists("g:netrw_port") && g:netrw_port != "" let useport= " ".g:netrw_scpport." ".fnameescape(g:netrw_port) else let useport= "" endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".shellescape(tmpfile,1)." ".shellescape(g:netrw_machine.":".b:netrw_fname,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)) let b:netrw_lastfile = choice "......................................... " NetWrite: (http) NetWrite Method #5 {{{3 elseif b:netrw_method == 5 -" call Decho("write via http (method #5)") +" call Decho("write via http (method #5)",'~'.expand("<slnum>")) let curl= substitute(g:netrw_http_put_cmd,'\s\+.*$',"","") if executable(curl) let url= g:netrw_choice - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".shellescape(tmpfile,1)." ".shellescape(url,1) ) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(url,1) ) elseif !exists("g:netrw_quiet") call netrw#ErrorMsg(s:ERROR,"can't write to http using <".g:netrw_http_put_cmd".">".",16) endif @@ -2537,7 +2537,7 @@ fun! netrw#NetWrite(...) range "......................................... " NetWrite: (dav) NetWrite Method #6 (cadaver) {{{3 elseif b:netrw_method == 6 -" call Decho("write via cadaver (method #6)") +" call Decho("write via cadaver (method #6)",'~'.expand("<slnum>")) " Construct execution string (four lines) which will be passed through filter let netrw_fname = escape(b:netrw_fname,g:netrw_fname_escape) @@ -2546,7 +2546,7 @@ fun! netrw#NetWrite(...) range " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead let curbuf = bufnr("%") setl bh=hide - keepalt enew + keepj keepalt enew setl ff=unix if exists("g:netrw_port") && g:netrw_port != "" @@ -2574,14 +2574,14 @@ fun! netrw#NetWrite(...) range "......................................... " NetWrite: (rsync) NetWrite Method #7 {{{3 elseif b:netrw_method == 7 -" call Decho("write via rsync (method #7)") - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".shellescape(tmpfile,1)." ".shellescape(g:netrw_machine.":".b:netrw_fname,1)) +" call Decho("write via rsync (method #7)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)) let b:netrw_lastfile = choice "......................................... " NetWrite: (sftp) NetWrite Method #9 {{{3 elseif b:netrw_method == 9 -" call Decho("write via sftp (method #9)") +" call Decho("write via sftp (method #9)",'~'.expand("<slnum>")) let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) if exists("g:netrw_uid") && ( g:netrw_uid != "" ) let uid_machine = g:netrw_uid .'@'. g:netrw_machine @@ -2593,13 +2593,13 @@ fun! netrw#NetWrite(...) range let bhkeep = &l:bh let curbuf = bufnr("%") setl bh=hide - keepalt enew + keepj keepalt enew setl ff=unix call setline(1,'put "'.escape(tmpfile,'\').'" '.netrw_fname) -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) let sftpcmd= substitute(g:netrw_sftp_cmd,"%TEMPFILE%",escape(tmpfile,'\'),"g") - call s:NetrwExe(s:netrw_silentxfer."%!".sftpcmd.' '.shellescape(uid_machine,1)) + call s:NetrwExe(s:netrw_silentxfer."%!".sftpcmd.' '.s:ShellEscape(uid_machine,1)) let filtbuf= bufnr("%") exe curbuf."b!" let &l:bh = bhkeep @@ -2615,9 +2615,9 @@ fun! netrw#NetWrite(...) range endwhile " NetWrite: Cleanup: {{{3 -" call Decho("cleanup") +" call Decho("cleanup",'~'.expand("<slnum>")) if s:FileReadable(tmpfile) -" call Decho("tmpfile<".tmpfile."> readable, will now delete it") +" call Decho("tmpfile<".tmpfile."> readable, will now delete it",'~'.expand("<slnum>")) call s:NetrwDelete(tmpfile) endif call s:NetrwOptionRestore("w:") @@ -2625,12 +2625,12 @@ fun! netrw#NetWrite(...) range if a:firstline == 1 && a:lastline == line("$") " restore modifiability; usually equivalent to set nomod let &mod= mod -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) elseif !exists("leavemod") " indicate that the buffer has not been modified since last written -" call Decho("set nomod") +" call Decho("set nomod",'~'.expand("<slnum>")) setl nomod -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) endif " call Dret("netrw#NetWrite") @@ -2659,11 +2659,11 @@ fun! netrw#NetSource(...) let i= 1 while i <= a:0 call netrw#NetRead(3,a:{i}) -" call Decho("s:netread_tmpfile<".s:netrw_tmpfile.">") +" call Decho("s:netread_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>")) if s:FileReadable(s:netrw_tmpfile) -" call Decho("exe so ".fnameescape(s:netrw_tmpfile)) +" call Decho("exe so ".fnameescape(s:netrw_tmpfile),'~'.expand("<slnum>")) exe "so ".fnameescape(s:netrw_tmpfile) -" call Decho("delete(".s:netrw_tmpfile.")") +" call Decho("delete(".s:netrw_tmpfile.")",'~'.expand("<slnum>")) call delete(s:netrw_tmpfile) unlet s:netrw_tmpfile else @@ -2683,33 +2683,37 @@ fun! netrw#SetTreetop(...) " clear out the current tree if exists("w:netrw_treetop") -" call Decho("clearing out current tree") +" call Decho("clearing out current tree",'~'.expand("<slnum>")) let inittreetop= w:netrw_treetop unlet w:netrw_treetop endif if exists("w:netrw_treedict") -" call Decho("freeing w:netrw_treedict") +" call Decho("freeing w:netrw_treedict",'~'.expand("<slnum>")) unlet w:netrw_treedict endif if a:1 == "" && exists("inittreetop") let treedir= s:NetrwTreePath(inittreetop) -" call Decho("treedir<".treedir.">") +" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) else - if isdirectory(a:1) -" call Decho("a:1<".a:1."> is a directory") + if isdirectory(s:NetrwFile(a:1)) +" call Decho("a:1<".a:1."> is a directory",'~'.expand("<slnum>")) let treedir= a:1 - elseif exists("b:netrw_curdir") && isdirectory(b:netrw_curdir."/".a:1) + elseif exists("b:netrw_curdir") && (isdirectory(s:NetrwFile(b:netrw_curdir."/".a:1)) || a:1 =~ '^\a\{3,}://') let treedir= b:netrw_curdir."/".a:1 -" call Decho("a:1<".a:1."> is NOT a directory, trying treedir<".treedir.">") +" call Decho("a:1<".a:1."> is NOT a directory, trying treedir<".treedir.">",'~'.expand("<slnum>")) else + " normally the cursor is left in the message window. + " However, here this results in the directory being listed in the message window, which is not wanted. + let netrwbuf= bufnr("%") call netrw#ErrorMsg(s:ERROR,"sorry, ".a:1." doesn't seem to be a directory!",95) + exe bufwinnr(netrwbuf)."wincmd w" let treedir= "." endif endif -" call Decho("treedir<".treedir.">") - let islocal= expand("%") !~ '^\a\+://' -" call Decho("islocal=".islocal) +" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) + let islocal= expand("%") !~ '^\a\{3,}://' +" call Decho("islocal=".islocal,'~'.expand("<slnum>")) if islocal call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(islocal,treedir)) else @@ -2729,14 +2733,14 @@ fun! s:NetrwGetFile(readcmd, tfile, method) " readcmd=='t': simply do nothing if a:readcmd == 't' -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("NetrwGetFile : skip read of <".a:tfile.">") return endif " get name of remote filename (ie. url and all) let rfile= bufname("%") -" call Decho("rfile<".rfile.">") +" call Decho("rfile<".rfile.">",'~'.expand("<slnum>")) if exists("*NetReadFixup") " for the use of NetReadFixup (not otherwise used internally) @@ -2745,7 +2749,7 @@ fun! s:NetrwGetFile(readcmd, tfile, method) if a:readcmd[0] == '%' " get file into buffer -" call Decho("get file into buffer") +" call Decho("get file into buffer",'~'.expand("<slnum>")) " rename the current buffer to the temp file (ie. tfile) if g:netrw_cygwin @@ -2753,41 +2757,41 @@ fun! s:NetrwGetFile(readcmd, tfile, method) else let tfile= a:tfile endif -" call Decho("exe sil! keepalt file ".fnameescape(tfile)) +" call Decho("exe sil! keepalt file ".fnameescape(tfile),'~'.expand("<slnum>")) exe "sil! keepalt file ".fnameescape(tfile) " edit temporary file (ie. read the temporary file in) if rfile =~ '\.zip$' -" call Decho("handling remote zip file with zip#Browse(tfile<".tfile.">)") +" call Decho("handling remote zip file with zip#Browse(tfile<".tfile.">)",'~'.expand("<slnum>")) call zip#Browse(tfile) elseif rfile =~ '\.tar$' -" call Decho("handling remote tar file with tar#Browse(tfile<".tfile.">)") +" call Decho("handling remote tar file with tar#Browse(tfile<".tfile.">)",'~'.expand("<slnum>")) call tar#Browse(tfile) elseif rfile =~ '\.tar\.gz$' -" call Decho("handling remote gzip-compressed tar file") +" call Decho("handling remote gzip-compressed tar file",'~'.expand("<slnum>")) call tar#Browse(tfile) elseif rfile =~ '\.tar\.bz2$' -" call Decho("handling remote bz2-compressed tar file") +" call Decho("handling remote bz2-compressed tar file",'~'.expand("<slnum>")) call tar#Browse(tfile) elseif rfile =~ '\.tar\.xz$' -" call Decho("handling remote xz-compressed tar file") +" call Decho("handling remote xz-compressed tar file",'~'.expand("<slnum>")) call tar#Browse(tfile) elseif rfile =~ '\.txz$' -" call Decho("handling remote xz-compressed tar file (.txz)") +" call Decho("handling remote xz-compressed tar file (.txz)",'~'.expand("<slnum>")) call tar#Browse(tfile) else -" call Decho("edit temporary file") +" call Decho("edit temporary file",'~'.expand("<slnum>")) NetrwKeepj e! endif " rename buffer back to remote filename -" call Decho("exe sil! keepalt file ".fnameescape(rfile)) +" call Decho("exe sil! keepalt file ".fnameescape(rfile),'~'.expand("<slnum>")) exe "sil! NetrwKeepj keepalt file ".fnameescape(rfile) " Detect filetype of local version of remote file. " Note that isk must not include a "/" for scripts.vim " to process this detection correctly. -" call Decho("detect filetype of local version of remote file") +" call Decho("detect filetype of local version of remote file",'~'.expand("<slnum>")) let iskkeep= &l:isk setl isk-=/ let &l:isk= iskkeep @@ -2798,23 +2802,23 @@ fun! s:NetrwGetFile(readcmd, tfile, method) elseif !&ma " attempting to read a file after the current line in the file, but the buffer is not modifiable NetrwKeepj call netrw#ErrorMsg(s:WARNING,"attempt to read<".a:tfile."> into a non-modifiable buffer!",94) -" call Dret("NetrwGetFile : attempt to read<".a:tfile."> into a non-modifiable buffer!") +" call Dret("NetrwGetFile : attempt to read<".a:tfile."> into a non-modifiable buffer!") return elseif s:FileReadable(a:tfile) " read file after current line -" call Decho("read file<".a:tfile."> after current line") +" call Decho("read file<".a:tfile."> after current line",'~'.expand("<slnum>")) let curline = line(".") let lastline= line("$") -" call Decho("exe<".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)."> line#".curline) +" call Decho("exe<".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)."> line#".curline,'~'.expand("<slnum>")) exe "NetrwKeepj ".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile) let line1= curline + 1 let line2= line("$") - lastline + 1 else " not readable -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") -" call Decho("tfile<".a:tfile."> not readable") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) +" call Decho("tfile<".a:tfile."> not readable",'~'.expand("<slnum>")) NetrwKeepj call netrw#ErrorMsg(s:WARNING,"file <".a:tfile."> not readable",9) " call Dret("NetrwGetFile : tfile<".a:tfile."> not readable") return @@ -2822,10 +2826,10 @@ fun! s:NetrwGetFile(readcmd, tfile, method) " User-provided (ie. optional) fix-it-up command if exists("*NetReadFixup") -" call Decho("calling NetReadFixup(method<".a:method."> line1=".line1." line2=".line2.")") +" call Decho("calling NetReadFixup(method<".a:method."> line1=".line1." line2=".line2.")",'~'.expand("<slnum>")) NetrwKeepj call NetReadFixup(a:method, line1, line2) " else " Decho -" call Decho("NetReadFixup() not called, doesn't exist (line1=".line1." line2=".line2.")") +" call Decho("NetReadFixup() not called, doesn't exist (line1=".line1." line2=".line2.")",'~'.expand("<slnum>")) endif if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu @@ -2833,12 +2837,12 @@ fun! s:NetrwGetFile(readcmd, tfile, method) NetrwKeepj call s:UpdateBuffersMenu() endif -" call Decho("readcmd<".a:readcmd."> cmdarg<".v:cmdarg."> tfile<".a:tfile."> readable=".s:FileReadable(a:tfile)) +" call Decho("readcmd<".a:readcmd."> cmdarg<".v:cmdarg."> tfile<".a:tfile."> readable=".s:FileReadable(a:tfile),'~'.expand("<slnum>")) " make sure file is being displayed " redraw! -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("NetrwGetFile") endfun @@ -2847,15 +2851,15 @@ endfun " Input: " choice = url [protocol:]//[userid@]hostname[:port]/[path-to-file] " Output: -" b:netrw_method= 1: rcp -" 2: ftp + <.netrc> -" 3: ftp + machine, id, password, and [path]filename -" 4: scp -" 5: http[s] (wget) +" b:netrw_method= 1: rcp +" 2: ftp + <.netrc> +" 3: ftp + machine, id, password, and [path]filename +" 4: scp +" 5: http[s] (wget) " 6: dav -" 7: rsync -" 8: fetch -" 9: sftp +" 7: rsync +" 8: fetch +" 9: sftp " 10: file " g:netrw_machine= hostname " b:netrw_fname = filename @@ -2876,7 +2880,7 @@ fun! s:NetrwMethod(choice) " curmachine used if protocol == ftp and no .netrc if exists("g:netrw_machine") let curmachine= g:netrw_machine -" call Decho("curmachine<".curmachine.">") +" call Decho("curmachine<".curmachine.">",'~'.expand("<slnum>")) else let curmachine= "N O T A HOST" endif @@ -2921,11 +2925,11 @@ fun! s:NetrwMethod(choice) let sftpurm = '^sftp://\([^/]\{-}\)/\(.*\)\=$' let fileurm = '^file\=://\(.*\)$' -" call Decho("determine method:") +" call Decho("determine method:",'~'.expand("<slnum>")) " Determine Method " Method#1: rcp://user@hostname/...path-to-file {{{3 if match(a:choice,rcpurm) == 0 -" call Decho("rcp://...") +" call Decho("rcp://...",'~'.expand("<slnum>")) let b:netrw_method = 1 let userid = substitute(a:choice,rcpurm,'\1',"") let g:netrw_machine = substitute(a:choice,rcpurm,'\2',"") @@ -2936,7 +2940,7 @@ fun! s:NetrwMethod(choice) " Method#4: scp://user@hostname/...path-to-file {{{3 elseif match(a:choice,scpurm) == 0 -" call Decho("scp://...") +" call Decho("scp://...",'~'.expand("<slnum>")) let b:netrw_method = 4 let g:netrw_machine = substitute(a:choice,scpurm,'\1',"") let g:netrw_port = substitute(a:choice,scpurm,'\2',"") @@ -2944,7 +2948,7 @@ fun! s:NetrwMethod(choice) " Method#5: http[s]://user@hostname/...path-to-file {{{3 elseif match(a:choice,httpurm) == 0 -" call Decho("http[s]://...") +" call Decho("http[s]://...",'~'.expand("<slnum>")) let b:netrw_method = 5 let g:netrw_machine= substitute(a:choice,httpurm,'\1',"") let b:netrw_fname = substitute(a:choice,httpurm,'\2',"") @@ -2952,7 +2956,7 @@ fun! s:NetrwMethod(choice) " Method#6: dav://hostname[:port]/..path-to-file.. {{{3 elseif match(a:choice,davurm) == 0 -" call Decho("dav://...") +" call Decho("dav://...",'~'.expand("<slnum>")) let b:netrw_method= 6 if a:choice =~ 'davs:' let g:netrw_machine= 'https://'.substitute(a:choice,davurm,'\1/\2',"") @@ -2963,19 +2967,19 @@ fun! s:NetrwMethod(choice) " Method#7: rsync://user@hostname/...path-to-file {{{3 elseif match(a:choice,rsyncurm) == 0 -" call Decho("rsync://...") +" call Decho("rsync://...",'~'.expand("<slnum>")) let b:netrw_method = 7 let g:netrw_machine= substitute(a:choice,rsyncurm,'\1',"") let b:netrw_fname = substitute(a:choice,rsyncurm,'\2',"") " Methods 2,3: ftp://[user@]hostname[[:#]port]/...path-to-file {{{3 elseif match(a:choice,ftpurm) == 0 -" call Decho("ftp://...") +" call Decho("ftp://...",'~'.expand("<slnum>")) let userid = substitute(a:choice,ftpurm,'\2',"") let g:netrw_machine= substitute(a:choice,ftpurm,'\3',"") let g:netrw_port = substitute(a:choice,ftpurm,'\4',"") let b:netrw_fname = substitute(a:choice,ftpurm,'\5',"") -" call Decho("g:netrw_machine<".g:netrw_machine.">") +" call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>")) if userid != "" let g:netrw_uid= userid endif @@ -3000,15 +3004,15 @@ fun! s:NetrwMethod(choice) call NetUserPass("ftp:".host) elseif (has("win32") || has("win95") || has("win64") || has("win16")) && s:netrw_ftp_cmd =~ '-[sS]:' -" call Decho("has -s: : s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">") -" call Decho(" g:netrw_ftp_cmd<".g:netrw_ftp_cmd.">") +" call Decho("has -s: : s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>")) +" call Decho(" g:netrw_ftp_cmd<".g:netrw_ftp_cmd.">",'~'.expand("<slnum>")) if g:netrw_ftp_cmd =~ '-[sS]:\S*MACHINE\>' let s:netrw_ftp_cmd= substitute(g:netrw_ftp_cmd,'\<MACHINE\>',g:netrw_machine,'') -" call Decho("s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">") +" call Decho("s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>")) endif let b:netrw_method= 2 elseif s:FileReadable(expand("$HOME/.netrc")) && !g:netrw_ignorenetrc -" call Decho("using <".expand("$HOME/.netrc")."> (readable)") +" call Decho("using <".expand("$HOME/.netrc")."> (readable)",'~'.expand("<slnum>")) let b:netrw_method= 2 else if !exists("g:netrw_uid") || g:netrw_uid == "" @@ -3023,7 +3027,7 @@ fun! s:NetrwMethod(choice) " Method#8: fetch {{{3 elseif match(a:choice,fetchurm) == 0 -" call Decho("fetch://...") +" call Decho("fetch://...",'~'.expand("<slnum>")) let b:netrw_method = 8 let g:netrw_userid = substitute(a:choice,fetchurm,'\2',"") let g:netrw_machine= substitute(a:choice,fetchurm,'\3',"") @@ -3032,7 +3036,7 @@ fun! s:NetrwMethod(choice) " Method#3: Issue an ftp : "machine id password [path/]filename" {{{3 elseif match(a:choice,mipf) == 0 -" call Decho("(ftp) host id pass file") +" call Decho("(ftp) host id pass file",'~'.expand("<slnum>")) let b:netrw_method = 3 let g:netrw_machine = substitute(a:choice,mipf,'\1',"") let g:netrw_uid = substitute(a:choice,mipf,'\2',"") @@ -3042,7 +3046,7 @@ fun! s:NetrwMethod(choice) " Method#3: Issue an ftp: "hostname [path/]filename" {{{3 elseif match(a:choice,mf) == 0 -" call Decho("(ftp) host file") +" call Decho("(ftp) host file",'~'.expand("<slnum>")) if exists("g:netrw_uid") && exists("s:netrw_passwd") let b:netrw_method = 3 let g:netrw_machine = substitute(a:choice,mf,'\1',"") @@ -3056,32 +3060,32 @@ fun! s:NetrwMethod(choice) " Method#9: sftp://user@hostname/...path-to-file {{{3 elseif match(a:choice,sftpurm) == 0 -" call Decho("sftp://...") +" call Decho("sftp://...",'~'.expand("<slnum>")) let b:netrw_method = 9 let g:netrw_machine= substitute(a:choice,sftpurm,'\1',"") let b:netrw_fname = substitute(a:choice,sftpurm,'\2',"") " Method#1: Issue an rcp: hostname:filename" (this one should be last) {{{3 elseif match(a:choice,rcphf) == 0 -" call Decho("(rcp) [user@]host:file) rcphf<".rcphf.">") +" call Decho("(rcp) [user@]host:file) rcphf<".rcphf.">",'~'.expand("<slnum>")) let b:netrw_method = 1 let userid = substitute(a:choice,rcphf,'\2',"") let g:netrw_machine = substitute(a:choice,rcphf,'\3',"") let b:netrw_fname = substitute(a:choice,rcphf,'\4',"") -" call Decho('\1<'.substitute(a:choice,rcphf,'\1',"").">") -" call Decho('\2<'.substitute(a:choice,rcphf,'\2',"").">") -" call Decho('\3<'.substitute(a:choice,rcphf,'\3',"").">") -" call Decho('\4<'.substitute(a:choice,rcphf,'\4',"").">") +" call Decho('\1<'.substitute(a:choice,rcphf,'\1',"").">",'~'.expand("<slnum>")) +" call Decho('\2<'.substitute(a:choice,rcphf,'\2',"").">",'~'.expand("<slnum>")) +" call Decho('\3<'.substitute(a:choice,rcphf,'\3',"").">",'~'.expand("<slnum>")) +" call Decho('\4<'.substitute(a:choice,rcphf,'\4',"").">",'~'.expand("<slnum>")) if userid != "" let g:netrw_uid= userid endif " Method#10: file://user@hostname/...path-to-file {{{3 elseif match(a:choice,fileurm) == 0 && exists("g:netrw_file_cmd") -" call Decho("http[s]://...") +" call Decho("http[s]://...",'~'.expand("<slnum>")) let b:netrw_method = 10 let b:netrw_fname = substitute(a:choice,fileurm,'\1',"") -" call Decho('\1<'.substitute(a:choice,fileurm,'\1',"").">") +" call Decho('\1<'.substitute(a:choice,fileurm,'\1',"").">",'~'.expand("<slnum>")) " Cannot Determine Method {{{3 else @@ -3100,17 +3104,17 @@ fun! s:NetrwMethod(choice) let g:netrw_port= netrw_port endif -" call Decho("a:choice <".a:choice.">") -" call Decho("b:netrw_method <".b:netrw_method.">") -" call Decho("g:netrw_machine<".g:netrw_machine.">") -" call Decho("g:netrw_port <".g:netrw_port.">") +" call Decho("a:choice <".a:choice.">",'~'.expand("<slnum>")) +" call Decho("b:netrw_method <".b:netrw_method.">",'~'.expand("<slnum>")) +" call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>")) +" call Decho("g:netrw_port <".g:netrw_port.">",'~'.expand("<slnum>")) " if exists("g:netrw_uid") "Decho -" call Decho("g:netrw_uid <".g:netrw_uid.">") +" call Decho("g:netrw_uid <".g:netrw_uid.">",'~'.expand("<slnum>")) " endif "Decho " if exists("s:netrw_passwd") "Decho -" call Decho("s:netrw_passwd <".s:netrw_passwd.">") +" call Decho("s:netrw_passwd <".s:netrw_passwd.">",'~'.expand("<slnum>")) " endif "Decho -" call Decho("b:netrw_fname <".b:netrw_fname.">") +" call Decho("b:netrw_fname <".b:netrw_fname.">",'~'.expand("<slnum>")) " call Dret("NetrwMethod : b:netrw_method=".b:netrw_method." g:netrw_port=".g:netrw_port) endfun @@ -3189,14 +3193,14 @@ fun! NetUserPass(...) if a:1 =~ '^ftp:' " get host from ftp:... url " access userid and password from hup (host-user-passwd) dictionary -" call Decho("case a:0=1: a:1<".a:1."> (get host from ftp:... url)") +" call Decho("case a:0=1: a:1<".a:1."> (get host from ftp:... url)",'~'.expand("<slnum>")) let host = substitute(a:1,'^ftp:','','') let host = substitute(host,'\..*','','') if exists("s:netrw_hup[host]") let g:netrw_uid = s:netrw_hup[host].uid let s:netrw_passwd = s:netrw_hup[host].passwd -" call Decho("get s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">") -" call Decho("get s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">") +" call Decho("get s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>")) +" call Decho("get s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>")) else let g:netrw_uid = input("Enter UserId: ") let s:netrw_passwd = inputsecret("Enter Password: ") @@ -3204,7 +3208,7 @@ fun! NetUserPass(...) else " case: one input argument, not an url. Using it as a new user-id. -" call Decho("case a:0=1: a:1<".a:1."> (get host from input argument, not an url)") +" call Decho("case a:0=1: a:1<".a:1."> (get host from input argument, not an url)",'~'.expand("<slnum>")) if exists("g:netrw_machine") if g:netrw_machine =~ '[0-9.]\+' let host= g:netrw_machine @@ -3215,7 +3219,7 @@ fun! NetUserPass(...) let g:netrw_machine= input('Enter hostname: ') endif let g:netrw_uid = a:1 -" call Decho("set g:netrw_uid= <".g:netrw_uid.">") +" call Decho("set g:netrw_uid= <".g:netrw_uid.">",'~'.expand("<slnum>")) if exists("g:netrw_passwd") " ask for password if one not previously entered let s:netrw_passwd= g:netrw_passwd @@ -3224,7 +3228,7 @@ fun! NetUserPass(...) endif endif -" call Decho("host<".host.">") +" call Decho("host<".host.">",'~'.expand("<slnum>")) if exists("host") if !exists('s:netrw_hup[host]') let s:netrw_hup[host]= {} @@ -3248,8 +3252,8 @@ fun! NetUserPass(...) let s:netrw_hup[host].passwd = a:3 let g:netrw_uid = s:netrw_hup[host].uid let s:netrw_passwd = s:netrw_hup[host].passwd -" call Decho("set s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">") -" call Decho("set s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">") +" call Decho("set s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>")) +" call Decho("set s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>")) endif " call Dret("NetUserPass : uid<".g:netrw_uid."> passwd<".s:netrw_passwd.">") @@ -3264,9 +3268,9 @@ endfun fun! s:ExplorePatHls(pattern) " call Dfunc("s:ExplorePatHls(pattern<".a:pattern.">)") let repat= substitute(a:pattern,'^**/\{1,2}','','') -" call Decho("repat<".repat.">") +" call Decho("repat<".repat.">",'~'.expand("<slnum>")) let repat= escape(repat,'][.\') -" call Decho("repat<".repat.">") +" call Decho("repat<".repat.">",'~'.expand("<slnum>")) let repat= '\<'.substitute(repat,'\*','\\(\\S\\+ \\)*\\S\\+','g').'\>' " call Dret("s:ExplorePatHls repat<".repat.">") return repat @@ -3293,7 +3297,7 @@ fun! s:NetrwBookHistHandler(chg,curdir) if a:chg == 0 " bookmark the current directory -" call Decho("(user: <b>) bookmark the current directory") +" call Decho("(user: <b>) bookmark the current directory",'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist_{curbufnr}") call s:NetrwBookmark(0) echo "bookmarked marked files" @@ -3304,9 +3308,9 @@ fun! s:NetrwBookHistHandler(chg,curdir) elseif a:chg == 1 " change to the bookmarked directory -" call Decho("(user: <".v:count."gb>) change to the bookmarked directory") +" call Decho("(user: <".v:count."gb>) change to the bookmarked directory",'~'.expand("<slnum>")) if exists("g:netrw_bookmarklist[v:count-1]") -" call Decho("(user: <".v:count."gb>) bookmarklist=".string(g:netrw_bookmarklist)) +" call Decho("(user: <".v:count."gb>) bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) exe "NetrwKeepj e ".fnameescape(g:netrw_bookmarklist[v:count-1]) else echomsg "Sorry, bookmark#".v:count." doesn't exist!" @@ -3316,12 +3320,12 @@ fun! s:NetrwBookHistHandler(chg,curdir) " redraw! let didwork= 0 " list user's bookmarks -" call Decho("(user: <q>) list user's bookmarks") +" call Decho("(user: <q>) list user's bookmarks",'~'.expand("<slnum>")) if exists("g:netrw_bookmarklist") -" call Decho('list '.len(g:netrw_bookmarklist).' bookmarks') +" call Decho('list '.len(g:netrw_bookmarklist).' bookmarks','~'.expand("<slnum>")) let cnt= 1 for bmd in g:netrw_bookmarklist -" call Decho("Netrw Bookmark#".cnt.": ".g:netrw_bookmarklist[cnt-1]) +" call Decho("Netrw Bookmark#".cnt.": ".g:netrw_bookmarklist[cnt-1],'~'.expand("<slnum>")) echo printf("Netrw Bookmark#%-2d: %s",cnt,g:netrw_bookmarklist[cnt-1]) let didwork = 1 let cnt = cnt + 1 @@ -3334,9 +3338,9 @@ fun! s:NetrwBookHistHandler(chg,curdir) let histcnt = 0 if g:netrw_dirhistmax > 0 while ( first || cnt != g:netrw_dirhist_cnt ) -" call Decho("first=".first." cnt=".cnt." dirhist_cnt=".g:netrw_dirhist_cnt) +" call Decho("first=".first." cnt=".cnt." dirhist_cnt=".g:netrw_dirhist_cnt,'~'.expand("<slnum>")) if exists("g:netrw_dirhist_{cnt}") -" call Decho("Netrw History#".histcnt.": ".g:netrw_dirhist_{cnt}) +" call Decho("Netrw History#".histcnt.": ".g:netrw_dirhist_{cnt},'~'.expand("<slnum>")) echo printf("Netrw History#%-2d: %s",histcnt,g:netrw_dirhist_{cnt}) let didwork= 1 endif @@ -3356,18 +3360,18 @@ fun! s:NetrwBookHistHandler(chg,curdir) elseif a:chg == 3 " saves most recently visited directories (when they differ) -" call Decho("(browsing) record curdir history") +" call Decho("(browsing) record curdir history",'~'.expand("<slnum>")) if !exists("g:netrw_dirhist_cnt") || !exists("g:netrw_dirhist_{g:netrw_dirhist_cnt}") || g:netrw_dirhist_{g:netrw_dirhist_cnt} != a:curdir if g:netrw_dirhistmax > 0 let g:netrw_dirhist_cnt = ( g:netrw_dirhist_cnt + 1 ) % g:netrw_dirhistmax let g:netrw_dirhist_{g:netrw_dirhist_cnt} = a:curdir endif -" call Decho("save dirhist#".g:netrw_dirhist_cnt."<".g:netrw_dirhist_{g:netrw_dirhist_cnt}.">") +" call Decho("save dirhist#".g:netrw_dirhist_cnt."<".g:netrw_dirhist_{g:netrw_dirhist_cnt}.">",'~'.expand("<slnum>")) endif elseif a:chg == 4 " u: change to the previous directory stored on the history list -" call Decho("(user: <u>) chg to prev dir from history") +" call Decho("(user: <u>) chg to prev dir from history",'~'.expand("<slnum>")) if g:netrw_dirhistmax > 0 let g:netrw_dirhist_cnt= ( g:netrw_dirhist_cnt - v:count1 ) % g:netrw_dirhistmax if g:netrw_dirhist_cnt < 0 @@ -3377,16 +3381,16 @@ fun! s:NetrwBookHistHandler(chg,curdir) let g:netrw_dirhist_cnt= 0 endif if exists("g:netrw_dirhist_{g:netrw_dirhist_cnt}") -" call Decho("changedir u#".g:netrw_dirhist_cnt."<".g:netrw_dirhist_{g:netrw_dirhist_cnt}.">") +" call Decho("changedir u#".g:netrw_dirhist_cnt."<".g:netrw_dirhist_{g:netrw_dirhist_cnt}.">",'~'.expand("<slnum>")) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") setl ma noro -" call Decho("setl ma noro") - sil! NetrwKeepj %d +" call Decho("setl ma noro",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ setl nomod -" call Decho("setl nomod") -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("setl nomod",'~'.expand("<slnum>")) +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) endif -" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhist_cnt})) +" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhist_cnt}),'~'.expand("<slnum>")) exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhist_cnt}) else if g:netrw_dirhistmax > 0 @@ -3399,21 +3403,21 @@ fun! s:NetrwBookHistHandler(chg,curdir) elseif a:chg == 5 " U: change to the subsequent directory stored on the history list -" call Decho("(user: <U>) chg to next dir from history") +" call Decho("(user: <U>) chg to next dir from history",'~'.expand("<slnum>")) if g:netrw_dirhistmax > 0 let g:netrw_dirhist_cnt= ( g:netrw_dirhist_cnt + 1 ) % g:netrw_dirhistmax if exists("g:netrw_dirhist_{g:netrw_dirhist_cnt}") -" call Decho("changedir U#".g:netrw_dirhist_cnt."<".g:netrw_dirhist_{g:netrw_dirhist_cnt}.">") +" call Decho("changedir U#".g:netrw_dirhist_cnt."<".g:netrw_dirhist_{g:netrw_dirhist_cnt}.">",'~'.expand("<slnum>")) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") -" call Decho("setl ma noro") +" call Decho("setl ma noro",'~'.expand("<slnum>")) setl ma noro - sil! NetrwKeepj %d -" call Decho("removed all lines from buffer (%d)") -" call Decho("setl nomod") + sil! NetrwKeepj %d _ +" call Decho("removed all lines from buffer (%d)",'~'.expand("<slnum>")) +" call Decho("setl nomod",'~'.expand("<slnum>")) setl nomod -" call Decho("(set nomod) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("(set nomod) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) endif -" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhist_cnt})) +" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhist_cnt}),'~'.expand("<slnum>")) exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhist_cnt}) else let g:netrw_dirhist_cnt= ( g:netrw_dirhist_cnt - 1 ) % g:netrw_dirhistmax @@ -3428,18 +3432,22 @@ fun! s:NetrwBookHistHandler(chg,curdir) endif elseif a:chg == 6 +" call Decho("(user: <mB>) delete bookmark'd directory",'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist_{curbufnr}") call s:NetrwBookmark(1) echo "removed marked files from bookmarks" else " delete the v:count'th bookmark -" call Decho("delete bookmark#".v:count."<".g:netrw_bookmarklist[v:count-1].">") + let iremove = v:count + let dremove = g:netrw_bookmarklist[iremove - 1] +" call Decho("delete bookmark#".iremove."<".g:netrw_bookmarklist[iremove - 1].">",'~'.expand("<slnum>")) call s:MergeBookmarks() -" call Decho("remove g:netrw_bookmarklist[".(v:count-1)."]") - NetrwKeepj call remove(g:netrw_bookmarklist,v:count-1) - echo "removed current directory from bookmarks" +" call Decho("remove g:netrw_bookmarklist[".(iremove-1)."]<".g:netrw_bookmarklist[(iremove-1)].">",'~'.expand("<slnum>")) + NetrwKeepj call remove(g:netrw_bookmarklist,iremove-1) + echo "removed ".dremove." from g:netrw_bookmarklist" +" call Decho("g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) endif -" call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist)) +" call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) endif call s:NetrwBookmarkMenu() call s:NetrwTgtMenu() @@ -3461,14 +3469,14 @@ fun! s:NetrwBookHistRead() if !exists("s:netrw_initbookhist") let home = s:NetrwHome() let savefile= home."/.netrwbook" - if filereadable(savefile) -" call Decho("sourcing .netrwbook") + if filereadable(s:NetrwFile(savefile)) +" call Decho("sourcing .netrwbook",'~'.expand("<slnum>")) exe "keepalt NetrwKeepj so ".savefile endif if g:netrw_dirhistmax > 0 let savefile= home."/.netrwhist" - if filereadable(savefile) -" call Decho("sourcing .netrwhist") + if filereadable(s:NetrwFile(savefile)) +" call Decho("sourcing .netrwhist",'~'.expand("<slnum>")) exe "keepalt NetrwKeepj so ".savefile endif let s:netrw_initbookhist= 1 @@ -3500,7 +3508,7 @@ fun! s:NetrwBookHistSave() setl nocin noai noci magic nospell nohid wig= noaw setl ma noro write if exists("+acd") | setl noacd | endif - sil! NetrwKeepj keepalt %d + sil! NetrwKeepj keepalt %d _ " save .netrwhist -- no attempt to merge sil! keepalt file .netrwhist @@ -3514,12 +3522,12 @@ fun! s:NetrwBookHistSave() endwhile exe "sil! w! ".savefile - sil NetrwKeepj %d + sil NetrwKeepj %d _ if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] " merge and write .netrwbook let savefile= s:NetrwHome()."/.netrwbook" - if filereadable(savefile) + if filereadable(s:NetrwFile(savefile)) let booklist= deepcopy(g:netrw_bookmarklist) exe "sil NetrwKeepj keepalt so ".savefile for bdm in booklist @@ -3546,10 +3554,12 @@ endfun " list of the contents of a local or remote directory. It is assumed that the " g:netrw_list_cmd has a string, USEPORT HOSTNAME, that needs to be substituted " with the requested remote hostname first. +" Often called via: Explore/e dirname/etc -> netrw#LocalBrowseCheck() -> s:NetrwBrowse() fun! s:NetrwBrowse(islocal,dirname) if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif " call Dfunc("s:NetrwBrowse(islocal=".a:islocal." dirname<".a:dirname.">) liststyle=".w:netrw_liststyle." ".g:loaded_netrw." buf#".bufnr("%")."<".bufname("%")."> win#".winnr()) -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")." modified=".&modified." modifiable=".&modifiable." readonly=".&readonly) +" call Decho("modified=".&modified." modifiable=".&modifiable." readonly=".&readonly,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Dredir("ls!") " save alternate-file's filename if w:netrw_rexlocal doesn't exist @@ -3564,7 +3574,7 @@ fun! s:NetrwBrowse(islocal,dirname) endif " s:NetrwBrowse : simplify the dirname (especially for ".."s in dirnames) {{{3 - if a:dirname !~ '^\a\+://' + if a:dirname !~ '^\a\{3,}://' let dirname= simplify(a:dirname) else let dirname= a:dirname @@ -3572,7 +3582,7 @@ fun! s:NetrwBrowse(islocal,dirname) if exists("s:netrw_skipbrowse") unlet s:netrw_skipbrowse -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." filename<".expand("%")."> win#".winnr()." ft<".&ft.">") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." filename<".expand("%")."> win#".winnr()." ft<".&ft.">",'~'.expand("<slnum>")) " call Dret("s:NetrwBrowse : s:netrw_skipbrowse existed") return endif @@ -3590,54 +3600,54 @@ fun! s:NetrwBrowse(islocal,dirname) endif " s:NetrwBrowse : save options: {{{3 - call s:NetrwOptionSave("w:") + call s:NetrwOptionSave("w:") " s:NetrwBrowse : re-instate any marked files {{{3 if exists("s:netrwmarkfilelist_{bufnr('%')}") -" call Decho("clearing marked files") +" call Decho("clearing marked files",'~'.expand("<slnum>")) exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" endif if a:islocal && exists("w:netrw_acdkeep") && w:netrw_acdkeep " s:NetrwBrowse : set up "safe" options for local directory/file {{{3 -" call Decho("handle w:netrw_acdkeep:") -" call Decho("NetrwKeepj lcd ".fnameescape(dirname)." (due to w:netrw_acdkeep=".w:netrw_acdkeep." - acd=".&acd.")") +" call Decho("handle w:netrw_acdkeep:",'~'.expand("<slnum>")) +" call Decho("NetrwKeepj lcd ".fnameescape(dirname)." (due to w:netrw_acdkeep=".w:netrw_acdkeep." - acd=".&acd.")",'~'.expand("<slnum>")) call s:NetrwLcd(dirname) call s:NetrwSafeOptions() -" call Decho("getcwd<".getcwd().">") +" call Decho("getcwd<".getcwd().">",'~'.expand("<slnum>")) elseif !a:islocal && dirname !~ '[\/]$' && dirname !~ '^"' " s:NetrwBrowse : remote regular file handler {{{3 -" call Decho("handle remote regular file: dirname<".dirname.">") +" call Decho("handle remote regular file: dirname<".dirname.">",'~'.expand("<slnum>")) if bufname(dirname) != "" -" call Decho("edit buf#".bufname(dirname)." in win#".winnr()) +" call Decho("edit buf#".bufname(dirname)." in win#".winnr(),'~'.expand("<slnum>")) exe "NetrwKeepj b ".bufname(dirname) else " attempt transfer of remote regular file -" call Decho("attempt transfer as regular file<".dirname.">") +" call Decho("attempt transfer as regular file<".dirname.">",'~'.expand("<slnum>")) " remove any filetype indicator from end of dirname, except for the " "this is a directory" indicator (/). " There shouldn't be one of those here, anyway. let path= substitute(dirname,'[*=@|]\r\=$','','e') -" call Decho("new path<".path.">") +" call Decho("new path<".path.">",'~'.expand("<slnum>")) call s:RemotePathAnalysis(dirname) " s:NetrwBrowse : remote-read the requested file into current buffer {{{3 call s:NetrwEnew(dirname) call s:NetrwSafeOptions() setl ma noro -" call Decho("setl ma noro") +" call Decho("setl ma noro",'~'.expand("<slnum>")) let b:netrw_curdir = dirname let url = s:method."://".((s:user == "")? "" : s:user."@").s:machine.(s:port ? ":".s:port : "")."/".s:path -" call Decho("exe sil! keepalt file ".fnameescape(url)." (bt=".&bt.")") +" call Decho("exe sil! keepalt file ".fnameescape(url)." (bt=".&bt.")",'~'.expand("<slnum>")) exe "sil! NetrwKeepj keepalt file ".fnameescape(url) exe "sil! NetrwKeepj keepalt doau BufReadPre ".fnameescape(s:fname) sil call netrw#NetRead(2,url) " netrw.vim and tar.vim have already handled decompression of the tarball; avoiding gzip.vim error -" call Decho("url<".url.">") -" call Decho("s:path<".s:path.">") -" call Decho("s:fname<".s:fname.">") +" call Decho("url<".url.">",'~'.expand("<slnum>")) +" call Decho("s:path<".s:path.">",'~'.expand("<slnum>")) +" call Decho("s:fname<".s:fname.">",'~'.expand("<slnum>")) if s:path =~ '.bz2' exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.bz2$','','')) elseif s:path =~ '.gz' @@ -3652,9 +3662,9 @@ fun! s:NetrwBrowse(islocal,dirname) " s:NetrwBrowse : save certain window-oriented variables into buffer-oriented variables {{{3 call s:SetBufWinVars() call s:NetrwOptionRestore("w:") -" call Decho("setl ma nomod") +" call Decho("setl ma nomod",'~'.expand("<slnum>")) setl ma nomod noro -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:NetrwBrowse : file<".s:fname.">") return @@ -3672,29 +3682,30 @@ fun! s:NetrwBrowse(islocal,dirname) NetrwKeepj call s:NetrwMenu(1) " get/set-up buffer {{{3 -" call Decho("saving position across a buffer refresh") +" call Decho("saving position across a buffer refresh",'~'.expand("<slnum>")) let svpos = netrw#SavePosn() let reusing= s:NetrwGetBuffer(a:islocal,dirname) + " maintain markfile highlighting if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != "" -" call Decho("bufnr(%)=".bufnr('%')) -" call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/") +" call Decho("bufnr(%)=".bufnr('%'),'~'.expand("<slnum>")) +" call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/",'~'.expand("<slnum>")) exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" else -" call Decho("2match none") +" call Decho("2match none",'~'.expand("<slnum>")) 2match none endif if reusing && line("$") > 1 call s:NetrwOptionRestore("w:") -" call Decho("setl noma nomod nowrap") +" call Decho("setl noma nomod nowrap",'~'.expand("<slnum>")) setl noma nomod nowrap -" call Decho("(set noma nomod nowrap) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("(set noma nomod nowrap) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:NetrwBrowse : re-using not-cleared buffer") return endif " set b:netrw_curdir to the new directory name {{{3 -" call Decho("set b:netrw_curdir to the new directory name<".dirname."> (buf#".bufnr("%").")") +" call Decho("set b:netrw_curdir to the new directory name<".dirname."> (buf#".bufnr("%").")",'~'.expand("<slnum>")) let b:netrw_curdir= dirname if b:netrw_curdir =~ '[/\\]$' let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e') @@ -3715,21 +3726,21 @@ fun! s:NetrwBrowse(islocal,dirname) if !a:islocal && b:netrw_curdir !~ '/$' let b:netrw_curdir= b:netrw_curdir.'/' endif -" call Decho("b:netrw_curdir<".b:netrw_curdir.">") +" call Decho("b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) " ------------ " (local only) {{{3 " ------------ if a:islocal -" call Decho("local only:") +" call Decho("local only:",'~'.expand("<slnum>")) " Set up ShellCmdPost handling. Append current buffer to browselist call s:LocalFastBrowser() " handle g:netrw_keepdir: set vim's current directory to netrw's notion of the current directory {{{3 if !g:netrw_keepdir -" call Decho("handle g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd) -" call Decho("l:acd".(exists("&l:acd")? "=".&l:acd : " doesn't exist")) +" call Decho("handle g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("<slnum>")) +" call Decho("l:acd".(exists("&l:acd")? "=".&l:acd : " doesn't exist"),'~'.expand("<slnum>")) if !exists("&l:acd") || !&l:acd call s:NetrwLcd(b:netrw_curdir) endif @@ -3739,23 +3750,23 @@ fun! s:NetrwBrowse(islocal,dirname) " remote handling: {{{3 " -------------------------------- else -" call Decho("remote only:") +" call Decho("remote only:",'~'.expand("<slnum>")) " analyze dirname and g:netrw_list_cmd {{{3 -" call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist")."> dirname<".dirname.">") +" call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist")."> dirname<".dirname.">",'~'.expand("<slnum>")) if dirname =~ "^NetrwTreeListing\>" let dirname= b:netrw_curdir -" call Decho("(dirname was <NetrwTreeListing>) dirname<".dirname.">") +" call Decho("(dirname was <NetrwTreeListing>) dirname<".dirname.">",'~'.expand("<slnum>")) elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") let dirname= substitute(b:netrw_curdir,'\\','/','g') if dirname !~ '/$' let dirname= dirname.'/' endif let b:netrw_curdir = dirname -" call Decho("(liststyle is TREELIST) dirname<".dirname.">") +" call Decho("(liststyle is TREELIST) dirname<".dirname.">",'~'.expand("<slnum>")) else let dirname = substitute(dirname,'\\','/','g') -" call Decho("(normal) dirname<".dirname.">") +" call Decho("(normal) dirname<".dirname.">",'~'.expand("<slnum>")) endif let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/]\+\)/\(.*\)$' @@ -3764,14 +3775,14 @@ fun! s:NetrwBrowse(islocal,dirname) NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw doesn't understand your dirname<".dirname.">",20) endif NetrwKeepj call s:NetrwOptionRestore("w:") -" call Decho("setl noma nomod nowrap") +" call Decho("setl noma nomod nowrap",'~'.expand("<slnum>")) setl noma nomod nowrap -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:NetrwBrowse : badly formatted dirname<".dirname.">") return endif let b:netrw_curdir= dirname -" call Decho("b:netrw_curdir<".b:netrw_curdir."> (remote)") +" call Decho("b:netrw_curdir<".b:netrw_curdir."> (remote)",'~'.expand("<slnum>")) endif " (additional remote handling) " ----------------------- @@ -3780,29 +3791,82 @@ fun! s:NetrwBrowse(islocal,dirname) NetrwKeepj call s:NetrwMaps(a:islocal) NetrwKeepj call s:NetrwCommands(a:islocal) NetrwKeepj call s:PerformListing(a:islocal) + + " If there is a rexposn: restore position with rexposn + " Otherwise : set rexposn + if exists("s:rexposn_".bufnr("%")) + NetrwKeepj call netrw#RestorePosn(s:rexposn_{bufnr('%')}) + else + NetrwKeepj call s:SetRexDir(a:islocal,b:netrw_curdir) + endif if v:version >= 700 && has("balloon_eval") && &beval == 0 && &l:bexpr == "" && !exists("g:netrw_nobeval") let &l:bexpr= "netrw#BalloonHelp()" -" call Decho("set up balloon help: l:bexpr=".&l:bexpr) +" call Decho("set up balloon help: l:bexpr=".&l:bexpr,'~'.expand("<slnum>")) setl beval endif call s:NetrwOptionRestore("w:") +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) - " restore position and jumplist entry - if !reusing -" call Decho("restoring position across buffer refresh") + " restore position + if reusing +" call Decho("reusing=".reusing.": restoring position across buffer refresh",'~'.expand("<slnum>")) call netrw#RestorePosn(svpos) endif " The s:LocalBrowseRefresh() function is called by an autocmd " installed by s:LocalFastBrowser() when g:netrw_fastbrowse <= 1 (ie. slow, medium speed). " However, s:NetrwBrowse() causes the FocusGained event to fire the firstt time. - -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:NetrwBrowse : did PerformListing ft<".&ft.">") return endfun " --------------------------------------------------------------------- +" s:NetrwFile: because of g:netrw_keepdir, isdirectory(), type(), etc may or {{{2 +" may not apply correctly; ie. netrw's idea of the current directory may +" differ from vim's. This function insures that netrw's idea of the current +" directory is used. +fun! s:NetrwFile(fname) +" call Dfunc("s:NetrwFile(fname<".a:fname.">)") + + if g:netrw_keepdir + " vim's idea of the current directory possibly may differ from netrw's + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + + if !exists("g:netrw_cygwin") && (has("win32") || has("win95") || has("win64") || has("win16")) + if a:fname =~ '^\' || a:fname =~ '^\a:\' + " windows, but full path given + let ret= a:fname +" call Decho("windows+full path: isdirectory(".a:fname.")",'~'.expand("<slnum>")) + else + " windows, relative path given + let ret= s:ComposePath(b:netrw_curdir,a:fname) +" call Decho("windows+rltv path: isdirectory(".a:fname.")",'~'.expand("<slnum>")) + endif + + elseif a:fname =~ '^/' + " not windows, full path given + let ret= a:fname +" call Decho("unix+full path: isdirectory(".a:fname.")",'~'.expand("<slnum>")) + else + " not windows, relative path given + let ret= s:ComposePath(b:netrw_curdir,a:fname) +" call Decho("unix+rltv path: isdirectory(".a:fname.")",'~'.expand("<slnum>")) + endif + else + " vim and netrw agree on the current directory + let ret= a:fname +" call Decho("vim and netrw agree on current directory (g:netrw_keepdir=".g:netrw_keepdir.")",'~'.expand("<slnum>")) + endif + +" call Dret("s:NetrwFile ".ret) + return ret +endfun + +" --------------------------------------------------------------------- " s:NetrwFileInfo: supports qf (query for file information) {{{2 fun! s:NetrwFileInfo(islocal,fname) " call Dfunc("s:NetrwFileInfo(islocal=".a:islocal." fname<".a:fname.">) b:netrw_curdir<".b:netrw_curdir.">") @@ -3811,40 +3875,34 @@ fun! s:NetrwFileInfo(islocal,fname) if (has("unix") || has("macunix")) && executable("/bin/ls") if getline(".") == "../" - echo system("/bin/ls -lsad ".shellescape("..")) -" call Decho("#1: echo system(/bin/ls -lsad ".shellescape(..).")") + echo system("/bin/ls -lsad ".s:ShellEscape("..")) +" call Decho("#1: echo system(/bin/ls -lsad ".s:ShellEscape(..).")",'~'.expand("<slnum>")) elseif w:netrw_liststyle == s:TREELIST && getline(".") !~ '^'.s:treedepthstring - echo system("/bin/ls -lsad ".shellescape(b:netrw_curdir)) -" call Decho("#2: echo system(/bin/ls -lsad ".shellescape(b:netrw_curdir).")") + echo system("/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir)) +" call Decho("#2: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir).")",'~'.expand("<slnum>")) elseif exists("b:netrw_curdir") - if b:netrw_curdir =~ '/$' - echo system("/bin/ls -lsad ".shellescape(b:netrw_curdir.a:fname)) -" call Decho("#3: echo system(/bin/ls -lsad ".shellescape(b:netrw_curdir.a:fname).")") - - else - echo system("/bin/ls -lsad ".shellescape(b:netrw_curdir."/".a:fname)) -" call Decho("#4: echo system(/bin/ls -lsad ".shellescape(b:netrw_curdir."/".a:fname).")") - endif + echo system("/bin/ls -lsad ".s:ShellEscape(s:ComposePath(b:netrw_curdir,a:fname))) +" call Decho("#3: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir.a:fname).")",'~'.expand("<slnum>")) else -" call Decho('using ls '.a:fname." using cwd<".getcwd().">") - echo system("/bin/ls -lsad ".shellescape(a:fname)) -" call Decho("#5: echo system(/bin/ls -lsad ".shellescape(a:fname).")") +" call Decho('using ls '.a:fname." using cwd<".getcwd().">",'~'.expand("<slnum>")) + echo system("/bin/ls -lsad ".s:ShellEscape(s:NetrwFile(a:fname))) +" call Decho("#5: echo system(/bin/ls -lsad ".s:ShellEscape(a:fname).")",'~'.expand("<slnum>")) endif else " use vim functions to return information about file below cursor -" call Decho("using vim functions to query for file info") - if !isdirectory(a:fname) && !filereadable(a:fname) && a:fname =~ '[*@/]' +" call Decho("using vim functions to query for file info",'~'.expand("<slnum>")) + if !isdirectory(s:NetrwFile(a:fname)) && !filereadable(s:NetrwFile(a:fname)) && a:fname =~ '[*@/]' let fname= substitute(a:fname,".$","","") else let fname= a:fname endif - let t = getftime(fname) - let sz = getfsize(fname) - echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(fname)) -" call Decho("fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(fname))) + let t = getftime(s:NetrwFile(fname)) + let sz = getfsize(s:NetrwFile(fname)) + echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(s:NetrwFile(fname))) +" call Decho("fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(fname)),'~'.expand("<slnum>")) endif else echo "sorry, \"qf\" not supported yet for remote files" @@ -3856,45 +3914,46 @@ endfun " --------------------------------------------------------------------- " s:NetrwGetBuffer: {{{2 " returns 0=cleared buffer -" 1=re-used buffer +" 1=re-used buffer (buffer not cleared) fun! s:NetrwGetBuffer(islocal,dirname) " call Dfunc("s:NetrwGetBuffer(islocal=".a:islocal." dirname<".a:dirname.">) liststyle=".g:netrw_liststyle) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) let dirname= a:dirname " re-use buffer if possible {{{3 -" call Decho("--re-use a buffer if possible--") +" call Decho("--re-use a buffer if possible--",'~'.expand("<slnum>")) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST " find NetrwTreeList buffer if there is one -" call Decho("case liststyle=treelist: find NetrwTreeList buffer if there is one") +" call Decho("case liststyle=treelist: find NetrwTreeList buffer if there is one",'~'.expand("<slnum>")) if exists("w:netrw_treebufnr") && w:netrw_treebufnr > 0 -" call Decho(" re-using w:netrw_treebufnr=".w:netrw_treebufnr) +" call Decho(" re-using w:netrw_treebufnr=".w:netrw_treebufnr,'~'.expand("<slnum>")) let eikeep= &ei setl ei=all - exe "sil! noswapfile keepalt b ".w:netrw_treebufnr + exe "sil! keepj noswapfile keepalt b ".w:netrw_treebufnr let &ei= eikeep setl ma - sil! NetrwKeepj %d -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) + sil! NetrwKeepj %d _ +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Dret("s:NetrwGetBuffer 0<buffer cleared> : bufnum#".w:netrw_treebufnr."<NetrwTreeListing>") return 0 endif let bufnum= -1 -" call Decho(" liststyle=TREE but w:netrw_treebufnr doesn't exist") +" call Decho(" liststyle=TREE but w:netrw_treebufnr doesn't exist",'~'.expand("<slnum>")) else " find buffer number of buffer named precisely the same as dirname {{{3 -" call Decho("case listtyle not treelist: find buffer numnber of buffer named precisely the same as dirname--") +" call Decho("case listtyle not treelist: find buffer numnber of buffer named precisely the same as dirname--",'~'.expand("<slnum>")) " call Dredir("(NetrwGetBuffer) ls!","ls!") " get dirname and associated buffer number let bufnum = bufnr(escape(dirname,'\')) -" call Decho(" find buffer<".dirname.">'s number ") -" call Decho(" bufnr(dirname<".escape(dirname,'\').">)=".bufnum) +" call Decho(" find buffer<".dirname.">'s number ",'~'.expand("<slnum>")) +" call Decho(" bufnr(dirname<".escape(dirname,'\').">)=".bufnum,'~'.expand("<slnum>")) if bufnum < 0 && dirname !~ '/$' " try appending a trailing / -" call Decho(" try appending a trailing / to dirname<".dirname.">") +" call Decho(" try appending a trailing / to dirname<".dirname.">",'~'.expand("<slnum>")) let bufnum= bufnr(escape(dirname.'/','\')) if bufnum > 0 let dirname= dirname.'/' @@ -3903,43 +3962,43 @@ fun! s:NetrwGetBuffer(islocal,dirname) if bufnum < 0 && dirname =~ '/$' " try removing a trailing / -" call Decho(" try removing a trailing / from dirname<".dirname.">") +" call Decho(" try removing a trailing / from dirname<".dirname.">",'~'.expand("<slnum>")) let bufnum= bufnr(escape(substitute(dirname,'/$','',''),'\')) if bufnum > 0 let dirname= substitute(dirname,'/$','','') endif endif -" call Decho(" findbuf1: bufnum=bufnr('".dirname."')=".bufnum." bufname(".bufnum.")<".bufname(bufnum)."> (initial)") +" call Decho(" findbuf1: bufnum=bufnr('".dirname."')=".bufnum." bufname(".bufnum.")<".bufname(bufnum)."> (initial)",'~'.expand("<slnum>")) " note: !~ was used just below, but that means using ../ to go back would match (ie. abc/def/ and abc/ matches) if bufnum > 0 && bufname(bufnum) != dirname && bufname(bufnum) != '.' " handle approximate matches -" call Decho(" handling approx match: bufnum#".bufnum.">0 AND bufname<".bufname(bufnum).">!=dirname<".dirname."> AND bufname(".bufnum.")!='.'") +" call Decho(" handling approx match: bufnum#".bufnum.">0 AND bufname<".bufname(bufnum).">!=dirname<".dirname."> AND bufname(".bufnum.")!='.'",'~'.expand("<slnum>")) let ibuf = 1 let buflast = bufnr("$") -" call Decho(" findbuf2: buflast=bufnr($)=".buflast) +" call Decho(" findbuf2: buflast=bufnr($)=".buflast,'~'.expand("<slnum>")) while ibuf <= buflast let bname= substitute(bufname(ibuf),'\\','/','g') let bname= substitute(bname,'.\zs/$','','') -" call Decho(" findbuf3: while [ibuf=",ibuf."]<=[buflast=".buflast."]: dirname<".dirname."> bname=bufname(".ibuf.")<".bname.">") +" call Decho(" findbuf3: while [ibuf=",ibuf."]<=[buflast=".buflast."]: dirname<".dirname."> bname=bufname(".ibuf.")<".bname.">",'~'.expand("<slnum>")) if bname != '' && dirname =~ '/'.bname.'/\=$' && dirname !~ '^/' " bname is not empty " dirname ends with bname, " dirname doesn't start with /, so its not a absolute path -" call Decho(" findbuf3a: passes test 1 : dirname<".dirname.'> =~ /'.bname.'/\=$ && dirname !~ ^/') +" call Decho(" findbuf3a: passes test 1 : dirname<".dirname.'> =~ /'.bname.'/\=$ && dirname !~ ^/','~'.expand("<slnum>")) break endif if bname =~ '^'.dirname.'/\=$' " bname begins with dirname -" call Decho(' findbuf3b: passes test 2 : bname<'.bname.'>=~^'.dirname.'/\=$') +" call Decho(' findbuf3b: passes test 2 : bname<'.bname.'>=~^'.dirname.'/\=$','~'.expand("<slnum>")) break endif if dirname =~ '^'.bname.'/$' -" call Decho(' findbuf3c: passes test 3 : dirname<'.dirname.'>=~^'.bname.'/$') +" call Decho(' findbuf3c: passes test 3 : dirname<'.dirname.'>=~^'.bname.'/$','~'.expand("<slnum>")) break endif if bname != '' && dirname =~ '/'.bname.'$' && bname == bufname("%") && line("$") == 1 -" call Decho(' findbuf3d: passes test 4 : dirname<'.dirname.'>=~ /'.bname.'$') +" call Decho(' findbuf3d: passes test 4 : dirname<'.dirname.'>=~ /'.bname.'$','~'.expand("<slnum>")) break endif let ibuf= ibuf + 1 @@ -3949,89 +4008,93 @@ fun! s:NetrwGetBuffer(islocal,dirname) else let bufnum= ibuf endif -" call Decho(" findbuf4: bufnum=".bufnum." (ibuf=".ibuf." buflast=".buflast.")") +" call Decho(" findbuf4: bufnum=".bufnum." (ibuf=".ibuf." buflast=".buflast.")",'~'.expand("<slnum>")) endif endif " get enew buffer and name it -or- re-use buffer {{{3 -" call Decho(" get enew buffer and name it OR re-use buffer") - sil! NetrwKeepj keepalt mark ' - if bufnum < 0 || !bufexists(bufnum) -" call Decho("--get enew buffer and name it (bufnum#".bufnum."<0 OR bufexists(".bufnum.")=".bufexists(bufnum)."==0)") + if bufnum < 0 || !bufexists(bufnum) " get enew buffer and name it +" call Decho("--get enew buffer and name it (bufnum#".bufnum."<0 OR bufexists(".bufnum.")=".bufexists(bufnum)."==0)",'~'.expand("<slnum>")) call s:NetrwEnew(dirname) -" call Decho(" got enew buffer#".bufnr("%")." (altbuf<".expand("#").">)") +" call Decho(" got enew buffer#".bufnr("%")." (altbuf<".expand("#").">)",'~'.expand("<slnum>")) " name the buffer if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST " Got enew buffer; transform into a NetrwTreeListing -" call Decho("--transform enew buffer#".bufnr("%")." into a NetrwTreeListing --") +" call Decho("--transform enew buffer#".bufnr("%")." into a NetrwTreeListing --",'~'.expand("<slnum>")) if !exists("s:netrw_treelistnum") let s:netrw_treelistnum= 1 else let s:netrw_treelistnum= s:netrw_treelistnum + 1 endif let w:netrw_treebufnr= bufnr("%") -" call Decho(" exe sil! keepalt file NetrwTreeListing ".fnameescape(s:netrw_treelistnum)) +" call Decho(" exe sil! keepalt file NetrwTreeListing ".fnameescape(s:netrw_treelistnum),'~'.expand("<slnum>")) exe 'sil! keepalt file NetrwTreeListing\ '.fnameescape(s:netrw_treelistnum) setl bt=nofile noswf nnoremap <silent> <buffer> [ :sil call <SID>TreeListMove('[')<cr> nnoremap <silent> <buffer> ] :sil call <SID>TreeListMove(']')<cr> nnoremap <silent> <buffer> [[ :sil call <SID>TreeListMove('[')<cr> nnoremap <silent> <buffer> ]] :sil call <SID>TreeListMove(']')<cr> -" call Decho(" tree listing#".s:netrw_treelistnum." bufnr=".w:netrw_treebufnr) +" call Decho(" tree listing#".s:netrw_treelistnum." bufnr=".w:netrw_treebufnr,'~'.expand("<slnum>")) else " let v:errmsg = "" " Decho let escdirname = fnameescape(dirname) -" call Decho(" errmsg<".v:errmsg."> bufnr(escdirname<".escdirname.">)=".bufnr(escdirname)." bufname()<".bufname(bufnr(escdirname)).">") -" call Decho(' exe sil! keepalt file '.escdirname) +" call Decho(" errmsg<".v:errmsg."> bufnr(escdirname<".escdirname.">)=".bufnr(escdirname)." bufname()<".bufname(bufnr(escdirname)).">",'~'.expand("<slnum>")) +" call Decho(' exe sil! keepalt file '.escdirname,'~'.expand("<slnum>")) " let v:errmsg= "" " Decho - exe 'sil! keepalt file '.escdirname -" call Decho(" errmsg<".v:errmsg."> bufnr(".escdirname.")=".bufnr(escdirname)."<".bufname(bufnr(escdirname)).">") + exe 'sil! keepj keepalt file '.escdirname +" call Decho(" errmsg<".v:errmsg."> bufnr(".escdirname.")=".bufnr(escdirname)."<".bufname(bufnr(escdirname)).">",'~'.expand("<slnum>")) endif -" call Decho(" named enew buffer#".bufnr("%")."<".bufname("%").">") +" call Decho(" named enew buffer#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) else " Re-use the buffer -" call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)") +" call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>")) let eikeep= &ei setl ei=all if getline(2) =~ '^" Netrw Directory Listing' -" call Decho(" getline(2)<".getline(2).'> matches "Netrw Directory Listing" : using keepalt b '.bufnum) - exe "sil! noswapfile keepalt b ".bufnum +" call Decho(" getline(2)<".getline(2).'> matches "Netrw Directory Listing" : using keepalt b '.bufnum,'~'.expand("<slnum>")) + exe "sil! NetrwKeepj noswapfile keepalt b ".bufnum else -" call Decho(" getline(2)<".getline(2).'> does not match "Netrw Directory Listing" : using b '.bufnum) - exe "sil! noswapfile keepalt b ".bufnum +" call Decho(" getline(2)<".getline(2).'> does not match "Netrw Directory Listing" : using b '.bufnum,'~'.expand("<slnum>")) + exe "sil! NetrwKeepj noswapfile keepalt b ".bufnum endif +" call Decho(" line($)=".line("$"),'~'.expand("<slnum>")) if bufname("%") == '.' -" call Decho("exe sil! keepalt file ".fnameescape(getcwd())) - exe "sil! keepalt file ".fnameescape(getcwd()) +" call Decho("exe sil! keepalt file ".fnameescape(getcwd()),'~'.expand("<slnum>")) + exe "sil! NetrwKeepj keepalt file ".fnameescape(getcwd()) endif let &ei= eikeep - if line("$") <= 1 + if line("$") <= 1 && getline(1) == "" + " empty buffer NetrwKeepj call s:NetrwListSettings(a:islocal) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Dret("s:NetrwGetBuffer 0<buffer empty> : re-using buffer#".bufnr("%").", but its empty, so refresh it") return 0 elseif g:netrw_fastbrowse == 0 || (a:islocal && g:netrw_fastbrowse == 1) -" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse." a:islocal=".a:islocal.": clear buffer") +" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse." a:islocal=".a:islocal.": clear buffer",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwListSettings(a:islocal) - sil NetrwKeepj %d -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) + sil NetrwKeepj %d _ +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Dret("s:NetrwGetBuffer 0<cleared buffer> : re-using buffer#".bufnr("%").", but refreshing due to g:netrw_fastbrowse=".g:netrw_fastbrowse) return 0 elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST -" call Decho("--re-use tree listing--") -" call Decho(" clear buffer<".expand("%")."> with :%d") - sil NetrwKeepj %d +" call Decho("--re-use tree listing--",'~'.expand("<slnum>")) +" call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + sil NetrwKeepj %d _ NetrwKeepj call s:NetrwListSettings(a:islocal) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Dret("s:NetrwGetBuffer 0<cleared buffer> : re-using buffer#".bufnr("%").", but treelist mode always needs a refresh") return 0 else -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) -" call Dret("s:NetrwGetBuffer 1<buffer not cleared> : buf#".bufnr("%")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) +" call Dret("s:NetrwGetBuffer 1<buffer not cleared>") return 1 endif endif @@ -4041,19 +4104,20 @@ fun! s:NetrwGetBuffer(islocal,dirname) " slow 0 D D Deleting a buffer implies it will not be re-used (slow) " med 1 D H " fast 2 H H -" call Decho("--do netrw settings: make this buffer#".bufnr("%")." not-a-file, modifiable, not line-numbered, etc--") +" call Decho("--do netrw settings: make this buffer#".bufnr("%")." not-a-file, modifiable, not line-numbered, etc--",'~'.expand("<slnum>")) let fname= expand("%") NetrwKeepj call s:NetrwListSettings(a:islocal) -" call Decho("exe sil! keepalt file ".fnameescape(fname)) +" call Decho("exe sil! keepalt file ".fnameescape(fname),'~'.expand("<slnum>")) exe "sil! NetrwKeepj keepalt file ".fnameescape(fname) " delete all lines from buffer {{{3 -" call Decho("--delete all lines from buffer--") -" call Decho(" clear buffer<".expand("%")."> with :%d") - sil! keepalt NetrwKeepj %d +" call Decho("--delete all lines from buffer--",'~'.expand("<slnum>")) +" call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + sil! keepalt NetrwKeepj %d _ -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) -" call Dret("s:NetrwGetBuffer 0<cleared buffer> : tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) +" call Dret("s:NetrwGetBuffer 0<cleared buffer>") return 0 endfun @@ -4077,7 +4141,11 @@ endfun " --------------------------------------------------------------------- " s:NetrwGetWord: it gets the directory/file named under the cursor {{{2 fun! s:NetrwGetWord() -" call Dfunc("s:NetrwGetWord() line#".line(".")." liststyle=".s:ShowStyle()." virtcol=".virtcol(".")) +" call Dfunc("s:NetrwGetWord() liststyle=".s:ShowStyle()." virtcol=".virtcol(".")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) + let keepsol= &l:sol + setl nosol + call s:UseBufWinVars() " insure that w:netrw_liststyle is set up @@ -4087,12 +4155,12 @@ fun! s:NetrwGetWord() else let w:netrw_liststyle= s:THINLIST endif -" "call Decho("w:netrw_liststyle=".w:netrw_liststyle) +" call Decho("w:netrw_liststyle=".w:netrw_liststyle,'~'.expand("<slnum>")) endif if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt " Active Banner support -" "call Decho("active banner handling") +" call Decho("active banner handling",'~'.expand("<slnum>")) NetrwKeepj norm! 0 let dirname= "./" let curline= getline('.') @@ -4120,35 +4188,35 @@ fun! s:NetrwGetWord() endif elseif w:netrw_liststyle == s:THINLIST -" "call Decho("thin column handling") +" call Decho("thin column handling",'~'.expand("<slnum>")) NetrwKeepj norm! 0 let dirname= substitute(getline('.'),'\t -->.*$','','') elseif w:netrw_liststyle == s:LONGLIST -" "call Decho("long column handling") +" call Decho("long column handling",'~'.expand("<slnum>")) NetrwKeepj norm! 0 let dirname= substitute(getline('.'),'^\(\%(\S\+ \)*\S\+\).\{-}$','\1','e') elseif w:netrw_liststyle == s:TREELIST -" "call Decho("treelist handling") +" call Decho("treelist handling",'~'.expand("<slnum>")) let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') let dirname= substitute(dirname,'\t -->.*$','','') else -" "call Decho("obtain word from wide listing") +" call Decho("obtain word from wide listing",'~'.expand("<slnum>")) let dirname= getline('.') if !exists("b:netrw_cpf") let b:netrw_cpf= 0 exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif' call histdel("/",-1) -" "call Decho("computed cpf=".b:netrw_cpf) +" "call Decho("computed cpf=".b:netrw_cpf,'~'.expand("<slnum>")) endif -" "call Decho("buf#".bufnr("%")."<".bufname("%").">") +" call Decho("buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) let filestart = (virtcol(".")/b:netrw_cpf)*b:netrw_cpf -" "call Decho("filestart= ([virtcol=".virtcol(".")."]/[b:netrw_cpf=".b:netrw_cpf."])*b:netrw_cpf=".filestart." bannercnt=".w:netrw_bannercnt) -" "call Decho("1: dirname<".dirname.">") +" call Decho("filestart= ([virtcol=".virtcol(".")."]/[b:netrw_cpf=".b:netrw_cpf."])*b:netrw_cpf=".filestart." bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) +" call Decho("1: dirname<".dirname.">",'~'.expand("<slnum>")) if filestart == 0 NetrwKeepj norm! 0ma else @@ -4165,9 +4233,9 @@ fun! s:NetrwGetWord() endif let dirname = @a let @a = rega -" "call Decho("2: dirname<".dirname.">") +" call Decho("2: dirname<".dirname.">",'~'.expand("<slnum>")) let dirname= substitute(dirname,'\s\+$','','e') -" "call Decho("3: dirname<".dirname.">") +" call Decho("3: dirname<".dirname.">",'~'.expand("<slnum>")) endif " symlinks are indicated by a trailing "@". Remove it before further processing. @@ -4176,6 +4244,8 @@ fun! s:NetrwGetWord() " executables are indicated by a trailing "*". Remove it before further processing. let dirname= substitute(dirname,"\*$","","") + let &l:sol= keepsol + " call Dret("s:NetrwGetWord <".dirname.">") return dirname endfun @@ -4184,17 +4254,17 @@ endfun " s:NetrwListSettings: make standard settings for a netrw listing {{{2 fun! s:NetrwListSettings(islocal) " call Dfunc("s:NetrwListSettings(islocal=".a:islocal.")") -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) let fname= bufname("%") -" " call Decho("(NetrwListSettings) setl bt=nofile nobl ma nonu nowrap noro nornu") +" " call Decho("(NetrwListSettings) setl bt=nofile nobl ma nonu nowrap noro nornu",'~'.expand("<slnum>")) setl bt=nofile nobl ma nonu nowrap noro nornu -" call Decho("(NetrwListSettings) exe sil! keepalt file ".fnameescape(fname)) +" call Decho("(NetrwListSettings) exe sil! keepalt file ".fnameescape(fname),'~'.expand("<slnum>")) exe "sil! keepalt file ".fnameescape(fname) if g:netrw_use_noswf setl noswf endif " call Dredir("ls!") -" call Decho("(NetrwListSettings) exe setl ts=".(g:netrw_maxfilenamelen+1)) +" call Decho("(NetrwListSettings) exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) exe "setl ts=".(g:netrw_maxfilenamelen+1) setl isk+=.,~,- if g:netrw_fastbrowse > a:islocal @@ -4202,7 +4272,7 @@ fun! s:NetrwListSettings(islocal) else setl bh=delete endif -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) " call Dret("s:NetrwListSettings") endfun @@ -4218,27 +4288,27 @@ fun! s:NetrwListStyle(islocal) if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif let svpos = netrw#SavePosn() let w:netrw_liststyle = (w:netrw_liststyle + 1) % s:MAXLIST -" call Decho("fname<".fname.">") -" call Decho("chgd w:netrw_liststyle to ".w:netrw_liststyle) -" call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist").">") +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) +" call Decho("chgd w:netrw_liststyle to ".w:netrw_liststyle,'~'.expand("<slnum>")) +" call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist").">",'~'.expand("<slnum>")) if w:netrw_liststyle == s:THINLIST " use one column listing -" call Decho("use one column list") +" call Decho("use one column list",'~'.expand("<slnum>")) let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') elseif w:netrw_liststyle == s:LONGLIST " use long list -" call Decho("use long list") +" call Decho("use long list",'~'.expand("<slnum>")) let g:netrw_list_cmd = g:netrw_list_cmd." -l" elseif w:netrw_liststyle == s:WIDELIST " give wide list -" call Decho("use wide list") +" call Decho("use wide list",'~'.expand("<slnum>")) let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') elseif w:netrw_liststyle == s:TREELIST -" call Decho("use tree list") +" call Decho("use tree list",'~'.expand("<slnum>")) let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') else @@ -4248,18 +4318,18 @@ fun! s:NetrwListStyle(islocal) let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') endif setl ma noro -" call Decho("setl ma noro") +" call Decho("setl ma noro",'~'.expand("<slnum>")) " clear buffer - this will cause NetrwBrowse/LocalBrowseCheck to do a refresh -" call Decho("clear buffer<".expand("%")."> with :%d") - sil! NetrwKeepj %d +" call Decho("clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ " following prevents tree listing buffer from being marked "modified" -" call Decho("setl nomod") +" call Decho("setl nomod",'~'.expand("<slnum>")) setl nomod -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " refresh the listing -" call Decho("refresh the listing") +" call Decho("refresh the listing",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) NetrwKeepj call s:NetrwCursor() @@ -4287,7 +4357,7 @@ fun! s:NetrwBannerCtrl(islocal) let fname= s:NetrwGetWord() sil NetrwKeepj $ let result= search('\%(^\%(|\+\s\)\=\|\s\{2,}\)\zs'.escape(fname,'.\[]*$^').'\%(\s\{2,}\|$\)','bc') -" call Decho("search result=".result." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'N/A')) +" call Decho("search result=".result." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'N/A'),'~'.expand("<slnum>")) if result <= 0 && exists("w:netrw_bannercnt") exe "NetrwKeepj ".w:netrw_bannercnt endif @@ -4314,9 +4384,9 @@ fun! s:NetrwBookmark(del,...) if exists("s:netrwmarkfilelist_{curbufnr}") " for every filename in the marked list -" call Decho("bookmark every filename in marked list") +" call Decho("bookmark every filename in marked list",'~'.expand("<slnum>")) let svpos = netrw#SavePosn() - let islocal= expand("%") !~ '^\a\+://' + let islocal= expand("%") !~ '^\a\{3,}://' for fname in s:netrwmarkfilelist_{curbufnr} if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif endfor @@ -4331,7 +4401,7 @@ fun! s:NetrwBookmark(del,...) else " bookmark currently open file -" call Decho("bookmark currently open file") +" call Decho("bookmark currently open file",'~'.expand("<slnum>")) let fname= expand("%") if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif endif @@ -4341,18 +4411,22 @@ fun! s:NetrwBookmark(del,...) " attempts to infer if working remote or local " by deciding if the current file begins with an url " Globbing cannot be done remotely. - let islocal= expand("%") !~ '^\a\+://' -" call Decho("bookmark specified file".((a:0>1)? "s" : "")) + let islocal= expand("%") !~ '^\a\{3,}://' +" call Decho("bookmark specified file".((a:0>1)? "s" : ""),'~'.expand("<slnum>")) let i = 1 while i <= a:0 if islocal - let mbfiles= glob(a:{i},0,1) + if v:version == 704 && has("patch656") + let mbfiles= glob(a:{i},0,1,1) + else + let mbfiles= glob(a:{i},0,1) + endif else let mbfiles= [a:{i}] endif -" call Decho("mbfiles".string(mbfiles)) +" call Decho("mbfiles".string(mbfiles),'~'.expand("<slnum>")) for mbfile in mbfiles -" call Decho("mbfile<".mbfile.">") +" call Decho("mbfile<".mbfile.">",'~'.expand("<slnum>")) if a:del|call s:DeleteBookmark(mbfile)|else|call s:MakeBookmark(mbfile)|endif endfor let i= i + 1 @@ -4379,7 +4453,7 @@ fun! s:NetrwBookmarkMenu() " the following test assures that gvim is running, has menus available, and has menus enabled. if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu if exists("g:NetrwTopLvlMenu") -" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)") +" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>")) exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks' exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete' endif @@ -4391,7 +4465,7 @@ fun! s:NetrwBookmarkMenu() if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0 let cnt= 1 for bmd in g:netrw_bookmarklist -" call Decho('sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmark.'.bmd.' :e '.bmd) +" call Decho('sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmark.'.bmd.' :e '.bmd,'~'.expand("<slnum>")) let bmd= escape(bmd,g:netrw_menu_escape) " show bookmarks for goto menu @@ -4414,7 +4488,7 @@ fun! s:NetrwBookmarkMenu() let priority = g:netrw_dirhist_cnt + histcnt if exists("g:netrw_dirhist_{cnt}") let histdir= escape(g:netrw_dirhist_{cnt},g:netrw_menu_escape) -" call Decho('sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir) +" call Decho('sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir,'~'.expand("<slnum>")) exe 'sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir."\<cr>" endif let first = 0 @@ -4436,27 +4510,27 @@ endfun " NetrwBrowseChgDir() edits the file. fun! s:NetrwBrowseChgDir(islocal,newdir,...) " call Dfunc("s:NetrwBrowseChgDir(islocal=".a:islocal."> newdir<".a:newdir.">) a:0=".a:0." curpos<".string(getpos("."))."> b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "").">") -" call Decho("win#".winnr()) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) let ykeep= @@ if !exists("b:netrw_curdir") " Don't try to change-directory: this can happen, for example, when netrw#ErrorMsg has been called " and the current window is the NetrwMessage window. let @@= ykeep -" call Decho("b:netrw_curdir doesn't exist!") -" call Decho("getcwd<".getcwd().">") +" call Decho("b:netrw_curdir doesn't exist!",'~'.expand("<slnum>")) +" call Decho("getcwd<".getcwd().">",'~'.expand("<slnum>")) " call Dredir("ls!") " call Dret("s:NetrwBrowseChgDir") return endif " NetrwBrowseChgDir: save options and initialize {{{3 -" call Decho("saving options") +" call Decho("saving options",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwOptionSave("s:") NetrwKeepj call s:NetrwSafeOptions() let nbcd_curpos = netrw#SavePosn() let s:nbcd_curpos_{bufnr('%')} = nbcd_curpos -" call Decho("setting s:nbcd_curpos_".bufnr('%')." to SavePosn") +" call Decho("setting s:nbcd_curpos_".bufnr('%')." to SavePosn",'~'.expand("<slnum>")) if (has("win32") || has("win95") || has("win64") || has("win16")) let dirname = substitute(b:netrw_curdir,'\\','/','ge') else @@ -4465,110 +4539,108 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) let newdir = a:newdir let dolockout = 0 let dorestore = 1 -" call Decho("dirname<".dirname.">") +" call Decho("dirname<".dirname.">",'~'.expand("<slnum>")) " ignore <cr>s when done in the banner -" call Decho('ignore <cr>s when done in banner (g:netrw_banner='.g:netrw_banner.")") +" call Decho('ignore [return]s when done in banner (g:netrw_banner='.g:netrw_banner.")",'~'.expand("<slnum>")) if g:netrw_banner -" call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." line(.)#".line('.')." line($)#".line("#")) +" call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." line(.)#".line('.')." line($)#".line("#"),'~'.expand("<slnum>")) if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt && line("$") >= w:netrw_bannercnt if getline(".") =~ 'Quick Help' -" call Decho("#1: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("#1: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) let g:netrw_quickhelp= (g:netrw_quickhelp + 1)%len(s:QuickHelp) -" call Decho("#2: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("#2: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) setl ma noro nowrap NetrwKeepj call setline(line('.'),'" Quick Help: <F1>:help '.s:QuickHelp[g:netrw_quickhelp]) setl noma nomod nowrap NetrwKeepj call netrw#RestorePosn(nbcd_curpos) NetrwKeepj call s:NetrwOptionRestore("s:") -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) endif endif " else " Decho -" call Decho("(s:NetrwBrowseChgdir) g:netrw_banner=".g:netrw_banner." (no banner)") +" call Decho("(s:NetrwBrowseChgdir) g:netrw_banner=".g:netrw_banner." (no banner)",'~'.expand("<slnum>")) endif " set up o/s-dependent directory recognition pattern -" call Decho("set up o/s-dependent directory recognition pattern") if has("amiga") let dirpat= '[\/:]$' else let dirpat= '[\/]$' endif -" call Decho("dirname<".dirname."> dirpat<".dirpat.">") +" call Decho("set up o/s-dependent directory recognition pattern: dirname<".dirname."> dirpat<".dirpat.">",'~'.expand("<slnum>")) if dirname !~ dirpat " apparently vim is "recognizing" that it is in a directory and " is removing the trailing "/". Bad idea, so let's put it back. let dirname= dirname.'/' -" call Decho("adjusting dirname<".dirname.">") +" call Decho("adjusting dirname<".dirname.'> (put trailing "/" back)','~'.expand("<slnum>")) endif -" call Decho("newdir<".newdir."> !~ dirpat<".dirpat.">? ".((newdir !~ dirpat)? "yes" : "no")) - if newdir !~ dirpat && !(a:islocal && isdirectory(newdir)) +" " call Decho("[newdir<".newdir."> ".((newdir =~ dirpat)? "=~" : "!~")." dirpat<".dirpat.">] && [islocal=".a:islocal."] && [newdir is ".(isdirectory(s:NetrwFile(newdir))? "" : "not ")."a directory]",'~'.expand("<slnum>")) + if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(s:ComposePath(dirname,newdir)))) " ------------------------------ " NetrwBrowseChgDir: edit a file {{{3 " ------------------------------ -" call Decho('edit-a-file: case "handling a file": newdir<'.newdir.'> !~ dirpat<'.dirpat.">") +" call Decho('edit-a-file: case "handling a file": newdir<'.newdir.'> !~ dirpat<'.dirpat.">",'~'.expand("<slnum>")) " save position for benefit of Rexplore let s:rexposn_{bufnr("%")}= netrw#SavePosn() - -" call Decho("edit-a-file: setting s:rexposn_".bufnr("%")." to SavePosn") -" call Decho("edit-a-file: win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> ft=".&ft) -" call Decho("edit-a-file: w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a')." w:netrw_treedict:".(exists("w:netrw_treedict")? "exists" : 'n/a')." newdir<".newdir.">") +" call Decho("edit-a-file: setting s:rexposn_".bufnr("%")."<".bufname("%")."> to SavePosn",'~'.expand("<slnum>")) +" call Decho("edit-a-file: win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> ft=".&ft,'~'.expand("<slnum>")) +" call Decho("edit-a-file: w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a')." w:netrw_treedict:".(exists("w:netrw_treedict")? "exists" : 'n/a')." newdir<".newdir.">",'~'.expand("<slnum>")) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") && newdir !~ '^\(/\|\a:\)' -" call Decho("edit-a-file: handle tree listing: w:netrw_treedict<".(exists("w:netrw_treedict")? string(w:netrw_treedict) : 'n/a').">") -" call Decho("edit-a-file: newdir<".newdir.">") +" call Decho("edit-a-file: handle tree listing: w:netrw_treedict<".(exists("w:netrw_treedict")? string(w:netrw_treedict) : 'n/a').">",'~'.expand("<slnum>")) +" call Decho("edit-a-file: newdir<".newdir.">",'~'.expand("<slnum>")) let dirname= s:NetrwTreeDir(a:islocal) if dirname =~ '/$' let dirname= dirname.newdir else let dirname= dirname."/".newdir endif -" call Decho("edit-a-file: dirname<".dirname.">") -" call Decho("edit-a-file: tree listing") +" call Decho("edit-a-file: dirname<".dirname.">",'~'.expand("<slnum>")) +" call Decho("edit-a-file: tree listing",'~'.expand("<slnum>")) elseif newdir =~ '^\(/\|\a:\)' let dirname= newdir else let dirname= s:ComposePath(dirname,newdir) endif -" call Decho("edit-a-file: handling a file: dirname<".dirname."> (a:0=".a:0.")") +" call Decho("edit-a-file: handling a file: dirname<".dirname."> (a:0=".a:0.")",'~'.expand("<slnum>")) " this lets netrw#BrowseX avoid the edit if a:0 < 1 -" call Decho("edit-a-file: set up windows for editing<".fnameescape(dirname)."> didsplit=".(exists("s:didsplit")? s:didsplit : "doesn't exist")) +" call Decho("edit-a-file: set up windows for editing<".fnameescape(dirname)."> didsplit=".(exists("s:didsplit")? s:didsplit : "doesn't exist"),'~'.expand("<slnum>")) NetrwKeepj call s:NetrwOptionRestore("s:") if !exists("s:didsplit") -" call Decho("edit-a-file: s:didsplit does not exist; g:netrw_browse_split=".string(g:netrw_browse_split)." win#".winnr()) +" call Decho("edit-a-file: s:didsplit does not exist; g:netrw_browse_split=".string(g:netrw_browse_split)." win#".winnr(),'~'.expand("<slnum>")) if type(g:netrw_browse_split) == 3 " open file in server " Note that g:netrw_browse_split is a List: [servername,tabnr,winnr] -" call Decho("edit-a-file: open file in server") +" call Decho("edit-a-file: open file in server",'~'.expand("<slnum>")) call s:NetrwServerEdit(a:islocal,dirname) " call Dret("s:NetrwBrowseChgDir") return elseif g:netrw_browse_split == 1 " horizontally splitting the window first -" call Decho("edit-a-file: horizontally splitting window prior to edit") +" call Decho("edit-a-file: horizontally splitting window prior to edit",'~'.expand("<slnum>")) keepalt new if !&ea keepalt wincmd _ endif elseif g:netrw_browse_split == 2 " vertically splitting the window first -" call Decho("edit-a-file: vertically splitting window prior to edit") +" call Decho("edit-a-file: vertically splitting window prior to edit",'~'.expand("<slnum>")) keepalt rightb vert new if !&ea keepalt wincmd | endif elseif g:netrw_browse_split == 3 " open file in new tab -" call Decho("edit-a-file: opening new tab prior to edit") +" call Decho("edit-a-file: opening new tab prior to edit",'~'.expand("<slnum>")) keepalt tabnew elseif g:netrw_browse_split == 4 " act like "P" (ie. open previous window) -" call Decho("edit-a-file: use previous window for edit") +" call Decho("edit-a-file: use previous window for edit",'~'.expand("<slnum>")) if s:NetrwPrevWinOpen(2) == 3 let @@= ykeep " call Dret("s:NetrwBrowseChgDir") @@ -4576,16 +4648,16 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) endif else " handling a file, didn't split, so remove menu -" call Decho("edit-a-file: handling a file+didn't split, so remove menu") +" call Decho("edit-a-file: handling a file+didn't split, so remove menu",'~'.expand("<slnum>")) call s:NetrwMenu(0) " optional change to window if g:netrw_chgwin >= 1 -" call Decho("edit-a-file: changing window to #".g:netrw_chgwin) +" call Decho("edit-a-file: changing window to #".g:netrw_chgwin,'~'.expand("<slnum>")) if winnr("$")+1 == g:netrw_chgwin " if g:netrw_chgwin is set to one more than the last window, then " vertically split the last window to make that window available. let curwin= winnr() - exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd ".winnr("$") + exe "NetrwKeepj keepalt ".winnr("$")."wincmd w" vs exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd ".curwin endif @@ -4598,7 +4670,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " if its local only: LocalBrowseCheck() doesn't edit a file, but NetrwBrowse() will " no keepalt to support :e # to return to a directory listing if a:islocal -" call Decho("edit-a-file: edit local file: exe e! ".fnameescape(dirname)) +" call Decho("edit-a-file: edit local file: exe e! ".fnameescape(dirname),'~'.expand("<slnum>")) " some like c-^ to return to the last edited file " others like c-^ to return to the netrw buffer if exists("g:netrw_altfile") && g:netrw_altfile @@ -4606,14 +4678,14 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) else exe "NetrwKeepj e! ".fnameescape(dirname) endif -" call Decho("edit-a-file: after e! ".dirname.": hidden=".&hidden." bufhidden<".&bufhidden."> mod=".&mod) +" call Decho("edit-a-file: after e! ".dirname.": hidden=".&hidden." bufhidden<".&bufhidden."> mod=".&mod,'~'.expand("<slnum>")) call s:NetrwCursor() if &hidden || &bufhidden == "hide" " file came from vim's hidden storage. Don't "restore" options with it. let dorestore= 0 endif else -" call Decho("edit-a-file: remote file: NetrwBrowse will edit it") +" call Decho("edit-a-file: remote file: NetrwBrowse will edit it",'~'.expand("<slnum>")) endif let dolockout= 1 @@ -4622,12 +4694,12 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " or as a list of function references. It will ignore anything that's not " a function reference. See :help Funcref for information about function references. if exists("g:Netrw_funcref") -" call Decho("edit-a-file: handle optional Funcrefs") +" call Decho("edit-a-file: handle optional Funcrefs",'~'.expand("<slnum>")) if type(g:Netrw_funcref) == 2 -" call Decho("edit-a-file: handling a g:Netrw_funcref") +" call Decho("edit-a-file: handling a g:Netrw_funcref",'~'.expand("<slnum>")) NetrwKeepj call g:Netrw_funcref() elseif type(g:Netrw_funcref) == 3 -" call Decho("edit-a-file: handling a list of g:Netrw_funcrefs") +" call Decho("edit-a-file: handling a list of g:Netrw_funcrefs",'~'.expand("<slnum>")) for Fncref in g:Netrw_funcref if type(FncRef) == 2 NetrwKeepj call FncRef() @@ -4641,42 +4713,44 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " ---------------------------------------------------- " NetrwBrowseChgDir: just go to the new directory spec {{{3 " ---------------------------------------------------- -" call Decho('goto-newdir: case "just go to new directory spec": newdir<'.newdir.'>') +" call Decho('goto-newdir: case "just go to new directory spec": newdir<'.newdir.'>','~'.expand("<slnum>")) let dirname = newdir NetrwKeepj call s:SetRexDir(a:islocal,dirname) NetrwKeepj call s:NetrwOptionRestore("s:") + norm! m` elseif newdir == './' " --------------------------------------------- " NetrwBrowseChgDir: refresh the directory list {{{3 " --------------------------------------------- -" call Decho('refresh-dirlist: case "refresh directory listing": newdir == "./"') +" call Decho('refresh-dirlist: case "refresh directory listing": newdir == "./"','~'.expand("<slnum>")) NetrwKeepj call s:SetRexDir(a:islocal,dirname) + norm! m` elseif newdir == '../' " -------------------------------------- " NetrwBrowseChgDir: go up one directory {{{3 " -------------------------------------- -" call Decho('go-up: case "go up one directory": newdir == "../"') +" call Decho('go-up: case "go up one directory": newdir == "../"','~'.expand("<slnum>")) if w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") " force a refresh -" call Decho("go-up: clear buffer<".expand("%")."> with :%d") -" call Decho("go-up: setl noro ma") +" call Decho("go-up: clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) +" call Decho("go-up: setl noro ma",'~'.expand("<slnum>")) setl noro ma - NetrwKeepj %d + NetrwKeepj %d _ endif if has("amiga") " amiga -" call Decho('go-up: case "go up one directory": newdir == "../" and amiga') +" call Decho('go-up: case "go up one directory": newdir == "../" and amiga','~'.expand("<slnum>")) if a:islocal let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+$\)','\1','') let dirname= substitute(dirname,'/$','','') else let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+/$\)','\1','') endif -" call Decho("go-up: amiga: dirname<".dirname."> (go up one dir)") +" call Decho("go-up: amiga: dirname<".dirname."> (go up one dir)",'~'.expand("<slnum>")) elseif !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) " windows @@ -4686,94 +4760,96 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) let dirname= '/' endif else - let dirname= substitute(dirname,'^\(\a\+://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') + let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') endif if dirname =~ '^\a:$' let dirname= dirname.'/' endif -" call Decho("go-up: windows: dirname<".dirname."> (go up one dir)") +" call Decho("go-up: windows: dirname<".dirname."> (go up one dir)",'~'.expand("<slnum>")) else " unix or cygwin -" call Decho('go-up: case "go up one directory": newdir == "../" and unix or cygwin') +" call Decho('go-up: case "go up one directory": newdir == "../" and unix or cygwin','~'.expand("<slnum>")) if a:islocal let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','') if dirname == "" let dirname= '/' endif else - let dirname= substitute(dirname,'^\(\a\+://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') + let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') endif -" call Decho("go-up: unix: dirname<".dirname."> (go up one dir)") +" call Decho("go-up: unix: dirname<".dirname."> (go up one dir)",'~'.expand("<slnum>")) endif NetrwKeepj call s:SetRexDir(a:islocal,dirname) + norm m` elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") " -------------------------------------- " NetrwBrowseChgDir: Handle Tree Listing {{{3 " -------------------------------------- -" call Decho('tree-list: case liststyle is TREELIST and w:netrw_treedict exists') - " force a refresh (for TREELIST, wait for NetrwTreeDir() to force the refresh) -" call Decho("tree-list: setl noro ma") +" call Decho('tree-list: case liststyle is TREELIST and w:netrw_treedict exists','~'.expand("<slnum>")) + " force a refresh (for TREELIST, NetrwTreeDir() will force the refresh) +" call Decho("tree-list: setl noro ma",'~'.expand("<slnum>")) setl noro ma if !(exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")) -" call Decho("tree-list: clear buffer<".expand("%")."> with :%d") - NetrwKeepj %d +" call Decho("tree-list: clear buffer<".expand("%")."> with :%d (force refresh)",'~'.expand("<slnum>")) + NetrwKeepj %d _ endif let treedir = s:NetrwTreeDir(a:islocal) +" call Decho("tree-list: treedir<".treedir.">",'~'.expand("<slnum>")) let s:treecurpos = nbcd_curpos let haskey = 0 -" call Decho("tree-list: w:netrw_treedict<".string(w:netrw_treedict).">") +" call Decho("tree-list: w:netrw_treedict<".string(w:netrw_treedict).">",'~'.expand("<slnum>")) " search treedict for tree dir as-is -" call Decho("search treedict for tree dir as-is") +" call Decho("search treedict for tree dir as-is",'~'.expand("<slnum>")) if has_key(w:netrw_treedict,treedir) -" call Decho('tree-list: ....searched for treedir<'.treedir.'> : found it!') +" call Decho('tree-list: ....searched for treedir<'.treedir.'> : found it!','~'.expand("<slnum>")) let haskey= 1 else -" call Decho('tree-list: ....searched for treedir<'.treedir.'> : not found') +" call Decho('tree-list: ....searched for treedir<'.treedir.'> : not found','~'.expand("<slnum>")) endif - " search treedict for treedir with a / appended -" call Decho("search treedict for treedir with a / appended") - if !haskey && treedir !~ '/$' + " search treedict for treedir with a [/@] appended +" call Decho("search treedict for treedir with a [/@] appended",'~'.expand("<slnum>")) + if !haskey && treedir !~ '[/@]$' if has_key(w:netrw_treedict,treedir."/") let treedir= treedir."/" -" call Decho('tree-list: ....searched.for treedir<'.treedir.'> found it!') +" call Decho('tree-list: ....searched.for treedir<'.treedir.'> found it!','~'.expand("<slnum>")) let haskey = 1 else -" call Decho('tree-list: ....searched for treedir<'.treedir.'/> : not found') +" call Decho('tree-list: ....searched for treedir<'.treedir.'/> : not found','~'.expand("<slnum>")) endif endif " search treedict for treedir with any trailing / elided -" call Decho("search treedict for treedir with any trailing / elided") +" call Decho("search treedict for treedir with any trailing / elided",'~'.expand("<slnum>")) if !haskey && treedir =~ '/$' let treedir= substitute(treedir,'/$','','') if has_key(w:netrw_treedict,treedir) -" call Decho('tree-list: ....searched.for treedir<'.treedir.'> found it!') +" call Decho('tree-list: ....searched.for treedir<'.treedir.'> found it!','~'.expand("<slnum>")) let haskey = 1 else -" call Decho('tree-list: ....searched for treedir<'.treedir.'> : not found') +" call Decho('tree-list: ....searched for treedir<'.treedir.'> : not found','~'.expand("<slnum>")) endif endif -" call Decho("haskey=".haskey) +" call Decho("haskey=".haskey,'~'.expand("<slnum>")) if haskey " close tree listing for selected subdirectory -" call Decho("tree-list: closing selected subdirectory<".dirname.">") +" call Decho("tree-list: closing selected subdirectory<".dirname.">",'~'.expand("<slnum>")) call remove(w:netrw_treedict,treedir) -" call Decho("tree-list: removed entry<".treedir."> from treedict") -" call Decho("tree-list: yielding treedict<".string(w:netrw_treedict).">") +" call Decho("tree-list: removed entry<".treedir."> from treedict",'~'.expand("<slnum>")) +" call Decho("tree-list: yielding treedict<".string(w:netrw_treedict).">",'~'.expand("<slnum>")) let dirname= w:netrw_treetop else " go down one directory let dirname= substitute(treedir,'/*$','/','') -" call Decho("tree-list: go down one dir: treedir<".treedir.">") -" call Decho("tree-list: ... : dirname<".dirname.">") +" call Decho("tree-list: go down one dir: treedir<".treedir.">",'~'.expand("<slnum>")) +" call Decho("tree-list: ... : dirname<".dirname.">",'~'.expand("<slnum>")) endif NetrwKeepj call s:SetRexDir(a:islocal,dirname) -" call Decho("setting s:treeforceredraw to true") +" call Decho("setting s:treeforceredraw to true",'~'.expand("<slnum>")) let s:treeforceredraw = 1 else @@ -4781,8 +4857,9 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " NetrwBrowseChgDir: Go down one directory {{{3 " ---------------------------------------- let dirname = s:ComposePath(dirname,newdir) -" call Decho("go down one dir: dirname<".dirname."> newdir<".newdir.">") +" call Decho("go down one dir: dirname<".dirname."> newdir<".newdir.">",'~'.expand("<slnum>")) NetrwKeepj call s:SetRexDir(a:islocal,dirname) + norm m` endif " -------------------------------------- @@ -4791,23 +4868,23 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) if dorestore " dorestore is zero'd when a local file was hidden or bufhidden; " in such a case, we want to keep whatever settings it may have. -" call Decho("doing option restore (dorestore=".dorestore.")") +" call Decho("doing option restore (dorestore=".dorestore.")",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwOptionRestore("s:") " else " Decho -" call Decho("skipping option restore (dorestore==0): hidden=".&hidden." bufhidden=".&bufhidden." mod=".&mod) +" call Decho("skipping option restore (dorestore==0): hidden=".&hidden." bufhidden=".&bufhidden." mod=".&mod,'~'.expand("<slnum>")) endif if dolockout && dorestore -" call Decho("restore: filewritable(dirname<".dirname.">)=".filewritable(dirname)) +" call Decho("restore: filewritable(dirname<".dirname.">)=".filewritable(dirname),'~'.expand("<slnum>")) if filewritable(dirname) -" call Decho("restore: doing modification lockout settings: ma nomod noro") -" call Decho("restore: setl ma nomod noro") +" call Decho("restore: doing modification lockout settings: ma nomod noro",'~'.expand("<slnum>")) +" call Decho("restore: setl ma nomod noro",'~'.expand("<slnum>")) setl ma noro nomod -" call Decho("restore: ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("restore: ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) else -" call Decho("restore: doing modification lockout settings: ma nomod ro") -" call Decho("restore: setl ma nomod noro") +" call Decho("restore: doing modification lockout settings: ma nomod ro",'~'.expand("<slnum>")) +" call Decho("restore: setl ma nomod noro",'~'.expand("<slnum>")) setl ma ro nomod -" call Decho("restore: ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("restore: ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) endif endif let @@= ykeep @@ -4825,7 +4902,7 @@ fun! s:NetrwBrowseUpDir(islocal) if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt-1 " this test needed because occasionally this function seems to be incorrectly called " when multiple leftmouse clicks are taken when atop the one line help in the banner. - " I'm allowing the very bottom line to permit a "-" exit so that one may escape empty + " I'm allowing the very bottom line to permit a "-" exit so that one may escape empty " directories. " call Dret("s:NetrwBrowseUpDir : cursor not in file area") return @@ -4833,12 +4910,13 @@ fun! s:NetrwBrowseUpDir(islocal) norm! 0 if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") -" call Decho("case: treestyle") +" call Decho("case: treestyle",'~'.expand("<slnum>")) let curline= getline(".") let swwline= winline() - 1 if exists("w:netrw_treetop") let b:netrw_curdir= w:netrw_treetop endif + let curdir= b:netrw_curdir if a:islocal call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../')) else @@ -4858,19 +4936,26 @@ fun! s:NetrwBrowseUpDir(islocal) endif endwhile else -" call Decho("case: not treestyle") +" call Decho("case: not treestyle",'~'.expand("<slnum>")) + if exists("b:netrw_curdir") + let curdir= b:netrw_curdir + else + let curdir= expand(getcwd()) + endif if a:islocal call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../')) else call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../')) endif if exists("w:netrw_bannercnt") -" call Decho("moving to line#".w:netrw_bannercnt) +" call Decho("moving to line#".w:netrw_bannercnt,'~'.expand("<slnum>")) exe w:netrw_bannercnt else 1 endif endif + let curdir= substitute(curdir,'^.*[\/]','','') + call search('\<'.curdir.'\>','wc') " call Dret("s:NetrwBrowseUpDir") endfun @@ -4900,11 +4985,11 @@ fun! netrw#BrowseX(fname,remote) if exists("g:Netrw_corehandler") if type(g:Netrw_corehandler) == 2 " g:Netrw_corehandler is a function reference (see :help Funcref) -" call Decho("g:Netrw_corehandler is a funcref") - call g:Netrw_corehandler(a:fname) +" call Decho("g:Netrw_corehandler is a funcref",'~'.expand("<slnum>")) + call g:Netrw_corehandler(s:NetrwFile(a:fname)) elseif type(g:Netrw_corehandler) == 3 " g:Netrw_corehandler is a List of function references (see :help Funcref) -" call Decho("g:Netrw_corehandler is a List") +" call Decho("g:Netrw_corehandler is a List",'~'.expand("<slnum>")) for Fncref in g:Netrw_corehandler if type(FncRef) == 2 call FncRef(a:fname) @@ -4925,18 +5010,18 @@ fun! netrw#BrowseX(fname,remote) if has("win32") || has("win95") || has("win64") || has("win16") let exten= substitute(exten,'^.*$','\L&\E','') endif -" call Decho("exten<".exten.">") +" call Decho("exten<".exten.">",'~'.expand("<slnum>")) if a:remote == 1 " create a local copy -" call Decho("remote: a:remote=".a:remote.": create a local copy of <".a:fname.">") +" call Decho("remote: a:remote=".a:remote.": create a local copy of <".a:fname.">",'~'.expand("<slnum>")) setl bh=delete call netrw#NetRead(3,a:fname) " attempt to rename tempfile let basename= substitute(a:fname,'^\(.*\)/\(.*\)\.\([^.]*\)$','\2','') let newname = substitute(s:netrw_tmpfile,'^\(.*\)/\(.*\)\.\([^.]*\)$','\1/'.basename.'.\3','') -" call Decho("basename<".basename.">") -" call Decho("newname <".newname.">") +" call Decho("basename<".basename.">",'~'.expand("<slnum>")) +" call Decho("newname <".newname.">",'~'.expand("<slnum>")) if rename(s:netrw_tmpfile,newname) == 0 " renaming succeeded let fname= newname @@ -4945,16 +5030,16 @@ fun! netrw#BrowseX(fname,remote) let fname= s:netrw_tmpfile endif else -" call Decho("local: a:remote=".a:remote.": handling local copy of <".a:fname.">") +" call Decho("local: a:remote=".a:remote.": handling local copy of <".a:fname.">",'~'.expand("<slnum>")) let fname= a:fname " special ~ handler for local if fname =~ '^\~' && expand("$HOME") != "" -" call Decho('invoking special ~ handler') - let fname= substitute(fname,'^\~',expand("$HOME"),'') +" call Decho('invoking special ~ handler','~'.expand("<slnum>")) + let fname= s:NetrwFile(substitute(fname,'^\~',expand("$HOME"),'')) endif endif -" call Decho("fname<".fname.">") -" call Decho("exten<".exten."> "."netrwFileHandlers#NFH_".exten."():exists=".exists("*netrwFileHandlers#NFH_".exten)) +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) +" call Decho("exten<".exten."> "."netrwFileHandlers#NFH_".exten."():exists=".exists("*netrwFileHandlers#NFH_".exten),'~'.expand("<slnum>")) " set up redirection if &srr =~ "%s" @@ -4968,12 +5053,12 @@ fun! netrw#BrowseX(fname,remote) else let redir= &srr . "/dev/null" endif -" call Decho("set up redirection: redir{".redir."} srr{".&srr."}") +" call Decho("set up redirection: redir{".redir."} srr{".&srr."}",'~'.expand("<slnum>")) " extract any viewing options. Assumes that they're set apart by quotes. -" call Decho("extract any viewing options") +" call Decho("extract any viewing options",'~'.expand("<slnum>")) if exists("g:netrw_browsex_viewer") -" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">") +" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) if g:netrw_browsex_viewer =~ '\s' let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','') let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." " @@ -4984,32 +5069,32 @@ fun! netrw#BrowseX(fname,remote) let viewopt = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\3','')." " let cnt = cnt + 1 let oviewer = viewer -" call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">") +" call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>")) endwhile else let viewer = g:netrw_browsex_viewer let viewopt = "" endif -" call Decho("viewer<".viewer."> viewopt<".viewopt.">") +" call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>")) endif " execute the file handler -" call Decho("execute the file handler (if any)") +" call Decho("execute the file handler (if any)",'~'.expand("<slnum>")) if exists("g:netrw_browsex_viewer") && g:netrw_browsex_viewer == '-' -" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">") +" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) let ret= netrwFileHandlers#Invoke(exten,fname) elseif exists("g:netrw_browsex_viewer") && executable(viewer) -" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">") - call s:NetrwExe("sil !".viewer." ".viewopt.shellescape(fname,1).redir) +" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) + call s:NetrwExe("sil !".viewer." ".viewopt.s:ShellEscape(fname,1).redir) let ret= v:shell_error elseif has("win32") || has("win64") -" call Decho("windows") +" call Decho("windows",'~'.expand("<slnum>")) if executable("start") - call s:NetrwExe('sil! !start rundll32 url.dll,FileProtocolHandler '.shellescape(fname,1)) + call s:NetrwExe('sil! !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1)) elseif executable("rundll32") - call s:NetrwExe('sil! !rundll32 url.dll,FileProtocolHandler '.shellescape(fname,1)) + call s:NetrwExe('sil! !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1)) else call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74) endif @@ -5018,32 +5103,37 @@ fun! netrw#BrowseX(fname,remote) elseif has("win32unix") let winfname= 'c:\cygwin'.substitute(fname,'/','\\','g') -" call Decho("cygwin: winfname<".shellescape(winfname,1).">") +" call Decho("cygwin: winfname<".s:ShellEscape(winfname,1).">",'~'.expand("<slnum>")) if executable("start") - call s:NetrwExe('sil !start rundll32 url.dll,FileProtocolHandler '.shellescape(winfname,1)) + call s:NetrwExe('sil !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1)) elseif executable("rundll32") - call s:NetrwExe('sil !rundll32 url.dll,FileProtocolHandler '.shellescape(winfname,1)) + call s:NetrwExe('sil !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1)) elseif executable("cygstart") - call s:NetrwExe('sil !cygstart '.shellescape(fname,1)) + call s:NetrwExe('sil !cygstart '.s:ShellEscape(fname,1)) else call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74) endif call inputsave()|call input("Press <cr> to continue")|call inputrestore() let ret= v:shell_error - elseif has("unix") && executable("xdg-open") && !s:CheckIfKde() -" call Decho("unix and xdg-open") - call s:NetrwExe("sil !xdg-open ".shellescape(fname,1).redir) + elseif has("unix") && executable("kfmclient") && s:CheckIfKde() +" call Decho("unix and kfmclient",'~'.expand("<slnum>")) + call s:NetrwExe("sil !kfmclient exec ".s:ShellEscape(fname,1)." ".redir) let ret= v:shell_error - elseif has("unix") && executable("kfmclient") && s:CheckIfKde() -" call Decho("unix and kfmclient") - call s:NetrwExe("sil !kfmclient exec ".shellescape(fname,1)." ".redir) + elseif has("unix") && executable("exo-open") && executable("xdg-open") && executable("setsid") +" call Decho("unix, exo-open, xdg-open",'~'.expand("<slnum>")) + call s:NetrwExe("sil !setsid xdg-open ".s:ShellEscape(fname,1).redir) + let ret= v:shell_error + + elseif has("unix") && executable("xdg-open") +" call Decho("unix and xdg-open",'~'.expand("<slnum>")) + call s:NetrwExe("sil !xdg-open ".s:ShellEscape(fname,1).redir) let ret= v:shell_error elseif has("macunix") && executable("open") -" call Decho("macunix and open") - call s:NetrwExe("sil !open ".shellescape(fname,1)." ".redir) +" call Decho("macunix and open",'~'.expand("<slnum>")) + call s:NetrwExe("sil !open ".s:ShellEscape(fname,1)." ".redir) let ret= v:shell_error else @@ -5065,7 +5155,7 @@ fun! netrw#BrowseX(fname,remote) " Feb 12, 2008: had to de-activiate removal of " temporary file because it wasn't getting seen. " if a:remote == 1 && fname != a:fname -"" call Decho("deleting temporary file<".fname.">") +"" call Decho("deleting temporary file<".fname.">",'~'.expand("<slnum>")) " call s:NetrwDelete(fname) " endif @@ -5090,7 +5180,7 @@ fun! netrw#BrowseXVis() " call Dfunc("netrw#BrowseXVis()") let atkeep = @@ norm! gvy -" call Decho("@@<".@@.">") +" call Decho("@@<".@@.">",'~'.expand("<slnum>")) call netrw#BrowseX(@@,netrw#CheckIfRemote()) let @@ = atkeep " call Dret("netrw#BrowseXVis") @@ -5100,7 +5190,7 @@ endfun " netrw#CheckIfRemote: returns 1 if current file looks like an url, 0 else {{{2 fun! netrw#CheckIfRemote() " call Dfunc("netrw#CheckIfRemote()") - if expand("%") =~ '^\a\+://' + if expand("%") =~ '^\a\{3,}://' " call Dret("netrw#CheckIfRemote 1") return 1 else @@ -5117,9 +5207,9 @@ fun! s:NetrwChgPerm(islocal,curdir) call inputsave() let newperm= input("Enter new permission: ") call inputrestore() - let chgperm= substitute(g:netrw_chgperm,'\<FILENAME\>',shellescape(expand("<cfile>")),'') - let chgperm= substitute(chgperm,'\<PERM\>',shellescape(newperm),'') -" call Decho("chgperm<".chgperm.">") + let chgperm= substitute(g:netrw_chgperm,'\<FILENAME\>',s:ShellEscape(expand("<cfile>")),'') + let chgperm= substitute(chgperm,'\<PERM\>',s:ShellEscape(newperm),'') +" call Decho("chgperm<".chgperm.">",'~'.expand("<slnum>")) call system(chgperm) if v:shell_error != 0 NetrwKeepj call netrw#ErrorMsg(1,"changing permission on file<".expand("<cfile>")."> seems to have failed",75) @@ -5142,14 +5232,14 @@ fun! s:CheckIfKde() " usually have "kdeinit" running, though... (tnx Mikolaj Machowski) if !exists("s:haskdeinit") if has("unix") && executable("ps") && !has("win32unix") - let s:haskdeinit= system("ps -e") =~ '\<kdeinit' + let s:haskdeinit= system("ps -e") =~ '\<kdeinit' if v:shell_error let s:haskdeinit = 0 endif else let s:haskdeinit= 0 endif -" call Decho("setting s:haskdeinit=".s:haskdeinit) +" call Decho("setting s:haskdeinit=".s:haskdeinit,'~'.expand("<slnum>")) endif " call Dret("s:CheckIfKde ".s:haskdeinit) @@ -5208,7 +5298,7 @@ fun! s:NetrwForceChgDir(islocal,newdir) else let newdir= a:newdir.'/' endif -" call Decho("adjusting newdir<".newdir."> due to gd") +" call Decho("adjusting newdir<".newdir."> due to gd",'~'.expand("<slnum>")) else " should already be getting treatment as a directory let newdir= a:newdir @@ -5249,18 +5339,18 @@ fun! s:NetrwHide(islocal) let svpos= netrw#SavePosn() if exists("s:netrwmarkfilelist_{bufnr('%')}") -" call Decho("((g:netrw_hide == 1)? "unhide" : "hide")." files in markfilelist<".string(s:netrwmarkfilelist_{bufnr("%")}).">") -" call Decho("g:netrw_list_hide<".g:netrw_list_hide.">") +" call Decho("((g:netrw_hide == 1)? "unhide" : "hide")." files in markfilelist<".string(s:netrwmarkfilelist_{bufnr("%")}).">",'~'.expand("<slnum>")) +" call Decho("g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>")) " hide the files in the markfile list for fname in s:netrwmarkfilelist_{bufnr("%")} -" call Decho("match(g:netrw_list_hide<".g:netrw_list_hide.'> fname<\<'.fname.'\>>)='.match(g:netrw_list_hide,'\<'.fname.'\>')." l:isk=".&l:isk) +" call Decho("match(g:netrw_list_hide<".g:netrw_list_hide.'> fname<\<'.fname.'\>>)='.match(g:netrw_list_hide,'\<'.fname.'\>')." l:isk=".&l:isk,'~'.expand("<slnum>")) if match(g:netrw_list_hide,'\<'.fname.'\>') != -1 " remove fname from hiding list let g:netrw_list_hide= substitute(g:netrw_list_hide,'..\<'.escape(fname,g:netrw_fname_escape).'\>..','','') let g:netrw_list_hide= substitute(g:netrw_list_hide,',,',',','g') let g:netrw_list_hide= substitute(g:netrw_list_hide,'^,\|,$','','') -" call Decho("unhide: g:netrw_list_hide<".g:netrw_list_hide.">") +" call Decho("unhide: g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>")) else " append fname to hiding list if exists("g:netrw_list_hide") && g:netrw_list_hide != "" @@ -5268,7 +5358,7 @@ fun! s:NetrwHide(islocal) else let g:netrw_list_hide= '\<'.escape(fname,g:netrw_fname_escape).'\>' endif -" call Decho("hide: g:netrw_list_hide<".g:netrw_list_hide.">") +" call Decho("hide: g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>")) endif endfor NetrwKeepj call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) @@ -5294,6 +5384,32 @@ fun! s:NetrwHide(islocal) endfun " --------------------------------------------------------------------- +" s:NetrwHideEdit: allows user to edit the file/directory hiding list {{{2 +fun! s:NetrwHideEdit(islocal) +" call Dfunc("NetrwHideEdit(islocal=".a:islocal.")") + + let ykeep= @@ + " save current cursor position + let svpos= netrw#SavePosn() + + " get new hiding list from user + call inputsave() + let newhide= input("Edit Hiding List: ",g:netrw_list_hide) + call inputrestore() + let g:netrw_list_hide= newhide +" call Decho("new g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>")) + + " refresh the listing + sil NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,"./")) + + " restore cursor position + call netrw#RestorePosn(svpos) + let @@= ykeep + +" call Dret("NetrwHideEdit") +endfun + +" --------------------------------------------------------------------- " s:NetrwHidden: invoked by "gh" {{{2 fun! s:NetrwHidden(islocal) " call Dfunc("s:NetrwHidden()") @@ -5325,9 +5441,9 @@ fun! s:NetrwHome() else " go to vim plugin home for home in split(&rtp,',') + [''] - if isdirectory(home) && filewritable(home) | break | endif + if isdirectory(s:NetrwFile(home)) && filewritable(s:NetrwFile(home)) | break | endif let basehome= substitute(home,'[/\\]\.vim$','','') - if isdirectory(basehome) && filewritable(basehome) + if isdirectory(s:NetrwFile(basehome)) && filewritable(s:NetrwFile(basehome)) let home= basehome."/.vim" break endif @@ -5341,9 +5457,9 @@ fun! s:NetrwHome() endif endif " insure that the home directory exists - if g:netrw_dirhistmax > 0 && !isdirectory(home) + if g:netrw_dirhistmax > 0 && !isdirectory(s:NetrwFile(home)) if exists("g:netrw_mkdir") - call system(g:netrw_mkdir." ".shellescape(home)) + call system(g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home))) else call mkdir(home) endif @@ -5370,8 +5486,8 @@ fun! s:NetrwLeftmouse(islocal) let mouse_lnum = v:mouse_lnum let wlastline = line('w$') let lastline = line('$') -" call Decho("v:mouse_lnum=".mouse_lnum." line(w$)=".wlastline." line($)=".lastline." v:mouse_win=".v:mouse_win." winnr#".winnr()) -" call Decho("v:mouse_col =".v:mouse_col." col=".col(".")." wincol =".wincol()." winwidth =".winwidth(0)) +" call Decho("v:mouse_lnum=".mouse_lnum." line(w$)=".wlastline." line($)=".lastline." v:mouse_win=".v:mouse_win." winnr#".winnr(),'~'.expand("<slnum>")) +" call Decho("v:mouse_col =".v:mouse_col." col=".col(".")." wincol =".wincol()." winwidth =".winwidth(0),'~'.expand("<slnum>")) if mouse_lnum >= wlastline + 1 || v:mouse_win != winnr() " appears to be a status bar leftmouse click let @@= ykeep @@ -5382,7 +5498,7 @@ fun! s:NetrwLeftmouse(islocal) " Windows are separated by vertical separator bars - but the mouse seems to be doing what it should when dragging that bar " without this test when its disabled. " May 26, 2014: edit file, :Lex, resize window -- causes refresh. Reinstated a modified test. See if problems develop. -" call Decho("v:mouse_col=".v:mouse_col." col#".col('.')." virtcol#".virtcol('.')." col($)#".col("$")." virtcol($)#".virtcol("$")) +" call Decho("v:mouse_col=".v:mouse_col." col#".col('.')." virtcol#".virtcol('.')." col($)#".col("$")." virtcol($)#".virtcol("$"),'~'.expand("<slnum>")) if v:mouse_col > virtcol('.') let @@= ykeep " call Dret("s:NetrwLeftmouse : detected a vertical separator bar leftmouse click") @@ -5403,6 +5519,14 @@ fun! s:NetrwLeftmouse(islocal) endfun " --------------------------------------------------------------------- +" s:NetrwCLeftmouse: used to select a file/directory for a target {{{2 +fun! s:NetrwCLeftmouse(islocal) +" call Dfunc("s:NetrwCLeftmouse(islocal=".a:islocal.")") + call s:NetrwMarkFileTgt(a:islocal) +" call Dret("s:NetrwCLeftmouse") +endfun + +" --------------------------------------------------------------------- " s:NetrwServerEdit: edit file in a server gvim, usually NETRWSERVER (implements <c-r>){{{2 " a:islocal=0 : <c-r> not used, remote " a:islocal=1 : <c-r> no used, local @@ -5412,10 +5536,12 @@ fun! s:NetrwServerEdit(islocal,fname) " call Dfunc("s:NetrwServerEdit(islocal=".a:islocal.",fname<".a:fname.">)") let islocal = a:islocal%2 " =0: remote =1: local let ctrlr = a:islocal >= 2 " =0: <c-r> not used =1: <c-r> used +" call Decho("islocal=".islocal." ctrlr=".ctrlr,'~'.expand("<slnum>")) - if (islocal && isdirectory(a:fname)) || (!islocal && a:fname =~ '/$') + if (islocal && isdirectory(s:NetrwFile(a:fname))) || (!islocal && a:fname =~ '/$') " handle directories in the local window -- not in the remote vim server " user must have closed the NETRWSERVER window. Treat as a normal editing from netrw. +" call Decho("handling directory in client window",'~'.expand("<slnum>")) let g:netrw_browse_split= 0 if exists("s:netrw_browse_split_".winnr()) let g:netrw_browse_split= s:netrw_browse_split_{winnr()} @@ -5426,22 +5552,23 @@ fun! s:NetrwServerEdit(islocal,fname) return endif +" call Decho("handling file in server window",'~'.expand("<slnum>")) if has("clientserver") && executable("gvim") -" call Decho("has clientserver and gvim") +" call Decho("has clientserver and gvim",'~'.expand("<slnum>")) if exists("g:netrw_browse_split") && type(g:netrw_browse_split) == 3 -" call Decho("g:netrw_browse_split=".string(g:netrw_browse_split)) +" call Decho("g:netrw_browse_split=".string(g:netrw_browse_split),'~'.expand("<slnum>")) let srvrname = g:netrw_browse_split[0] let tabnum = g:netrw_browse_split[1] let winnum = g:netrw_browse_split[2] if serverlist() !~ '\<'.srvrname.'\>' -" call Decho("server not available; ctrlr=".ctrlr) +" call Decho("server not available; ctrlr=".ctrlr,'~'.expand("<slnum>")) if !ctrlr " user must have closed the server window and the user did not use <c-r>, but " used something like <cr>. -" call Decho("user must have closed server AND did not use ctrl-r") +" call Decho("user must have closed server AND did not use ctrl-r",'~'.expand("<slnum>")) if exists("g:netrw_browse_split") unlet g:netrw_browse_split endif @@ -5455,27 +5582,27 @@ fun! s:NetrwServerEdit(islocal,fname) elseif has("win32") && executable("start") " start up remote netrw server under windows -" call Decho("starting up gvim server<".srvrname."> for windows") +" call Decho("starting up gvim server<".srvrname."> for windows",'~'.expand("<slnum>")) call system("start gvim --servername ".srvrname) else " start up remote netrw server under linux -" call Decho("starting up gvim server<".srvrname.">") +" call Decho("starting up gvim server<".srvrname.">",'~'.expand("<slnum>")) call system("gvim --servername ".srvrname) endif endif -" call Decho("srvrname<".srvrname."> tabnum=".tabnum." winnum=".winnum." server-editing<".a:fname.">") +" call Decho("srvrname<".srvrname."> tabnum=".tabnum." winnum=".winnum." server-editing<".a:fname.">",'~'.expand("<slnum>")) call remote_send(srvrname,":tabn ".tabnum."\<cr>") call remote_send(srvrname,":".winnum."wincmd w\<cr>") - call remote_send(srvrname,":e ".fnameescape(a:fname)."\<cr>") + call remote_send(srvrname,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>") else if serverlist() !~ '\<'.g:netrw_servername.'\>' if !ctrlr -" call Decho("server<".g:netrw_servername."> not available and ctrl-r not used") +" call Decho("server<".g:netrw_servername."> not available and ctrl-r not used",'~'.expand("<slnum>")) if exists("g:netrw_browse_split") unlet g:netrw_browse_split endif @@ -5485,14 +5612,14 @@ fun! s:NetrwServerEdit(islocal,fname) return else -" call Decho("server<".g:netrw_servername."> not available but ctrl-r used") +" call Decho("server<".g:netrw_servername."> not available but ctrl-r used",'~'.expand("<slnum>")) if has("win32") && executable("start") " start up remote netrw server under windows -" call Decho("starting up gvim server<".g:netrw_servername."> for windows") +" call Decho("starting up gvim server<".g:netrw_servername."> for windows",'~'.expand("<slnum>")) call system("start gvim --servername ".g:netrw_servername) else " start up remote netrw server under linux -" call Decho("starting up gvim server<".g:netrw_servername.">") +" call Decho("starting up gvim server<".g:netrw_servername.">",'~'.expand("<slnum>")) call system("gvim --servername ".g:netrw_servername) endif endif @@ -5500,8 +5627,8 @@ fun! s:NetrwServerEdit(islocal,fname) while 1 try -" call Decho("remote-send: e ".a:fname) - call remote_send(g:netrw_servername,":e ".fnameescape(a:fname)."\<cr>") +" call Decho("remote-send: e ".a:fname,'~'.expand("<slnum>")) + call remote_send(g:netrw_servername,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>") break catch /^Vim\%((\a\+)\)\=:E241/ sleep 200m @@ -5528,7 +5655,7 @@ endfun " s:NetrwSLeftmouse: marks the file under the cursor. May be dragged to select additional files {{{2 fun! s:NetrwSLeftmouse(islocal) " call Dfunc("s:NetrwSLeftmouse(islocal=".a:islocal.")") - + let s:ngw= s:NetrwGetWord() call s:NetrwMarkFile(a:islocal,s:ngw) @@ -5587,7 +5714,7 @@ fun! s:NetrwListHide() " string. Use the first character left as a separator character. let listhide= g:netrw_list_hide let sep = strpart(substitute('/~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1) -" call Decho("sep=".sep) +" call Decho("sep=".sep,'~'.expand("<slnum>")) while listhide != "" if listhide =~ ',' @@ -5600,10 +5727,10 @@ fun! s:NetrwListHide() " Prune the list by hiding any files which match if g:netrw_hide == 1 -" call Decho("hiding<".hide."> listhide<".listhide.">") +" call Decho("hiding<".hide."> listhide<".listhide.">",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'d' elseif g:netrw_hide == 2 -" call Decho("showing<".hide."> listhide<".listhide.">") +" call Decho("showing<".hide."> listhide<".listhide.">",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'s@^@ /-KEEP-/ @' endif endwhile @@ -5621,52 +5748,6 @@ fun! s:NetrwListHide() endfun " --------------------------------------------------------------------- -" NetrwHideEdit: allows user to edit the file/directory hiding list -fun! s:NetrwHideEdit(islocal) -" call Dfunc("NetrwHideEdit(islocal=".a:islocal.")") - - let ykeep= @@ - " save current cursor position - let svpos= netrw#SavePosn() - - " get new hiding list from user - call inputsave() - let newhide= input("Edit Hiding List: ",g:netrw_list_hide) - call inputrestore() - let g:netrw_list_hide= newhide -" call Decho("new g:netrw_list_hide<".g:netrw_list_hide.">") - - " refresh the listing - sil NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,"./")) - - " restore cursor position - call netrw#RestorePosn(svpos) - let @@= ykeep - -" call Dret("NetrwHideEdit") -endfun - -" --------------------------------------------------------------------- -" NetSortSequence: allows user to edit the sorting sequence -fun! s:NetSortSequence(islocal) -" call Dfunc("NetSortSequence(islocal=".a:islocal.")") - - let ykeep= @@ - let svpos= netrw#SavePosn() - call inputsave() - let newsortseq= input("Edit Sorting Sequence: ",g:netrw_sort_sequence) - call inputrestore() - - " refresh the listing - let g:netrw_sort_sequence= newsortseq - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) - NetrwKeepj call netrw#RestorePosn(svpos) - let @@= ykeep - -" call Dret("NetSortSequence") -endfun - -" --------------------------------------------------------------------- " s:NetrwMakeDir: this function makes a directory (both local and remote) {{{2 " implements the "d" mapping. fun! s:NetrwMakeDir(usrhost) @@ -5679,7 +5760,7 @@ fun! s:NetrwMakeDir(usrhost) call inputsave() let newdirname= input("Please give directory name: ") call inputrestore() -" call Decho("newdirname<".newdirname.">") +" call Decho("newdirname<".newdirname.">",'~'.expand("<slnum>")) if newdirname == "" let @@= ykeep @@ -5688,13 +5769,13 @@ fun! s:NetrwMakeDir(usrhost) endif if a:usrhost == "" -" call Decho("local mkdir") +" call Decho("local mkdir",'~'.expand("<slnum>")) " Local mkdir: " sanity checks let fullnewdir= b:netrw_curdir.'/'.newdirname -" call Decho("fullnewdir<".fullnewdir.">") - if isdirectory(fullnewdir) +" call Decho("fullnewdir<".fullnewdir.">",'~'.expand("<slnum>")) + if isdirectory(s:NetrwFile(fullnewdir)) if !exists("g:netrw_quiet") NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a directory!",24) endif @@ -5722,23 +5803,23 @@ fun! s:NetrwMakeDir(usrhost) else let netrw_origdir= s:NetrwGetcwd(1) call s:NetrwLcd(b:netrw_curdir) -" call Decho("netrw_origdir<".netrw_origdir.">: lcd b:netrw_curdir<".fnameescape(b:netrw_curdir).">") - call s:NetrwExe("sil! !".g:netrw_localmkdir.' '.shellescape(newdirname,1)) +" call Decho("netrw_origdir<".netrw_origdir.">: lcd b:netrw_curdir<".fnameescape(b:netrw_curdir).">",'~'.expand("<slnum>")) + call s:NetrwExe("sil! !".g:netrw_localmkdir.' '.s:ShellEscape(newdirname,1)) if v:shell_error != 0 let @@= ykeep call netrw#ErrorMsg(s:ERROR,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80) -" call Dret("s:NetrwMakeDir : failed: sil! !".g:netrw_localmkdir.' '.shellescape(newdirname,1)) +" call Dret("s:NetrwMakeDir : failed: sil! !".g:netrw_localmkdir.' '.s:ShellEscape(newdirname,1)) return endif if !g:netrw_keepdir -" call Decho("restoring netrw_origdir since g:netrw_keepdir=".g:netrw_keepdir) +" call Decho("restoring netrw_origdir since g:netrw_keepdir=".g:netrw_keepdir,'~'.expand("<slnum>")) call s:NetrwLcd(netrw_origdir) endif endif if v:shell_error == 0 " refresh listing -" call Decho("refresh listing") +" call Decho("refresh listing",'~'.expand("<slnum>")) let svpos= netrw#SavePosn() call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./')) call netrw#RestorePosn(svpos) @@ -5749,10 +5830,10 @@ fun! s:NetrwMakeDir(usrhost) elseif !exists("b:netrw_method") || b:netrw_method == 4 " Remote mkdir: using ssh -" call Decho("remote mkdir") +" call Decho("remote mkdir",'~'.expand("<slnum>")) let mkdircmd = s:MakeSshCmd(g:netrw_mkdir_cmd) let newdirname= substitute(b:netrw_curdir,'^\%(.\{-}/\)\{3}\(.*\)$','\1','').newdirname - call s:NetrwExe("sil! !".mkdircmd." ".shellescape(newdirname,1)) + call s:NetrwExe("sil! !".mkdircmd." ".s:ShellEscape(newdirname,1)) if v:shell_error == 0 " refresh listing let svpos= netrw#SavePosn() @@ -5766,9 +5847,9 @@ fun! s:NetrwMakeDir(usrhost) elseif b:netrw_method == 2 " Remote mkdir: using ftp+.netrc let svpos= netrw#SavePosn() -" call Decho("b:netrw_curdir<".b:netrw_curdir.">") +" call Decho("b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) if exists("b:netrw_fname") -" call Decho("b:netrw_fname<".b:netrw_fname.">") +" call Decho("b:netrw_fname<".b:netrw_fname.">",'~'.expand("<slnum>")) let remotepath= b:netrw_fname else let remotepath= "" @@ -5780,9 +5861,9 @@ fun! s:NetrwMakeDir(usrhost) elseif b:netrw_method == 3 " Remote mkdir: using ftp + machine, id, passwd, and fname (ie. no .netrc) let svpos= netrw#SavePosn() -" call Decho("b:netrw_curdir<".b:netrw_curdir.">") +" call Decho("b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) if exists("b:netrw_fname") -" call Decho("b:netrw_fname<".b:netrw_fname.">") +" call Decho("b:netrw_fname<".b:netrw_fname.">",'~'.expand("<slnum>")) let remotepath= b:netrw_fname else let remotepath= "" @@ -5805,19 +5886,19 @@ fun! s:TreeSqueezeDir(islocal) let curdepth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') let iline = line(".") - 1 let stopline = (exists("w:netrw_bannercnt")? (w:netrw_bannercnt + 1) : 1) -" call Decho("curdepth=".curdepth) -" call Decho("stopline#".stopline) -" call Decho("starting with line#".line(".").": ".getline('.')) +" call Decho("curdepth=".curdepth,'~'.expand("<slnum>")) +" call Decho("stopline#".stopline,'~'.expand("<slnum>")) +" call Decho("starting with line#".line(".").": ".getline('.'),'~'.expand("<slnum>")) while iline > stopline " find a line that has less depth let depth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') -" call Decho("considering line#".line(".").": ".getline('.')) +" call Decho("considering line#".line(".").": ".getline('.'),'~'.expand("<slnum>")) if depth < curdepth break endif norm! k endwhile -" call Decho("squeezing at line#".line(".").": ".getline('.')) +" call Decho("squeezing at line#".line(".").": ".getline('.'),'~'.expand("<slnum>")) call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,s:NetrwGetWord())) endif " call Dret("s:TreeSqueezeDir") @@ -5829,128 +5910,130 @@ fun! s:NetrwMaps(islocal) " call Dfunc("s:NetrwMaps(islocal=".a:islocal.") b:netrw_curdir<".b:netrw_curdir.">") if g:netrw_mousemaps && g:netrw_retmap -" call Decho("set up Rexplore 2-leftmouse") +" call Decho("set up Rexplore 2-leftmouse",'~'.expand("<slnum>")) if !hasmapto("<Plug>NetrwReturn") if maparg("<2-leftmouse>","n") == "" || maparg("<2-leftmouse>","n") =~ '^-$' -" call Decho("making map for 2-leftmouse") +" call Decho("making map for 2-leftmouse",'~'.expand("<slnum>")) nmap <unique> <silent> <2-leftmouse> <Plug>NetrwReturn elseif maparg("<c-leftmouse>","n") == "" -" call Decho("making map for c-leftmouse") +" call Decho("making map for c-leftmouse",'~'.expand("<slnum>")) nmap <unique> <silent> <c-leftmouse> <Plug>NetrwReturn endif endif nno <silent> <Plug>NetrwReturn :Rexplore<cr> -" call Decho("made <Plug>NetrwReturn map") +" call Decho("made <Plug>NetrwReturn map",'~'.expand("<slnum>")) endif if a:islocal -" call Decho("make local maps") +" call Decho("make local maps",'~'.expand("<slnum>")) " local normal-mode maps - nnoremap <buffer> <silent> a :call <SID>NetrwHide(1)<cr> - nnoremap <buffer> <silent> % :call <SID>NetrwOpenFile(1)<cr> - nnoremap <buffer> <silent> c :call <SID>NetrwLcd(b:netrw_curdir)<cr> - nnoremap <buffer> <silent> C :<c-u>call <SID>NetrwSetChgwin()<cr> - nnoremap <buffer> <silent> <cr> :call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord()))<cr> - nnoremap <buffer> <silent> <s-cr> :call <SID>TreeSqueezeDir(1)<cr> - nnoremap <buffer> <silent> <c-r> :call <SID>NetrwServerEdit(3,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> d :call <SID>NetrwMakeDir("")<cr> - nnoremap <buffer> <silent> - :call <SID>NetrwBrowseUpDir(1)<cr> - nnoremap <buffer> <silent> gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> gd :<c-u>call <SID>NetrwForceChgDir(1,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> gf :<c-u>call <SID>NetrwForceFile(1,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> gh :<c-u>call <SID>NetrwHidden(1)<cr> - nnoremap <buffer> <silent> gp :<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> I :call <SID>NetrwBannerCtrl(1)<cr> - nnoremap <buffer> <silent> i :call <SID>NetrwListStyle(1)<cr> - nnoremap <buffer> <silent> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> mc :<c-u>call <SID>NetrwMarkFileCopy(1)<cr> - nnoremap <buffer> <silent> md :<c-u>call <SID>NetrwMarkFileDiff(1)<cr> - nnoremap <buffer> <silent> me :<c-u>call <SID>NetrwMarkFileEdit(1)<cr> - nnoremap <buffer> <silent> mf :<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> - nnoremap <buffer> <silent> mg :<c-u>call <SID>NetrwMarkFileGrep(1)<cr> - nnoremap <buffer> <silent> mh :<c-u>call <SID>NetrwMarkHideSfx(1)<cr> - nnoremap <buffer> <silent> mm :<c-u>call <SID>NetrwMarkFileMove(1)<cr> - nnoremap <buffer> <silent> mp :<c-u>call <SID>NetrwMarkFilePrint(1)<cr> - nnoremap <buffer> <silent> mr :<c-u>call <SID>NetrwMarkFileRegexp(1)<cr> - nnoremap <buffer> <silent> ms :<c-u>call <SID>NetrwMarkFileSource(1)<cr> - nnoremap <buffer> <silent> mt :<c-u>call <SID>NetrwMarkFileTgt(1)<cr> - nnoremap <buffer> <silent> mT :<c-u>call <SID>NetrwMarkFileTag(1)<cr> - nnoremap <buffer> <silent> mu :<c-u>call <SID>NetrwUnMarkFile(1)<cr> - nnoremap <buffer> <silent> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> - nnoremap <buffer> <silent> mx :<c-u>call <SID>NetrwMarkFileExe(1,0)<cr> - nnoremap <buffer> <silent> mX :<c-u>call <SID>NetrwMarkFileExe(1,1)<cr> - nnoremap <buffer> <silent> mz :<c-u>call <SID>NetrwMarkFileCompress(1)<cr> - nnoremap <buffer> <silent> O :call <SID>NetrwObtain(1)<cr> - nnoremap <buffer> <silent> o :call <SID>NetrwSplit(3)<cr> - nnoremap <buffer> <silent> p :call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> - nnoremap <buffer> <silent> P :call <SID>NetrwPrevWinOpen(1)<cr> - nnoremap <buffer> <silent> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> qf :<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr> - nnoremap <buffer> <silent> r :let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> - nnoremap <buffer> <silent> s :call <SID>NetrwSortStyle(1)<cr> - nnoremap <buffer> <silent> S :call <SID>NetSortSequence(1)<cr> - nnoremap <buffer> <silent> t :call <SID>NetrwSplit(4)<cr> - nnoremap <buffer> <silent> Tb :<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> - nnoremap <buffer> <silent> Th :<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> - nnoremap <buffer> <silent> u :<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr> - nnoremap <buffer> <silent> U :<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr> - nnoremap <buffer> <silent> v :call <SID>NetrwSplit(5)<cr> - nnoremap <buffer> <silent> x :call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),0),0)"<cr> - nnoremap <buffer> <silent> X :call <SID>NetrwLocalExecute(expand("<cword>"))"<cr> + nnoremap <buffer> <silent> <nowait> a :call <SID>NetrwHide(1)<cr> + nnoremap <buffer> <silent> <nowait> % :call <SID>NetrwOpenFile(1)<cr> + nnoremap <buffer> <silent> <nowait> c :call <SID>NetrwLcd(b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> C :<c-u>call <SID>NetrwSetChgwin()<cr> + nnoremap <buffer> <silent> <nowait> <cr> :call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord()))<cr> + nnoremap <buffer> <silent> <nowait> <s-cr> :call <SID>TreeSqueezeDir(1)<cr> + nnoremap <buffer> <silent> <nowait> <c-r> :call <SID>NetrwServerEdit(3,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("")<cr> + nnoremap <buffer> <silent> <nowait> - :call <SID>NetrwBrowseUpDir(1)<cr> + nnoremap <buffer> <silent> <nowait> gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(1,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(1,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(1)<cr> + nnoremap <buffer> <silent> <nowait> gn :<c-u>call netrw#SetTreetop(<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> I :call <SID>NetrwBannerCtrl(1)<cr> + nnoremap <buffer> <silent> <nowait> i :call <SID>NetrwListStyle(1)<cr> + nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(1)<cr> + nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(1)<cr> + nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(1)<cr> + nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(1)<cr> + nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(1)<cr> + nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(1)<cr> + nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(1)<cr> + nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(1)<cr> + nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(1)<cr> + nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(1)<cr> + nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(1)<cr> + nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(1)<cr> + nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> + nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(1,0)<cr> + nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(1,1)<cr> + nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(1)<cr> + nnoremap <buffer> <silent> <nowait> O :call <SID>NetrwObtain(1)<cr> + nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(3)<cr> + nnoremap <buffer> <silent> <nowait> p :call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> + nnoremap <buffer> <silent> <nowait> P :call <SID>NetrwPrevWinOpen(1)<cr> + nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr> + nnoremap <buffer> <silent> <nowait> r :let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> + nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(1)<cr> + nnoremap <buffer> <silent> <nowait> S :call <SID>NetSortSequence(1)<cr> + nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(4)<cr> + nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> + nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> + nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr> + nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr> + nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(5)<cr> + nnoremap <buffer> <silent> <nowait> x :call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),0),0)"<cr> + nnoremap <buffer> <silent> <nowait> X :call <SID>NetrwLocalExecute(expand("<cword>"))"<cr> " local insert-mode maps - inoremap <buffer> <silent> a <c-o>:call <SID>NetrwHide(1)<cr> - inoremap <buffer> <silent> c <c-o>:exe "NetrwKeepj lcd ".fnameescape(b:netrw_curdir)<cr> - inoremap <buffer> <silent> c <c-o>:call <SID>NetrwLcd(b:netrw_curdir)<cr> - inoremap <buffer> <silent> C <c-o>:call <SID>NetrwSetChgwin()<cr> - inoremap <buffer> <silent> % <c-o>:call <SID>NetrwOpenFile(1)<cr> - inoremap <buffer> <silent> - <c-o>:call <SID>NetrwBrowseUpDir(1)<cr> - inoremap <buffer> <silent> <cr> <c-o>:call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord()))<cr> - inoremap <buffer> <silent> <s-cr> <c-o>:call <SID>TreeSqueezeDir(1)<cr> - inoremap <buffer> <silent> d <c-o>:call <SID>NetrwMakeDir("")<cr> - inoremap <buffer> <silent> gb <c-o>:<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> - inoremap <buffer> <silent> gh <c-o>:<c-u>call <SID>NetrwHidden(1)<cr> - inoremap <buffer> <silent> gp <c-o>:<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr> - inoremap <buffer> <silent> I <c-o>:call <SID>NetrwBannerCtrl(1)<cr> - inoremap <buffer> <silent> i <c-o>:call <SID>NetrwListStyle(1)<cr> - inoremap <buffer> <silent> mb <c-o>:<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> - inoremap <buffer> <silent> mB <c-o>:<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> - inoremap <buffer> <silent> mc <c-o>:<c-u>call <SID>NetrwMarkFileCopy(1)<cr> - inoremap <buffer> <silent> md <c-o>:<c-u>call <SID>NetrwMarkFileDiff(1)<cr> - inoremap <buffer> <silent> me <c-o>:<c-u>call <SID>NetrwMarkFileEdit(1)<cr> - inoremap <buffer> <silent> mf <c-o>:<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> - inoremap <buffer> <silent> mg <c-o>:<c-u>call <SID>NetrwMarkFileGrep(1)<cr> - inoremap <buffer> <silent> mh <c-o>:<c-u>call <SID>NetrwMarkHideSfx(1)<cr> - inoremap <buffer> <silent> mm <c-o>:<c-u>call <SID>NetrwMarkFileMove(1)<cr> - inoremap <buffer> <silent> mp <c-o>:<c-u>call <SID>NetrwMarkFilePrint(1)<cr> - inoremap <buffer> <silent> mr <c-o>:<c-u>call <SID>NetrwMarkFileRegexp(1)<cr> - inoremap <buffer> <silent> ms <c-o>:<c-u>call <SID>NetrwMarkFileSource(1)<cr> - inoremap <buffer> <silent> mT <c-o>:<c-u>call <SID>NetrwMarkFileTag(1)<cr> - inoremap <buffer> <silent> mt <c-o>:<c-u>call <SID>NetrwMarkFileTgt(1)<cr> - inoremap <buffer> <silent> mu <c-o>:<c-u>call <SID>NetrwUnMarkFile(1)<cr> - inoremap <buffer> <silent> mv <c-o>:<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> - inoremap <buffer> <silent> mx <c-o>:<c-u>call <SID>NetrwMarkFileExe(1,0)<cr> - inoremap <buffer> <silent> mX <c-o>:<c-u>call <SID>NetrwMarkFileExe(1,1)<cr> - inoremap <buffer> <silent> mz <c-o>:<c-u>call <SID>NetrwMarkFileCompress(1)<cr> - inoremap <buffer> <silent> O <c-o>:call <SID>NetrwObtain(1)<cr> - inoremap <buffer> <silent> o <c-o>:call <SID>NetrwSplit(3)<cr> - inoremap <buffer> <silent> p <c-o>:call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> - inoremap <buffer> <silent> P <c-o>:call <SID>NetrwPrevWinOpen(1)<cr> - inoremap <buffer> <silent> qb <c-o>:<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> - inoremap <buffer> <silent> qf <c-o>:<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr> - inoremap <buffer> <silent> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr> - inoremap <buffer> <silent> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> - inoremap <buffer> <silent> s <c-o>:call <SID>NetrwSortStyle(1)<cr> - inoremap <buffer> <silent> S <c-o>:call <SID>NetSortSequence(1)<cr> - inoremap <buffer> <silent> t <c-o>:call <SID>NetrwSplit(4)<cr> - inoremap <buffer> <silent> Tb <c-o>:<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> - inoremap <buffer> <silent> Th <c-o>:<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> - inoremap <buffer> <silent> u <c-o>:<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr> - inoremap <buffer> <silent> U <c-o>:<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr> - inoremap <buffer> <silent> v <c-o>:call <SID>NetrwSplit(5)<cr> - inoremap <buffer> <silent> x <c-o>:call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),0),0)"<cr> + inoremap <buffer> <silent> <nowait> a <c-o>:call <SID>NetrwHide(1)<cr> + inoremap <buffer> <silent> <nowait> c <c-o>:exe "NetrwKeepj lcd ".fnameescape(b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> c <c-o>:call <SID>NetrwLcd(b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> C <c-o>:call <SID>NetrwSetChgwin()<cr> + inoremap <buffer> <silent> <nowait> % <c-o>:call <SID>NetrwOpenFile(1)<cr> + inoremap <buffer> <silent> <nowait> - <c-o>:call <SID>NetrwBrowseUpDir(1)<cr> + inoremap <buffer> <silent> <nowait> <cr> <c-o>:call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord()))<cr> + inoremap <buffer> <silent> <nowait> <s-cr> <c-o>:call <SID>TreeSqueezeDir(1)<cr> + inoremap <buffer> <silent> <nowait> d <c-o>:call <SID>NetrwMakeDir("")<cr> + inoremap <buffer> <silent> <nowait> gb <c-o>:<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> gh <c-o>:<c-u>call <SID>NetrwHidden(1)<cr> + nnoremap <buffer> <silent> <nowait> gn :<c-u>call netrw#SetTreetop(<SID>NetrwGetWord())<cr> + inoremap <buffer> <silent> <nowait> gp <c-o>:<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> I <c-o>:call <SID>NetrwBannerCtrl(1)<cr> + inoremap <buffer> <silent> <nowait> i <c-o>:call <SID>NetrwListStyle(1)<cr> + inoremap <buffer> <silent> <nowait> mb <c-o>:<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> mB <c-o>:<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> mc <c-o>:<c-u>call <SID>NetrwMarkFileCopy(1)<cr> + inoremap <buffer> <silent> <nowait> md <c-o>:<c-u>call <SID>NetrwMarkFileDiff(1)<cr> + inoremap <buffer> <silent> <nowait> me <c-o>:<c-u>call <SID>NetrwMarkFileEdit(1)<cr> + inoremap <buffer> <silent> <nowait> mf <c-o>:<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> + inoremap <buffer> <silent> <nowait> mg <c-o>:<c-u>call <SID>NetrwMarkFileGrep(1)<cr> + inoremap <buffer> <silent> <nowait> mh <c-o>:<c-u>call <SID>NetrwMarkHideSfx(1)<cr> + inoremap <buffer> <silent> <nowait> mm <c-o>:<c-u>call <SID>NetrwMarkFileMove(1)<cr> + inoremap <buffer> <silent> <nowait> mp <c-o>:<c-u>call <SID>NetrwMarkFilePrint(1)<cr> + inoremap <buffer> <silent> <nowait> mr <c-o>:<c-u>call <SID>NetrwMarkFileRegexp(1)<cr> + inoremap <buffer> <silent> <nowait> ms <c-o>:<c-u>call <SID>NetrwMarkFileSource(1)<cr> + inoremap <buffer> <silent> <nowait> mT <c-o>:<c-u>call <SID>NetrwMarkFileTag(1)<cr> + inoremap <buffer> <silent> <nowait> mt <c-o>:<c-u>call <SID>NetrwMarkFileTgt(1)<cr> + inoremap <buffer> <silent> <nowait> mu <c-o>:<c-u>call <SID>NetrwUnMarkFile(1)<cr> + inoremap <buffer> <silent> <nowait> mv <c-o>:<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> + inoremap <buffer> <silent> <nowait> mx <c-o>:<c-u>call <SID>NetrwMarkFileExe(1,0)<cr> + inoremap <buffer> <silent> <nowait> mX <c-o>:<c-u>call <SID>NetrwMarkFileExe(1,1)<cr> + inoremap <buffer> <silent> <nowait> mz <c-o>:<c-u>call <SID>NetrwMarkFileCompress(1)<cr> + inoremap <buffer> <silent> <nowait> O <c-o>:call <SID>NetrwObtain(1)<cr> + inoremap <buffer> <silent> <nowait> o <c-o>:call <SID>NetrwSplit(3)<cr> + inoremap <buffer> <silent> <nowait> p <c-o>:call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> + inoremap <buffer> <silent> <nowait> P <c-o>:call <SID>NetrwPrevWinOpen(1)<cr> + inoremap <buffer> <silent> <nowait> qb <c-o>:<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> qf <c-o>:<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr> + inoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr> + inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> + inoremap <buffer> <silent> <nowait> s <c-o>:call <SID>NetrwSortStyle(1)<cr> + inoremap <buffer> <silent> <nowait> S <c-o>:call <SID>NetSortSequence(1)<cr> + inoremap <buffer> <silent> <nowait> t <c-o>:call <SID>NetrwSplit(4)<cr> + inoremap <buffer> <silent> <nowait> Tb <c-o>:<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> + inoremap <buffer> <silent> <nowait> Th <c-o>:<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> + inoremap <buffer> <silent> <nowait> u <c-o>:<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr> + inoremap <buffer> <silent> <nowait> U <c-o>:<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr> + inoremap <buffer> <silent> <nowait> v <c-o>:call <SID>NetrwSplit(5)<cr> + inoremap <buffer> <silent> <nowait> x <c-o>:call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),0),0)"<cr> if !hasmapto('<Plug>NetrwHideEdit') nmap <buffer> <unique> <c-h> <Plug>NetrwHideEdit imap <buffer> <unique> <c-h> <Plug>NetrwHideEdit @@ -5960,7 +6043,7 @@ fun! s:NetrwMaps(islocal) nmap <buffer> <unique> <c-l> <Plug>NetrwRefresh imap <buffer> <unique> <c-l> <Plug>NetrwRefresh endif - nnoremap <buffer> <silent> <Plug>NetrwRefresh :call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> + nnoremap <buffer> <silent> <Plug>NetrwRefresh :call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,(w:netrw_liststyle == 3)? w:netrw_treetop : './'))<cr> if s:didstarstar || !mapcheck("<s-down>","n") nnoremap <buffer> <silent> <s-down> :Nexplore<cr> inoremap <buffer> <silent> <s-down> :Nexplore<cr> @@ -5972,144 +6055,149 @@ fun! s:NetrwMaps(islocal) let mapsafecurdir = escape(b:netrw_curdir, s:netrw_map_escape) if g:netrw_mousemaps == 1 nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse - nno <buffer> <silent> <Plug>NetrwLeftmouse <leftmouse>:call <SID>NetrwLeftmouse(1)<cr> + nno <buffer> <silent> <Plug>NetrwLeftmouse <leftmouse>:call <SID>NetrwLeftmouse(1)<cr> + nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse + nno <buffer> <silent> <Plug>NetrwCLeftmouse <leftmouse>:call <SID>NetrwCLeftmouse(1)<cr> nmap <buffer> <middlemouse> <Plug>NetrwMiddlemouse - nno <buffer> <silent> <Plug>NetrwMiddlemouse <leftmouse>:call <SID>NetrwPrevWinOpen(1)<cr> + nno <buffer> <silent> <Plug>NetrwMiddlemouse <leftmouse>:call <SID>NetrwPrevWinOpen(1)<cr> nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse - nno <buffer> <silent> <Plug>NetrwSLeftmouse <leftmouse>:call <SID>NetrwSLeftmouse(1)<cr> + nno <buffer> <silent> <Plug>NetrwSLeftmouse <leftmouse>:call <SID>NetrwSLeftmouse(1)<cr> nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag - nno <buffer> <silent> <Plug>NetrwSLeftdrag <leftmouse>:call <SID>NetrwSLeftdrag(1)<cr> + nno <buffer> <silent> <Plug>NetrwSLeftdrag <leftmouse>:call <SID>NetrwSLeftdrag(1)<cr> nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse - nmap <buffer> <silent> <Plug>Netrw2Leftmouse - + nmap <buffer> <silent> <Plug>Netrw2Leftmouse - imap <buffer> <leftmouse> <Plug>ILeftmouse - ino <buffer> <silent> <Plug>ILeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwLeftmouse(1)<cr> + ino <buffer> <silent> <Plug>ILeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwLeftmouse(1)<cr> imap <buffer> <middlemouse> <Plug>IMiddlemouse - ino <buffer> <silent> <Plug>IMiddlemouse <c-o><leftmouse><c-o>:call <SID>NetrwPrevWinOpen(1)<cr> + ino <buffer> <silent> <Plug>IMiddlemouse <c-o><leftmouse><c-o>:call <SID>NetrwPrevWinOpen(1)<cr> imap <buffer> <s-leftmouse> <Plug>ISLeftmouse - ino <buffer> <silent> <Plug>ISLeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> + ino <buffer> <silent> <Plug>ISLeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> exe 'nnoremap <buffer> <silent> <rightmouse> <leftmouse>:call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' exe 'vnoremap <buffer> <silent> <rightmouse> <leftmouse>:call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' exe 'inoremap <buffer> <silent> <rightmouse> <c-o><leftmouse><c-o>:call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' endif - exe 'nnoremap <buffer> <silent> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'nnoremap <buffer> <silent> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'nnoremap <buffer> <silent> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' - exe 'nnoremap <buffer> <silent> d :call <SID>NetrwMakeDir("")<cr>' - exe 'vnoremap <buffer> <silent> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'vnoremap <buffer> <silent> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'vnoremap <buffer> <silent> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' - exe 'inoremap <buffer> <silent> <del> <c-o>:call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'inoremap <buffer> <silent> D <c-o>:call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'inoremap <buffer> <silent> R <c-o>:call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' - exe 'inoremap <buffer> <silent> d <c-o>:call <SID>NetrwMakeDir("")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> <del> <c-o>:call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> D <c-o>:call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> R <c-o>:call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> d <c-o>:call <SID>NetrwMakeDir("")<cr>' nnoremap <buffer> <F1> :he netrw-quickhelp<cr> + " support user-specified maps + call netrw#UserMaps(1) + else " remote -" call Decho("make remote maps") +" call Decho("make remote maps",'~'.expand("<slnum>")) call s:RemotePathAnalysis(b:netrw_curdir) " remote normal-mode maps - nnoremap <buffer> <silent> <cr> :call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()))<cr> - nnoremap <buffer> <silent> <s-cr> :call <SID>TreeSqueezeDir(0)<cr> - nnoremap <buffer> <silent> <c-l> :call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> - nnoremap <buffer> <silent> <c-r> :call <SID>NetrwServerEdit(2,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> - :call <SID>NetrwBrowseUpDir(0)<cr> - nnoremap <buffer> <silent> a :call <SID>NetrwHide(0)<cr> - nnoremap <buffer> <silent> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> mc :<c-u>call <SID>NetrwMarkFileCopy(0)<cr> - nnoremap <buffer> <silent> md :<c-u>call <SID>NetrwMarkFileDiff(0)<cr> - nnoremap <buffer> <silent> me :<c-u>call <SID>NetrwMarkFileEdit(0)<cr> - nnoremap <buffer> <silent> mf :<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> - nnoremap <buffer> <silent> mg :<c-u>call <SID>NetrwMarkFileGrep(0)<cr> - nnoremap <buffer> <silent> mh :<c-u>call <SID>NetrwMarkHideSfx(0)<cr> - nnoremap <buffer> <silent> mm :<c-u>call <SID>NetrwMarkFileMove(0)<cr> - nnoremap <buffer> <silent> mp :<c-u>call <SID>NetrwMarkFilePrint(0)<cr> - nnoremap <buffer> <silent> mr :<c-u>call <SID>NetrwMarkFileRegexp(0)<cr> - nnoremap <buffer> <silent> ms :<c-u>call <SID>NetrwMarkFileSource(0)<cr> - nnoremap <buffer> <silent> mt :<c-u>call <SID>NetrwMarkFileTgt(0)<cr> - nnoremap <buffer> <silent> mT :<c-u>call <SID>NetrwMarkFileTag(0)<cr> - nnoremap <buffer> <silent> mu :<c-u>call <SID>NetrwUnMarkFile(0)<cr> - nnoremap <buffer> <silent> mv :<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr> - nnoremap <buffer> <silent> mx :<c-u>call <SID>NetrwMarkFileExe(0,0)<cr> - nnoremap <buffer> <silent> mX :<c-u>call <SID>NetrwMarkFileExe(0,1)<cr> - nnoremap <buffer> <silent> mz :<c-u>call <SID>NetrwMarkFileCompress(0)<cr> - nnoremap <buffer> <silent> gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> gd :<c-u>call <SID>NetrwForceChgDir(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> gf :<c-u>call <SID>NetrwForceFile(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> gh :<c-u>call <SID>NetrwHidden(0)<cr> - nnoremap <buffer> <silent> gp :<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> C :<c-u>call <SID>NetrwSetChgwin()<cr> - nnoremap <buffer> <silent> i :call <SID>NetrwListStyle(0)<cr> - nnoremap <buffer> <silent> I :call <SID>NetrwBannerCtrl(1)<cr> - nnoremap <buffer> <silent> o :call <SID>NetrwSplit(0)<cr> - nnoremap <buffer> <silent> O :call <SID>NetrwObtain(0)<cr> - nnoremap <buffer> <silent> p :call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> - nnoremap <buffer> <silent> P :call <SID>NetrwPrevWinOpen(0)<cr> - nnoremap <buffer> <silent> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> qf :<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr> - nnoremap <buffer> <silent> r :let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> - nnoremap <buffer> <silent> s :call <SID>NetrwSortStyle(0)<cr> - nnoremap <buffer> <silent> S :call <SID>NetSortSequence(0)<cr> - nnoremap <buffer> <silent> t :call <SID>NetrwSplit(1)<cr> - nnoremap <buffer> <silent> Tb :<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> - nnoremap <buffer> <silent> Th :<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> - nnoremap <buffer> <silent> u :<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> v :call <SID>NetrwSplit(2)<cr> - nnoremap <buffer> <silent> x :call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()),1)<cr> - nnoremap <buffer> <silent> % :call <SID>NetrwOpenFile(0)<cr> + nnoremap <buffer> <silent> <nowait> <cr> :call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()))<cr> + nnoremap <buffer> <silent> <nowait> <s-cr> :call <SID>TreeSqueezeDir(0)<cr> + nnoremap <buffer> <silent> <nowait> <c-l> :call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> + nnoremap <buffer> <silent> <nowait> <c-r> :call <SID>NetrwServerEdit(2,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> - :call <SID>NetrwBrowseUpDir(0)<cr> + nnoremap <buffer> <silent> <nowait> a :call <SID>NetrwHide(0)<cr> + nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(0)<cr> + nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(0)<cr> + nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(0)<cr> + nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(0)<cr> + nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(0)<cr> + nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(0)<cr> + nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(0)<cr> + nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(0)<cr> + nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(0)<cr> + nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(0)<cr> + nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(0)<cr> + nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(0)<cr> + nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr> + nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(0,0)<cr> + nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(0,1)<cr> + nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(0)<cr> + nnoremap <buffer> <silent> <nowait> gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(0)<cr> + nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> C :<c-u>call <SID>NetrwSetChgwin()<cr> + nnoremap <buffer> <silent> <nowait> i :call <SID>NetrwListStyle(0)<cr> + nnoremap <buffer> <silent> <nowait> I :call <SID>NetrwBannerCtrl(1)<cr> + nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(0)<cr> + nnoremap <buffer> <silent> <nowait> O :call <SID>NetrwObtain(0)<cr> + nnoremap <buffer> <silent> <nowait> p :call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> + nnoremap <buffer> <silent> <nowait> P :call <SID>NetrwPrevWinOpen(0)<cr> + nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr> + nnoremap <buffer> <silent> <nowait> r :let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> + nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(0)<cr> + nnoremap <buffer> <silent> <nowait> S :call <SID>NetSortSequence(0)<cr> + nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(1)<cr> + nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> + nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> + nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(2)<cr> + nnoremap <buffer> <silent> <nowait> x :call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()),1)<cr> + nnoremap <buffer> <silent> <nowait> % :call <SID>NetrwOpenFile(0)<cr> " remote insert-mode maps - inoremap <buffer> <silent> <cr> <c-o>:call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()))<cr> - inoremap <buffer> <silent> <c-l> <c-o>:call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> - inoremap <buffer> <silent> <s-cr> <c-o>:call <SID>TreeSqueezeDir(0)<cr> - inoremap <buffer> <silent> - <c-o>:call <SID>NetrwBrowseUpDir(0)<cr> - inoremap <buffer> <silent> a <c-o>:call <SID>NetrwHide(0)<cr> - inoremap <buffer> <silent> mb <c-o>:<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> - inoremap <buffer> <silent> mc <c-o>:<c-u>call <SID>NetrwMarkFileCopy(0)<cr> - inoremap <buffer> <silent> md <c-o>:<c-u>call <SID>NetrwMarkFileDiff(0)<cr> - inoremap <buffer> <silent> me <c-o>:<c-u>call <SID>NetrwMarkFileEdit(0)<cr> - inoremap <buffer> <silent> mf <c-o>:<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> - inoremap <buffer> <silent> mg <c-o>:<c-u>call <SID>NetrwMarkFileGrep(0)<cr> - inoremap <buffer> <silent> mh <c-o>:<c-u>call <SID>NetrwMarkHideSfx(0)<cr> - inoremap <buffer> <silent> mm <c-o>:<c-u>call <SID>NetrwMarkFileMove(0)<cr> - inoremap <buffer> <silent> mp <c-o>:<c-u>call <SID>NetrwMarkFilePrint(0)<cr> - inoremap <buffer> <silent> mr <c-o>:<c-u>call <SID>NetrwMarkFileRegexp(0)<cr> - inoremap <buffer> <silent> ms <c-o>:<c-u>call <SID>NetrwMarkFileSource(0)<cr> - inoremap <buffer> <silent> mt <c-o>:<c-u>call <SID>NetrwMarkFileTgt(0)<cr> - inoremap <buffer> <silent> mT <c-o>:<c-u>call <SID>NetrwMarkFileTag(0)<cr> - inoremap <buffer> <silent> mu <c-o>:<c-u>call <SID>NetrwUnMarkFile(0)<cr> - nnoremap <buffer> <silent> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> - inoremap <buffer> <silent> mx <c-o>:<c-u>call <SID>NetrwMarkFileExe(0,0)<cr> - inoremap <buffer> <silent> mX <c-o>:<c-u>call <SID>NetrwMarkFileExe(0,1)<cr> - inoremap <buffer> <silent> mv <c-o>:<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr> - inoremap <buffer> <silent> mz <c-o>:<c-u>call <SID>NetrwMarkFileCompress(0)<cr> - inoremap <buffer> <silent> gb <c-o>:<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> - inoremap <buffer> <silent> gh <c-o>:<c-u>call <SID>NetrwHidden(0)<cr> - inoremap <buffer> <silent> gp <c-o>:<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr> - inoremap <buffer> <silent> C <c-o>:call <SID>NetrwSetChgwin()<cr> - inoremap <buffer> <silent> i <c-o>:call <SID>NetrwListStyle(0)<cr> - inoremap <buffer> <silent> I <c-o>:call <SID>NetrwBannerCtrl(1)<cr> - inoremap <buffer> <silent> o <c-o>:call <SID>NetrwSplit(0)<cr> - inoremap <buffer> <silent> O <c-o>:call <SID>NetrwObtain(0)<cr> - inoremap <buffer> <silent> p <c-o>:call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> - inoremap <buffer> <silent> P <c-o>:call <SID>NetrwPrevWinOpen(0)<cr> - inoremap <buffer> <silent> qb <c-o>:<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> - inoremap <buffer> <silent> mB <c-o>:<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> - inoremap <buffer> <silent> qf <c-o>:<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr> - inoremap <buffer> <silent> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr> - inoremap <buffer> <silent> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> - inoremap <buffer> <silent> s <c-o>:call <SID>NetrwSortStyle(0)<cr> - inoremap <buffer> <silent> S <c-o>:call <SID>NetSortSequence(0)<cr> - inoremap <buffer> <silent> t <c-o>:call <SID>NetrwSplit(1)<cr> - inoremap <buffer> <silent> Tb <c-o>:<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> - inoremap <buffer> <silent> Th <c-o>:<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> - inoremap <buffer> <silent> u <c-o>:<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr> - inoremap <buffer> <silent> U <c-o>:<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr> - inoremap <buffer> <silent> v <c-o>:call <SID>NetrwSplit(2)<cr> - inoremap <buffer> <silent> x <c-o>:call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()),1)<cr> - inoremap <buffer> <silent> % <c-o>:call <SID>NetrwOpenFile(0)<cr> + inoremap <buffer> <silent> <nowait> <cr> <c-o>:call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()))<cr> + inoremap <buffer> <silent> <nowait> <c-l> <c-o>:call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> + inoremap <buffer> <silent> <nowait> <s-cr> <c-o>:call <SID>TreeSqueezeDir(0)<cr> + inoremap <buffer> <silent> <nowait> - <c-o>:call <SID>NetrwBrowseUpDir(0)<cr> + inoremap <buffer> <silent> <nowait> a <c-o>:call <SID>NetrwHide(0)<cr> + inoremap <buffer> <silent> <nowait> mb <c-o>:<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> mc <c-o>:<c-u>call <SID>NetrwMarkFileCopy(0)<cr> + inoremap <buffer> <silent> <nowait> md <c-o>:<c-u>call <SID>NetrwMarkFileDiff(0)<cr> + inoremap <buffer> <silent> <nowait> me <c-o>:<c-u>call <SID>NetrwMarkFileEdit(0)<cr> + inoremap <buffer> <silent> <nowait> mf <c-o>:<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> + inoremap <buffer> <silent> <nowait> mg <c-o>:<c-u>call <SID>NetrwMarkFileGrep(0)<cr> + inoremap <buffer> <silent> <nowait> mh <c-o>:<c-u>call <SID>NetrwMarkHideSfx(0)<cr> + inoremap <buffer> <silent> <nowait> mm <c-o>:<c-u>call <SID>NetrwMarkFileMove(0)<cr> + inoremap <buffer> <silent> <nowait> mp <c-o>:<c-u>call <SID>NetrwMarkFilePrint(0)<cr> + inoremap <buffer> <silent> <nowait> mr <c-o>:<c-u>call <SID>NetrwMarkFileRegexp(0)<cr> + inoremap <buffer> <silent> <nowait> ms <c-o>:<c-u>call <SID>NetrwMarkFileSource(0)<cr> + inoremap <buffer> <silent> <nowait> mt <c-o>:<c-u>call <SID>NetrwMarkFileTgt(0)<cr> + inoremap <buffer> <silent> <nowait> mT <c-o>:<c-u>call <SID>NetrwMarkFileTag(0)<cr> + inoremap <buffer> <silent> <nowait> mu <c-o>:<c-u>call <SID>NetrwUnMarkFile(0)<cr> + nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> + inoremap <buffer> <silent> <nowait> mx <c-o>:<c-u>call <SID>NetrwMarkFileExe(0,0)<cr> + inoremap <buffer> <silent> <nowait> mX <c-o>:<c-u>call <SID>NetrwMarkFileExe(0,1)<cr> + inoremap <buffer> <silent> <nowait> mv <c-o>:<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr> + inoremap <buffer> <silent> <nowait> mz <c-o>:<c-u>call <SID>NetrwMarkFileCompress(0)<cr> + inoremap <buffer> <silent> <nowait> gb <c-o>:<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> gh <c-o>:<c-u>call <SID>NetrwHidden(0)<cr> + inoremap <buffer> <silent> <nowait> gp <c-o>:<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> C <c-o>:call <SID>NetrwSetChgwin()<cr> + inoremap <buffer> <silent> <nowait> i <c-o>:call <SID>NetrwListStyle(0)<cr> + inoremap <buffer> <silent> <nowait> I <c-o>:call <SID>NetrwBannerCtrl(1)<cr> + inoremap <buffer> <silent> <nowait> o <c-o>:call <SID>NetrwSplit(0)<cr> + inoremap <buffer> <silent> <nowait> O <c-o>:call <SID>NetrwObtain(0)<cr> + inoremap <buffer> <silent> <nowait> p <c-o>:call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> + inoremap <buffer> <silent> <nowait> P <c-o>:call <SID>NetrwPrevWinOpen(0)<cr> + inoremap <buffer> <silent> <nowait> qb <c-o>:<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> mB <c-o>:<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> qf <c-o>:<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr> + inoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr> + inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> + inoremap <buffer> <silent> <nowait> s <c-o>:call <SID>NetrwSortStyle(0)<cr> + inoremap <buffer> <silent> <nowait> S <c-o>:call <SID>NetSortSequence(0)<cr> + inoremap <buffer> <silent> <nowait> t <c-o>:call <SID>NetrwSplit(1)<cr> + inoremap <buffer> <silent> <nowait> Tb <c-o>:<c-u>call <SID>NetrwSetTgt('b',v:count1)<cr> + inoremap <buffer> <silent> <nowait> Th <c-o>:<c-u>call <SID>NetrwSetTgt('h',v:count)<cr> + inoremap <buffer> <silent> <nowait> u <c-o>:<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> U <c-o>:<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr> + inoremap <buffer> <silent> <nowait> v <c-o>:call <SID>NetrwSplit(2)<cr> + inoremap <buffer> <silent> <nowait> x <c-o>:call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()),1)<cr> + inoremap <buffer> <silent> <nowait> % <c-o>:call <SID>NetrwOpenFile(0)<cr> if !hasmapto('<Plug>NetrwHideEdit') nmap <buffer> <c-h> <Plug>NetrwHideEdit imap <buffer> <c-h> <Plug>NetrwHideEdit @@ -6125,44 +6213,45 @@ fun! s:NetrwMaps(islocal) nnoremap <buffer> <silent> <Plug>NetrwRefresh :call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> if g:netrw_mousemaps == 1 - nmap <leftmouse> <Plug>NetrwLeftmouse - nno <buffer> <silent> <Plug>NetrwLeftmouse <leftmouse>:call <SID>NetrwLeftmouse(0)<cr> - nmap <buffer> <leftdrag> <Plug>NetrwLeftdrag - nno <buffer> <silent> <Plug>NetrwLeftdrag :call <SID>NetrwLeftdrag(0)<cr> - nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse - nno <buffer> <silent> <Plug>NetrwSLeftmouse <leftmouse>:call <SID>NetrwSLeftmouse(0)<cr> - nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag - nno <buffer> <silent> <Plug>NetrwSLeftdrag <leftmouse>:call <SID>NetrwSLeftdrag(0)<cr> - nmap <middlemouse> <Plug>NetrwMiddlemouse - nno <buffer> <silent> <middlemouse> <Plug>NetrwMiddlemouse <leftmouse>:call <SID>NetrwPrevWinOpen(0)<cr> - nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse - nmap <buffer> <silent> <Plug>Netrw2Leftmouse - - imap <buffer> <leftmouse> <Plug>ILeftmouse - ino <buffer> <silent> <Plug>ILeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwLeftmouse(0)<cr> - imap <buffer> <middlemouse> <Plug>IMiddlemouse - ino <buffer> <silent> <Plug>IMiddlemouse <c-o><leftmouse><c-o>:call <SID>NetrwPrevWinOpen(0)<cr> - imap <buffer> <s-leftmouse> <Plug>ISLeftmouse - ino <buffer> <silent> <Plug>ISLeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> + nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse + nno <buffer> <silent> <Plug>NetrwLeftmouse <leftmouse>:call <SID>NetrwLeftmouse(0)<cr> + nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse + nno <buffer> <silent> <Plug>NetrwCLeftmouse <leftmouse>:call <SID>NetrwCLeftmouse(0)<cr> + nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse + nno <buffer> <silent> <Plug>NetrwSLeftmouse <leftmouse>:call <SID>NetrwSLeftmouse(0)<cr> + nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag + nno <buffer> <silent> <Plug>NetrwSLeftdrag <leftmouse>:call <SID>NetrwSLeftdrag(0)<cr> + nmap <middlemouse> <Plug>NetrwMiddlemouse + nno <buffer> <silent> <middlemouse> <Plug>NetrwMiddlemouse <leftmouse>:call <SID>NetrwPrevWinOpen(0)<cr> + nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse + nmap <buffer> <silent> <Plug>Netrw2Leftmouse - + imap <buffer> <leftmouse> <Plug>ILeftmouse + ino <buffer> <silent> <Plug>ILeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwLeftmouse(0)<cr> + imap <buffer> <middlemouse> <Plug>IMiddlemouse + ino <buffer> <silent> <Plug>IMiddlemouse <c-o><leftmouse><c-o>:call <SID>NetrwPrevWinOpen(0)<cr> + imap <buffer> <s-leftmouse> <Plug>ISLeftmouse + ino <buffer> <silent> <Plug>ISLeftmouse <c-o><leftmouse><c-o>:call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> exe 'nnoremap <buffer> <silent> <rightmouse> <leftmouse>:call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' exe 'vnoremap <buffer> <silent> <rightmouse> <leftmouse>:call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' exe 'inoremap <buffer> <silent> <rightmouse> <c-o><leftmouse><c-o>:call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' endif - exe 'nnoremap <buffer> <silent> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'nnoremap <buffer> <silent> d :call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>' - exe 'nnoremap <buffer> <silent> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'nnoremap <buffer> <silent> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'vnoremap <buffer> <silent> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'vnoremap <buffer> <silent> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'vnoremap <buffer> <silent> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'inoremap <buffer> <silent> <del> <c-o>:call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'inoremap <buffer> <silent> d <c-o>:call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>' - exe 'inoremap <buffer> <silent> D <c-o>:call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'inoremap <buffer> <silent> R <c-o>:call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> <del> <c-o>:call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> d <c-o>:call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> D <c-o>:call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'inoremap <buffer> <silent> <nowait> R <c-o>:call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' nnoremap <buffer> <F1> :he netrw-quickhelp<cr> inoremap <buffer> <F1> <c-o>:he netrw-quickhelp<cr> - endif - NetrwKeepj call s:SetRexDir(a:islocal,b:netrw_curdir) + " support user-specified maps + call netrw#UserMaps(0) + endif " call Dret("s:NetrwMaps") endfun @@ -6184,9 +6273,6 @@ fun! s:NetrwCommands(islocal) com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(0,<f-args>) endif com! -buffer -nargs=? -complete=file MT call s:NetrwMarkTarget(<q-args>) - " the following two commands are intended to be used for testing only, so I'm not advertising them in the manual - com! -buffer -nargs=0 CRL call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord())) - com! -buffer -nargs=0 CRR call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord())) " call Dret("s:NetrwCommands") endfun @@ -6196,16 +6282,21 @@ endfun " glob()ing only works with local files fun! s:NetrwMarkFiles(islocal,...) " call Dfunc("s:NetrwMarkFiles(islocal=".a:islocal."...) a:0=".a:0) - let i = 1 + let curdir = s:NetrwGetCurdir(a:islocal) + let i = 1 while i <= a:0 if a:islocal - let mffiles= glob(a:{i},0,1) + if v:version == 704 && has("patch656") + let mffiles= glob(a:{i},0,1,1) + else + let mffiles= glob(a:{i},0,1) + endif else let mffiles= [a:{i}] endif -" call Decho("mffiles".string(mffiles)) +" call Decho("mffiles".string(mffiles),'~'.expand("<slnum>")) for mffile in mffiles -" call Decho("mffile<".mffile.">") +" call Decho("mffile<".mffile.">",'~'.expand("<slnum>")) call s:NetrwMarkFile(a:islocal,mffile) endfor let i= i + 1 @@ -6214,18 +6305,20 @@ fun! s:NetrwMarkFiles(islocal,...) endfun " --------------------------------------------------------------------- -" s:NetrwMarkTarget: {{{2 +" s:NetrwMarkTarget: implements :MT (mark target) {{{2 fun! s:NetrwMarkTarget(...) " call Dfunc("s:NetrwMarkTarget() a:0=".a:0) if a:0 == 0 || (a:0 == 1 && a:1 == "") - let tgt= b:netrw_curdir + let curdir = s:NetrwGetCurdir(1) + let tgt = b:netrw_curdir else - let tgt= a:1 + let curdir = s:NetrwGetCurdir((a:1 =~ '^\a\{3,}://')? 0 : 1) + let tgt = a:1 endif -" call Decho("tgt<".tgt.">") +" call Decho("tgt<".tgt.">",'~'.expand("<slnum>")) let s:netrwmftgt = tgt - let s:netrwmftgt_islocal = tgt !~ '^\a\+://' - let curislocal = b:netrw_curdir !~ '^\a\+://' + let s:netrwmftgt_islocal = tgt !~ '^\a\{3,}://' + let curislocal = b:netrw_curdir !~ '^\a\{3,}://' let svpos = netrw#SavePosn() call s:NetrwRefresh(curislocal,s:NetrwBrowseChgDir(curislocal,'./')) call netrw#RestorePosn(svpos) @@ -6251,16 +6344,17 @@ endfun " b:netrw_islocal fun! s:NetrwMarkFile(islocal,fname) " call Dfunc("s:NetrwMarkFile(islocal=".a:islocal." fname<".a:fname.">)") +" call Decho("bufnr(%)=".bufnr("%").": ".bufname("%"),'~'.expand("<slnum>")) " sanity check if empty(a:fname) " call Dret("s:NetrwMarkFile : emtpy fname") return endif + let curdir = s:NetrwGetCurdir(a:islocal) let ykeep = @@ let curbufnr= bufnr("%") - let curdir = b:netrw_curdir if a:fname =~ '^\a' let leader= '\<' else @@ -6272,29 +6366,30 @@ fun! s:NetrwMarkFile(islocal,fname) let trailer = '[@=|\/\*]\=\ze\%( \|\t\|$\)' endif - if exists("s:netrwmarkfilelist_{curbufnr}") + if exists("s:netrwmarkfilelist_".curbufnr) " markfile list pre-exists -" call Decho("starting s:netrwmarkfilelist_{curbufnr}<".string(s:netrwmarkfilelist_{curbufnr}).">") -" call Decho("starting s:netrwmarkfilemtch_{curbufnr}<".s:netrwmarkfilemtch_{curbufnr}.">") +" call Decho("case s:netrwmarkfilelist_".curbufnr." already exists",'~'.expand("<slnum>")) +" call Decho("starting s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) +" call Decho("starting s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>")) let b:netrw_islocal= a:islocal if index(s:netrwmarkfilelist_{curbufnr},a:fname) == -1 " append filename to buffer's markfilelist -" call Decho("append filename<".a:fname."> to local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">") +" call Decho("append filename<".a:fname."> to local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) call add(s:netrwmarkfilelist_{curbufnr},a:fname) let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(a:fname,g:netrw_markfileesc).trailer else " remove filename from buffer's markfilelist -" call Decho("remove filename<".a:fname."> from local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">") +" call Decho("remove filename<".a:fname."> from local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) call filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname') if s:netrwmarkfilelist_{curbufnr} == [] " local markfilelist is empty; remove it entirely -" call Decho("markfile list now empty") +" call Decho("markfile list now empty",'~'.expand("<slnum>")) call s:NetrwUnmarkList(curbufnr,curdir) else " rebuild match list to display markings correctly -" call Decho("rebuild s:netrwmarkfilemtch_".curbufnr) +" call Decho("rebuild s:netrwmarkfilemtch_".curbufnr,'~'.expand("<slnum>")) let s:netrwmarkfilemtch_{curbufnr}= "" let first = 1 for fname in s:netrwmarkfilelist_{curbufnr} @@ -6305,17 +6400,18 @@ fun! s:NetrwMarkFile(islocal,fname) endif let first= 0 endfor -" call Decho("ending s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">") +" call Decho("ending s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) endif endif else " initialize new markfilelist +" call Decho("case: initialize new markfilelist",'~'.expand("<slnum>")) -" call Decho("add fname<".a:fname."> to new markfilelist_".curbufnr) +" call Decho("add fname<".a:fname."> to new markfilelist_".curbufnr,'~'.expand("<slnum>")) let s:netrwmarkfilelist_{curbufnr}= [] - call add(s:netrwmarkfilelist_{curbufnr},a:fname) -" call Decho("ending s:netrwmarkfilelist_{curbufnr}<".string(s:netrwmarkfilelist_{curbufnr}).">") + call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','','')) +" call Decho("ending s:netrwmarkfilelist_{curbufnr}<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) " build initial markfile matching pattern if a:fname =~ '/$' @@ -6323,7 +6419,7 @@ fun! s:NetrwMarkFile(islocal,fname) else let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc).trailer endif -" call Decho("ending s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">") +" call Decho("ending s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>")) endif " handle global markfilelist @@ -6332,12 +6428,12 @@ fun! s:NetrwMarkFile(islocal,fname) if index(s:netrwmarkfilelist,dname) == -1 " append new filename to global markfilelist call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname)) -" call Decho("append filename<".a:fname."> to global markfilelist<".string(s:netrwmarkfilelist).">") +" call Decho("append filename<".a:fname."> to global markfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) else " remove new filename from global markfilelist -" call Decho("filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")") +" call Decho("filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")",'~'.expand("<slnum>")) call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"') -" call Decho("ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">") +" call Decho("ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) if s:netrwmarkfilelist == [] unlet s:netrwmarkfilelist endif @@ -6346,17 +6442,17 @@ fun! s:NetrwMarkFile(islocal,fname) " initialize new global-directory markfilelist let s:netrwmarkfilelist= [] call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname)) -" call Decho("init s:netrwmarkfilelist<".string(s:netrwmarkfilelist).">") +" call Decho("init s:netrwmarkfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) endif - " set up 2match'ing to netrwmarkfilemtch list + " set up 2match'ing to netrwmarkfilemtch_# list if exists("s:netrwmarkfilemtch_{curbufnr}") && s:netrwmarkfilemtch_{curbufnr} != "" -" call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/") +" call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/",'~'.expand("<slnum>")) if exists("g:did_drchip_netrwlist_syntax") exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/" endif else -" call Decho("2match none") +" call Decho("2match none",'~'.expand("<slnum>")) 2match none endif let @@= ykeep @@ -6374,7 +6470,7 @@ endfun fun! s:NetrwMarkFileCompress(islocal) " call Dfunc("s:NetrwMarkFileCompress(islocal=".a:islocal.")") let svpos = netrw#SavePosn() - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let curbufnr = bufnr("%") " sanity check @@ -6383,25 +6479,25 @@ fun! s:NetrwMarkFileCompress(islocal) " call Dret("s:NetrwMarkFileCompress") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist_{curbufnr}") && exists("g:netrw_compress") && exists("g:netrw_decompress") " for every filename in the marked list for fname in s:netrwmarkfilelist_{curbufnr} let sfx= substitute(fname,'^.\{-}\(\.\a\+\)$','\1','') -" call Decho("extracted sfx<".sfx.">") +" call Decho("extracted sfx<".sfx.">",'~'.expand("<slnum>")) if exists("g:netrw_decompress['".sfx."']") " fname has a suffix indicating that its compressed; apply associated decompression routine let exe= g:netrw_decompress[sfx] -" call Decho("fname<".fname."> is compressed so decompress with <".exe.">") +" call Decho("fname<".fname."> is compressed so decompress with <".exe.">",'~'.expand("<slnum>")) let exe= netrw#WinPath(exe) if a:islocal if g:netrw_keepdir - let fname= shellescape(s:ComposePath(curdir,fname)) + let fname= s:ShellEscape(s:ComposePath(curdir,fname)) endif else - let fname= shellescape(b:netrw_curdir.fname,1) + let fname= s:ShellEscape(b:netrw_curdir.fname,1) endif if executable(exe) if a:islocal @@ -6419,10 +6515,10 @@ fun! s:NetrwMarkFileCompress(islocal) unlet exe elseif a:islocal " fname not a compressed file, so compress it - call system(netrw#WinPath(g:netrw_compress)." ".shellescape(s:ComposePath(b:netrw_curdir,fname))) + call system(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,fname))) else " fname not a compressed file, so compress it - NetrwKeepj call s:RemoteSystem(netrw#WinPath(g:netrw_compress)." ".shellescape(fname)) + NetrwKeepj call s:RemoteSystem(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(fname)) endif endfor " for every file in the marked list @@ -6443,11 +6539,7 @@ endfun fun! s:NetrwMarkFileCopy(islocal,...) " call Dfunc("s:NetrwMarkFileCopy(islocal=".a:islocal.") target<".(exists("s:netrwmftgt")? s:netrwmftgt : '---')."> a:0=".a:0) - if !exists("b:netrw_curdir") - let b:netrw_curdir= getcwd() -" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used getcwd)") - endif - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let curbufnr = bufnr("%") " sanity check @@ -6456,18 +6548,18 @@ fun! s:NetrwMarkFileCopy(islocal,...) " call Dret("s:NetrwMarkFileCopy") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if !exists("s:netrwmftgt") NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your marked file target is empty! (:help netrw-mt)",67) " call Dret("s:NetrwMarkFileCopy 0") return 0 endif -" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">") +" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) if a:islocal && s:netrwmftgt_islocal " Copy marked files, local directory to local directory -" call Decho("copy from local to local") +" call Decho("copy from local to local",'~'.expand("<slnum>")) if !executable(g:netrw_localcopycmd) && g:netrw_localcopycmd !~ '^'.expand("$COMSPEC").'\s' call netrw#ErrorMsg(s:ERROR,"g:netrw_localcopycmd<".g:netrw_localcopycmd."> not executable on your system, aborting",91) " call Dfunc("s:NetrwMarkFileMove : g:netrw_localcopycmd<".g:netrw_localcopycmd."> n/a!") @@ -6478,17 +6570,17 @@ fun! s:NetrwMarkFileCopy(islocal,...) if simplify(s:netrwmftgt) == simplify(b:netrw_curdir) if len(s:netrwmarkfilelist_{bufnr('%')}) == 1 " only one marked file -" call Decho("case: only one marked file") - let args = shellescape(b:netrw_curdir."/".s:netrwmarkfilelist_{bufnr('%')}[0]) +" call Decho("case: only one marked file",'~'.expand("<slnum>")) + let args = s:ShellEscape(b:netrw_curdir."/".s:netrwmarkfilelist_{bufnr('%')}[0]) let oldname = s:netrwmarkfilelist_{bufnr('%')}[0] elseif a:0 == 1 -" call Decho("case: handling one input argument") +" call Decho("case: handling one input argument",'~'.expand("<slnum>")) " this happens when the next case was used to recursively call s:NetrwMarkFileCopy() - let args = shellescape(b:netrw_curdir."/".a:1) + let args = s:ShellEscape(b:netrw_curdir."/".a:1) let oldname = a:1 else " copy multiple marked files inside the same directory -" call Decho("case: handling a multiple marked files") +" call Decho("case: handling a multiple marked files",'~'.expand("<slnum>")) let s:recursive= 1 for oldname in s:netrwmarkfilelist_{bufnr("%")} let ret= s:NetrwMarkFileCopy(a:islocal,oldname) @@ -6509,11 +6601,11 @@ fun! s:NetrwMarkFileCopy(islocal,...) " call Dret("s:NetrwMarkFileCopy 0") return 0 endif - let args= shellescape(oldname) - let tgt = shellescape(s:netrwmftgt.'/'.newname) + let args= s:ShellEscape(oldname) + let tgt = s:ShellEscape(s:netrwmftgt.'/'.newname) else - let args= join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"shellescape(b:netrw_curdir.\"/\".v:val)")) - let tgt = shellescape(s:netrwmftgt) + let args= join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"s:ShellEscape(b:netrw_curdir.\"/\".v:val)")) + let tgt = s:ShellEscape(s:netrwmftgt) endif if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) let args= substitute(args,'/','\\','g') @@ -6521,16 +6613,17 @@ fun! s:NetrwMarkFileCopy(islocal,...) endif if args =~ "'"|let args= substitute(args,"'\\(.*\\)'",'\1','')|endif if tgt =~ "'"|let tgt = substitute(tgt,"'\\(.*\\)'",'\1','') |endif - if isdirectory(args) -" call Decho("args<".args."> is a directory") + if args =~ '//$'|let args= substitute(args,'//$','/','')|endif + if isdirectory(s:NetrwFile(args)) +" call Decho("args<".args."> is a directory",'~'.expand("<slnum>")) let copycmd= g:netrw_localcopydircmd -" call Decho("using copydircmd<".copycmd.">") +" call Decho("using copydircmd<".copycmd.">",'~'.expand("<slnum>")) if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) " window's xcopy doesn't copy a directory to a target properly. Instead, it copies a directory's " contents to a target. One must append the source directory name to the target to get xcopy to " do the right thing. let tgt= tgt.'\'.substitute(a:1,'^.*[\\/]','','') -" call Decho("modified tgt for xcopy") +" call Decho("modified tgt for xcopy",'~'.expand("<slnum>")) endif else let copycmd= g:netrw_localcopycmd @@ -6542,30 +6635,34 @@ fun! s:NetrwMarkFileCopy(islocal,...) else let copycmd = netrw#WinPath(copycmd) endif -" call Decho("args <".args.">") -" call Decho("tgt <".tgt.">") -" call Decho("copycmd<".copycmd.">") -" call Decho("system(".copycmd." '".args."' '".tgt."')") +" call Decho("args <".args.">",'~'.expand("<slnum>")) +" call Decho("tgt <".tgt.">",'~'.expand("<slnum>")) +" call Decho("copycmd<".copycmd.">",'~'.expand("<slnum>")) +" call Decho("system(".copycmd." '".args."' '".tgt."')",'~'.expand("<slnum>")) call system(copycmd." '".args."' '".tgt."'") if v:shell_error != 0 - call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localcopycmd<".g:netrw_localcopycmd.">; it doesn't work!",80) -" call Dret("s:NetrwMarkFileCopy 0 : failed: system(".g:netrw_localcopycmd." ".args." ".shellescape(s:netrwmftgt)) + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir + call netrw#ErrorMsg(s:ERROR,"copy failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-c)",101) + else + call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localcopycmd<".g:netrw_localcopycmd.">; it doesn't work!",80) + endif +" call Dret("s:NetrwMarkFileCopy 0 : failed: system(".g:netrw_localcopycmd." ".args." ".s:ShellEscape(s:netrwmftgt)) return 0 endif elseif a:islocal && !s:netrwmftgt_islocal " Copy marked files, local directory to remote directory -" call Decho("copy from local to remote") +" call Decho("copy from local to remote",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt) elseif !a:islocal && s:netrwmftgt_islocal " Copy marked files, remote directory to local directory -" call Decho("copy from remote to local") +" call Decho("copy from remote to local",'~'.expand("<slnum>")) NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt) elseif !a:islocal && !s:netrwmftgt_islocal " Copy marked files, remote directory to remote directory -" call Decho("copy from remote to remote") +" call Decho("copy from remote to remote",'~'.expand("<slnum>")) let curdir = getcwd() let tmpdir = s:GetTempfile("") if tmpdir !~ '/' @@ -6574,14 +6671,14 @@ fun! s:NetrwMarkFileCopy(islocal,...) if exists("*mkdir") call mkdir(tmpdir) else - call s:NetrwExe("sil! !".g:netrw_localmkdir.' '.shellescape(tmpdir,1)) + call s:NetrwExe("sil! !".g:netrw_localmkdir.' '.s:ShellEscape(tmpdir,1)) if v:shell_error != 0 call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80) -" call Dret("s:NetrwMarkFileCopy : failed: sil! !".g:netrw_localmkdir.' '.shellescape(tmpdir,1) ) +" call Dret("s:NetrwMarkFileCopy : failed: sil! !".g:netrw_localmkdir.' '.s:ShellEscape(tmpdir,1) ) return endif endif - if isdirectory(tmpdir) + if isdirectory(s:NetrwFile(tmpdir)) call s:NetrwLcd(tmpdir) NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},tmpdir) let localfiles= map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),'substitute(v:val,"^.*/","","")') @@ -6591,10 +6688,10 @@ fun! s:NetrwMarkFileCopy(islocal,...) NetrwKeepj call s:NetrwDelete(fname) endfor call s:NetrwLcd(curdir) - call s:NetrwExe("sil !".g:netrw_localrmdir." ".shellescape(tmpdir,1)) + call s:NetrwExe("sil !".g:netrw_localrmdir." ".s:ShellEscape(tmpdir,1)) if v:shell_error != 0 call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localrmdir<".g:netrw_localrmdir."> to something that works",80) -" call Dret("s:NetrwMarkFileCopy : failed: sil !".g:netrw_localrmdir." ".shellescape(tmpdir,1) ) +" call Dret("s:NetrwMarkFileCopy : failed: sil !".g:netrw_localrmdir." ".s:ShellEscape(tmpdir,1) ) return endif else @@ -6606,7 +6703,7 @@ fun! s:NetrwMarkFileCopy(islocal,...) " ------- " cleanup " ------- -" call Decho("cleanup") +" call Decho("cleanup",'~'.expand("<slnum>")) if !exists("s:recursive") " remove markings from local buffer call s:NetrwUnmarkList(curbufnr,curdir) @@ -6622,7 +6719,7 @@ fun! s:NetrwMarkFileCopy(islocal,...) if g:netrw_fastbrowse <= 1 NetrwKeepj call s:LocalBrowseRefresh() endif - + " call Dret("s:NetrwMarkFileCopy 1") return 1 endfun @@ -6642,21 +6739,21 @@ fun! s:NetrwMarkFileDiff(islocal) " call Dret("s:NetrwMarkFileDiff") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) + let curdir= s:NetrwGetCurdir(a:islocal) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist_{".curbufnr."}") let cnt = 0 - let curdir = b:netrw_curdir for fname in s:netrwmarkfilelist let cnt= cnt + 1 if cnt == 1 -" call Decho("diffthis: fname<".fname.">") +" call Decho("diffthis: fname<".fname.">",'~'.expand("<slnum>")) exe "NetrwKeepj e ".fnameescape(fname) diffthis elseif cnt == 2 || cnt == 3 vsplit wincmd l -" call Decho("diffthis: ".fname) +" call Decho("diffthis: ".fname,'~'.expand("<slnum>")) exe "NetrwKeepj e ".fnameescape(fname) diffthis else @@ -6675,7 +6772,7 @@ endfun fun! s:NetrwMarkFileEdit(islocal) " call Dfunc("s:NetrwMarkFileEdit(islocal=".a:islocal.")") - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let curbufnr = bufnr("%") " sanity check @@ -6684,7 +6781,7 @@ fun! s:NetrwMarkFileEdit(islocal) " call Dret("s:NetrwMarkFileEdit") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist_{curbufnr}") call s:SetRexDir(a:islocal,curdir) @@ -6692,11 +6789,11 @@ fun! s:NetrwMarkFileEdit(islocal) " unmark markedfile list " call s:NetrwUnmarkList(curbufnr,curdir) call s:NetrwUnmarkAll() -" call Decho("exe sil args ".flist) +" call Decho("exe sil args ".flist,'~'.expand("<slnum>")) exe "sil args ".flist endif echo "(use :bn, :bp to navigate files; :Rex to return)" - + " call Dret("s:NetrwMarkFileEdit") endfun @@ -6710,17 +6807,17 @@ fun! s:NetrwMarkFileQFEL(islocal,qfel) if !empty(a:qfel) for entry in a:qfel let bufnmbr= entry["bufnr"] -" call Decho("bufname(".bufnmbr.")<".bufname(bufnmbr)."> line#".entry["lnum"]." text=".entry["text"]) +" call Decho("bufname(".bufnmbr.")<".bufname(bufnmbr)."> line#".entry["lnum"]." text=".entry["text"],'~'.expand("<slnum>")) if !exists("s:netrwmarkfilelist_{curbufnr}") -" call Decho("case: no marked file list") +" call Decho("case: no marked file list",'~'.expand("<slnum>")) call s:NetrwMarkFile(a:islocal,bufname(bufnmbr)) elseif index(s:netrwmarkfilelist_{curbufnr},bufname(bufnmbr)) == -1 " s:NetrwMarkFile will remove duplicate entries from the marked file list. " So, this test lets two or more hits on the same pattern to be ignored. -" call Decho("case: ".bufname(bufnmbr)." not currently in marked file list") +" call Decho("case: ".bufname(bufnmbr)." not currently in marked file list",'~'.expand("<slnum>")) call s:NetrwMarkFile(a:islocal,bufname(bufnmbr)) else -" call Decho("case: ".bufname(bufnmbr)." already in marked file list") +" call Decho("case: ".bufname(bufnmbr)." already in marked file list",'~'.expand("<slnum>")) endif endfor echo "(use me to edit marked files)" @@ -6738,7 +6835,7 @@ endfun fun! s:NetrwMarkFileExe(islocal,enbloc) " call Dfunc("s:NetrwMarkFileExe(islocal=".a:islocal.",enbloc=".a:enbloc.")") let svpos = netrw#SavePosn() - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let curbufnr = bufnr("%") if a:enbloc == 0 @@ -6749,14 +6846,14 @@ fun! s:NetrwMarkFileExe(islocal,enbloc) " call Dret("s:NetrwMarkFileExe") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist_{curbufnr}") " get the command call inputsave() let cmd= input("Enter command: ","","file") call inputrestore() -" call Decho("cmd<".cmd.">") +" call Decho("cmd<".cmd.">",'~'.expand("<slnum>")) if cmd == "" " call Dret("s:NetrwMarkFileExe : early exit, empty command") return @@ -6767,10 +6864,10 @@ fun! s:NetrwMarkFileExe(islocal,enbloc) for fname in s:netrwmarkfilelist_{curbufnr} if a:islocal if g:netrw_keepdir - let fname= shellescape(netrw#WinPath(s:ComposePath(curdir,fname))) + let fname= s:ShellEscape(netrw#WinPath(s:ComposePath(curdir,fname))) endif else - let fname= shellescape(netrw#WinPath(b:netrw_curdir.fname)) + let fname= s:ShellEscape(netrw#WinPath(b:netrw_curdir.fname)) endif if cmd =~ '%' let xcmd= substitute(cmd,'%',fname,'g') @@ -6778,10 +6875,10 @@ fun! s:NetrwMarkFileExe(islocal,enbloc) let xcmd= cmd.' '.fname endif if a:islocal -" call Decho("local: xcmd<".xcmd.">") +" call Decho("local: xcmd<".xcmd.">",'~'.expand("<slnum>")) let ret= system(xcmd) else -" call Decho("remote: xcmd<".xcmd.">") +" call Decho("remote: xcmd<".xcmd.">",'~'.expand("<slnum>")) let ret= s:RemoteSystem(xcmd) endif if v:shell_error < 0 @@ -6807,15 +6904,15 @@ fun! s:NetrwMarkFileExe(islocal,enbloc) call inputsave() let cmd= input("Enter command: ","","file") call inputrestore() -" call Decho("cmd<".cmd.">") +" call Decho("cmd<".cmd.">",'~'.expand("<slnum>")) if cmd == "" " call Dret("s:NetrwMarkFileExe : early exit, empty command") return endif if cmd =~ '%' - let cmd= substitute(cmd,'%',join(map(s:netrwmarkfilelist,'shellescape(v:val)'),' '),'g') + let cmd= substitute(cmd,'%',join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' '),'g') else - let cmd= cmd.' '.join(map(s:netrwmarkfilelist,'shellescape(v:val)'),' ') + let cmd= cmd.' '.join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' ') endif if a:islocal call system(cmd) @@ -6832,7 +6929,7 @@ fun! s:NetrwMarkFileExe(islocal,enbloc) NetrwKeepj call netrw#RestorePosn(svpos) endif - + " call Dret("s:NetrwMarkFileExe") endfun @@ -6849,7 +6946,7 @@ fun! s:NetrwMarkHideSfx(islocal) if exists("s:netrwmarkfilelist_{curbufnr}") for fname in s:netrwmarkfilelist_{curbufnr} -" call Decho("s:NetrwMarkFileCopy: fname<".fname.">") +" call Decho("s:NetrwMarkFileCopy: fname<".fname.">",'~'.expand("<slnum>")) " construct suffix pattern if fname =~ '\.' let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','') @@ -6869,7 +6966,7 @@ fun! s:NetrwMarkHideSfx(islocal) let itemnum= itemnum + 1 endfor endif -" call Decho("fname<".fname."> inhidelist=".inhidelist." sfxpat<".sfxpat.">") +" call Decho("fname<".fname."> inhidelist=".inhidelist." sfxpat<".sfxpat.">",'~'.expand("<slnum>")) if inhidelist " remove sfxpat from list call remove(hidelist,itemnum) @@ -6899,7 +6996,7 @@ endfun fun! s:NetrwMarkFileVimCmd(islocal) " call Dfunc("s:NetrwMarkFileVimCmd(islocal=".a:islocal.")") let svpos = netrw#SavePosn() - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let curbufnr = bufnr("%") " sanity check @@ -6908,14 +7005,14 @@ fun! s:NetrwMarkFileVimCmd(islocal) " call Dret("s:NetrwMarkFileVimCmd") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist_{curbufnr}") " get the command call inputsave() let cmd= input("Enter vim command: ","","file") call inputrestore() -" call Decho("cmd<".cmd.">") +" call Decho("cmd<".cmd.">",'~'.expand("<slnum>")) if cmd == "" " " call Dret("s:NetrwMarkFileVimCmd : early exit, empty command") return @@ -6924,15 +7021,15 @@ fun! s:NetrwMarkFileVimCmd(islocal) " apply command to marked files. Substitute: filename -> % " If no %, then append a space and the filename to the command for fname in s:netrwmarkfilelist_{curbufnr} -" call Decho("fname<".fname.">") +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) if a:islocal 1split exe "sil! NetrwKeepj keepalt e ".fnameescape(fname) -" call Decho("local<".fname.">: exe ".cmd) +" call Decho("local<".fname.">: exe ".cmd,'~'.expand("<slnum>")) exe cmd exe "sil! keepalt wq!" else -" call Decho("remote<".fname.">: exe ".cmd." : NOT SUPPORTED YET") +" call Decho("remote<".fname.">: exe ".cmd." : NOT SUPPORTED YET",'~'.expand("<slnum>")) echo "sorry, \"mv\" not supported yet for remote files" endif endfor @@ -6946,7 +7043,7 @@ fun! s:NetrwMarkFileVimCmd(islocal) else NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) endif - + " call Dret("s:NetrwMarkFileVimCmd") endfun @@ -6963,7 +7060,7 @@ fun! s:NetrwMarkHideSfx(islocal) if exists("s:netrwmarkfilelist_{curbufnr}") for fname in s:netrwmarkfilelist_{curbufnr} -" call Decho("s:NetrwMarkFileCopy: fname<".fname.">") +" call Decho("s:NetrwMarkFileCopy: fname<".fname.">",'~'.expand("<slnum>")) " construct suffix pattern if fname =~ '\.' let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','') @@ -6983,7 +7080,7 @@ fun! s:NetrwMarkHideSfx(islocal) let itemnum= itemnum + 1 endfor endif -" call Decho("fname<".fname."> inhidelist=".inhidelist." sfxpat<".sfxpat.">") +" call Decho("fname<".fname."> inhidelist=".inhidelist." sfxpat<".sfxpat.">",'~'.expand("<slnum>")) if inhidelist " remove sfxpat from list call remove(hidelist,itemnum) @@ -7014,13 +7111,14 @@ fun! s:NetrwMarkFileGrep(islocal) " call Dfunc("s:NetrwMarkFileGrep(islocal=".a:islocal.")") let svpos = netrw#SavePosn() let curbufnr = bufnr("%") + let curdir = s:NetrwGetCurdir(a:islocal) if exists("s:netrwmarkfilelist") -" call Decho("s:netrwmarkfilelist".string(s:netrwmarkfilelist).">") +" call Decho("s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)")) call s:NetrwUnmarkAll() else -" call Decho('no marked files, using "*"') +" call Decho('no marked files, using "*"','~'.expand("<slnum>")) let netrwmarkfilelist= "*" endif @@ -7031,7 +7129,7 @@ fun! s:NetrwMarkFileGrep(islocal) let patbang = "" if pat =~ '^!' let patbang = "!" - let pat= strpart(pat,2) + let pat = strpart(pat,2) endif if pat =~ '^\i' let pat = escape(pat,'/') @@ -7041,7 +7139,7 @@ fun! s:NetrwMarkFileGrep(islocal) endif " use vimgrep for both local and remote -" call Decho("exe vimgrep".patbang." ".pat." ".netrwmarkfilelist) +" call Decho("exe vimgrep".patbang." ".pat." ".netrwmarkfilelist,'~'.expand("<slnum>")) try exe "NetrwKeepj noautocmd vimgrep".patbang." ".pat." ".netrwmarkfilelist catch /^Vim\%((\a\+)\)\=:E480/ @@ -7056,7 +7154,7 @@ fun! s:NetrwMarkFileGrep(islocal) if exists("nonisi") " original, user-supplied pattern did not begin with a character from isident -" call Decho("looking for trailing nonisi<".nonisi."> followed by a j, gj, or jg") +" call Decho("looking for trailing nonisi<".nonisi."> followed by a j, gj, or jg",'~'.expand("<slnum>")) if pat =~ nonisi.'j$\|'.nonisi.'gj$\|'.nonisi.'jg$' call s:NetrwMarkFileQFEL(a:islocal,getqflist()) endif @@ -7072,7 +7170,7 @@ endfun " = 1: target directory is local fun! s:NetrwMarkFileMove(islocal) " call Dfunc("s:NetrwMarkFileMove(islocal=".a:islocal.")") - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let curbufnr = bufnr("%") " sanity check @@ -7081,61 +7179,65 @@ fun! s:NetrwMarkFileMove(islocal) " call Dret("s:NetrwMarkFileMove") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if !exists("s:netrwmftgt") NetrwKeepj call netrw#ErrorMsg(2,"your marked file target is empty! (:help netrw-mt)",67) " call Dret("s:NetrwMarkFileCopy 0") return 0 endif -" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">") +" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) if a:islocal && s:netrwmftgt_islocal " move: local -> local -" call Decho("move from local to local") -" call Decho("local to local move") +" call Decho("move from local to local",'~'.expand("<slnum>")) +" call Decho("local to local move",'~'.expand("<slnum>")) if !executable(g:netrw_localmovecmd) && g:netrw_localmovecmd !~ '^'.expand("$COMSPEC").'\s' call netrw#ErrorMsg(s:ERROR,"g:netrw_localmovecmd<".g:netrw_localmovecmd."> not executable on your system, aborting",90) " call Dfunc("s:NetrwMarkFileMove : g:netrw_localmovecmd<".g:netrw_localmovecmd."> n/a!") return endif - let tgt = shellescape(s:netrwmftgt) -" call Decho("tgt<".tgt.">") + let tgt = s:ShellEscape(s:netrwmftgt) +" call Decho("tgt<".tgt.">",'~'.expand("<slnum>")) if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) let tgt = substitute(tgt, '/','\\','g') -" call Decho("windows exception: tgt<".tgt.">") +" call Decho("windows exception: tgt<".tgt.">",'~'.expand("<slnum>")) if g:netrw_localmovecmd =~ '\s' let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','') let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','') let movecmd = netrw#WinPath(movecmd).movecmdargs -" call Decho("windows exception: movecmd<".movecmd."> (#1: had a space)") +" call Decho("windows exception: movecmd<".movecmd."> (#1: had a space)",'~'.expand("<slnum>")) else let movecmd = netrw#WinPath(movecmd) -" call Decho("windows exception: movecmd<".movecmd."> (#2: no space)") +" call Decho("windows exception: movecmd<".movecmd."> (#2: no space)",'~'.expand("<slnum>")) endif else let movecmd = netrw#WinPath(g:netrw_localmovecmd) -" call Decho("movecmd<".movecmd."> (#3 linux or cygwin)") +" call Decho("movecmd<".movecmd."> (#3 linux or cygwin)",'~'.expand("<slnum>")) endif for fname in s:netrwmarkfilelist_{bufnr("%")} -" call Decho("system(".movecmd." ".shellescape(fname)." ".tgt.")") if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) let fname= substitute(fname,'/','\\','g') endif - let ret= system(g:netrw_localmovecmd." ".shellescape(fname)." ".tgt) +" call Decho("system(".movecmd." ".s:ShellEscape(fname)." ".tgt.")",'~'.expand("<slnum>")) + let ret= system(movecmd." ".s:ShellEscape(fname)." ".tgt) if v:shell_error != 0 - call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localmovecmd<".g:netrw_localmovecmd.">; it doesn't work!",54) + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir + call netrw#ErrorMsg(s:ERROR,"move failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-c)",100) + else + call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localmovecmd<".g:netrw_localmovecmd.">; it doesn't work!",54) + endif break endif endfor elseif a:islocal && !s:netrwmftgt_islocal " move: local -> remote -" call Decho("move from local to remote") -" call Decho("copy") +" call Decho("move from local to remote",'~'.expand("<slnum>")) +" call Decho("copy",'~'.expand("<slnum>")) let mflist= s:netrwmarkfilelist_{bufnr("%")} NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) -" call Decho("remove") +" call Decho("remove",'~'.expand("<slnum>")) for fname in mflist let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') let ok = s:NetrwLocalRmFile(b:netrw_curdir,barefname,1) @@ -7144,11 +7246,11 @@ fun! s:NetrwMarkFileMove(islocal) elseif !a:islocal && s:netrwmftgt_islocal " move: remote -> local -" call Decho("move from remote to local") -" call Decho("copy") +" call Decho("move from remote to local",'~'.expand("<slnum>")) +" call Decho("copy",'~'.expand("<slnum>")) let mflist= s:netrwmarkfilelist_{bufnr("%")} NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) -" call Decho("remove") +" call Decho("remove",'~'.expand("<slnum>")) for fname in mflist let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1) @@ -7157,11 +7259,11 @@ fun! s:NetrwMarkFileMove(islocal) elseif !a:islocal && !s:netrwmftgt_islocal " move: remote -> remote -" call Decho("move from remote to remote") -" call Decho("copy") +" call Decho("move from remote to remote",'~'.expand("<slnum>")) +" call Decho("copy",'~'.expand("<slnum>")) let mflist= s:netrwmarkfilelist_{bufnr("%")} NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) -" call Decho("remove") +" call Decho("remove",'~'.expand("<slnum>")) for fname in mflist let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1) @@ -7172,25 +7274,25 @@ fun! s:NetrwMarkFileMove(islocal) " ------- " cleanup " ------- -" call Decho("cleanup") +" call Decho("cleanup",'~'.expand("<slnum>")) " remove markings from local buffer call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer " refresh buffers if !s:netrwmftgt_islocal -" call Decho("refresh netrwmftgt<".s:netrwmftgt.">") +" call Decho("refresh netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt) endif if a:islocal -" call Decho("refresh b:netrw_curdir<".b:netrw_curdir.">") +" call Decho("refresh b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefreshDir(a:islocal,b:netrw_curdir) endif if g:netrw_fastbrowse <= 1 -" call Decho("since g:netrw_fastbrowse=".g:netrw_fastbrowse.", perform shell cmd refresh") +" call Decho("since g:netrw_fastbrowse=".g:netrw_fastbrowse.", perform shell cmd refresh",'~'.expand("<slnum>")) NetrwKeepj call s:LocalBrowseRefresh() endif - + " call Dret("s:NetrwMarkFileMove") endfun @@ -7207,10 +7309,11 @@ fun! s:NetrwMarkFilePrint(islocal) " call Dret("s:NetrwMarkFilePrint") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + let curdir= s:NetrwGetCurdir(a:islocal) + if exists("s:netrwmarkfilelist_{curbufnr}") let netrwmarkfilelist = s:netrwmarkfilelist_{curbufnr} - let curdir = b:netrw_curdir call s:NetrwUnmarkList(curbufnr,curdir) for fname in netrwmarkfilelist if a:islocal @@ -7222,9 +7325,9 @@ fun! s:NetrwMarkFilePrint(islocal) endif 1split " the autocmds will handle both local and remote files -" call Decho("exe sil e ".escape(fname,' ')) +" call Decho("exe sil e ".escape(fname,' '),'~'.expand("<slnum>")) exe "sil NetrwKeepj e ".fnameescape(fname) -" call Decho("hardcopy") +" call Decho("hardcopy",'~'.expand("<slnum>")) hardcopy q endfor @@ -7246,28 +7349,33 @@ fun! s:NetrwMarkFileRegexp(islocal) call inputrestore() if a:islocal + let curdir= s:NetrwGetCurdir(a:islocal) " get the matching list of files using local glob() -" call Decho("handle local regexp") +" call Decho("handle local regexp",'~'.expand("<slnum>")) let dirname = escape(b:netrw_curdir,g:netrw_glob_escape) - let files = glob(s:ComposePath(dirname,regexp)) -" call Decho("files<".files.">") + if v:version == 704 && has("patch656") + let files = glob(s:ComposePath(dirname,regexp),0,0,1) + else + let files = glob(s:ComposePath(dirname,regexp),0,0) + endif +" call Decho("files<".files.">",'~'.expand("<slnum>")) let filelist= split(files,"\n") " mark the list of files for fname in filelist -" call Decho("fname<".fname.">") +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^.*/','','')) endfor else -" call Decho("handle remote regexp") +" call Decho("handle remote regexp",'~'.expand("<slnum>")) " convert displayed listing into a filelist let eikeep = &ei let areg = @a sil NetrwKeepj %y a setl ei=all ma -" call Decho("setl ei=all ma") +" call Decho("setl ei=all ma",'~'.expand("<slnum>")) 1split NetrwKeepj call s:NetrwEnew() NetrwKeepj call s:NetrwSafeOptions() @@ -7290,7 +7398,7 @@ fun! s:NetrwMarkFileRegexp(islocal) endif " convert regexp into the more usual glob-style format let regexp= substitute(regexp,'\*','.*','g') -" call Decho("regexp<".regexp.">") +" call Decho("regexp<".regexp.">",'~'.expand("<slnum>")) exe "sil! NetrwKeepj v/".escape(regexp,'/')."/d" call histdel("/",-1) let filelist= getline(1,line("$")) @@ -7320,10 +7428,11 @@ fun! s:NetrwMarkFileSource(islocal) " call Dret("s:NetrwMarkFileSource") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + let curdir= s:NetrwGetCurdir(a:islocal) + if exists("s:netrwmarkfilelist_{curbufnr}") let netrwmarkfilelist = s:netrwmarkfilelist_{bufnr("%")} - let curdir = b:netrw_curdir call s:NetrwUnmarkList(curbufnr,curdir) for fname in netrwmarkfilelist if a:islocal @@ -7334,7 +7443,7 @@ fun! s:NetrwMarkFileSource(islocal) let fname= curdir.fname endif " the autocmds will handle sourcing both local and remote files -" call Decho("exe so ".fnameescape(fname)) +" call Decho("exe so ".fnameescape(fname),'~'.expand("<slnum>")) exe "so ".fnameescape(fname) endfor 2match none @@ -7348,7 +7457,7 @@ endfun fun! s:NetrwMarkFileTag(islocal) " call Dfunc("s:NetrwMarkFileTag(islocal=".a:islocal.")") let svpos = netrw#SavePosn() - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let curbufnr = bufnr("%") " sanity check @@ -7357,16 +7466,16 @@ fun! s:NetrwMarkFileTag(islocal) " call Dret("s:NetrwMarkFileTag") return endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr})) +" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) if exists("s:netrwmarkfilelist") -" call Decho("s:netrwmarkfilelist".string(s:netrwmarkfilelist).">") - let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "shellescape(v:val,".!a:islocal.")")) +" call Decho("s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) + let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "s:ShellEscape(v:val,".!a:islocal.")")) call s:NetrwUnmarkAll() if a:islocal if executable(g:netrw_ctags) -" call Decho("call system(".g:netrw_ctags." ".netrwmarkfilelist.")") +" call Decho("call system(".g:netrw_ctags." ".netrwmarkfilelist.")",'~'.expand("<slnum>")) call system(g:netrw_ctags." ".netrwmarkfilelist) else call netrw#ErrorMsg(s:ERROR,"g:netrw_ctags<".g:netrw_ctags."> is not executable!",51) @@ -7378,7 +7487,7 @@ fun! s:NetrwMarkFileTag(islocal) 1split NetrwKeepj e tags let path= substitute(curdir,'^\(.*\)/[^/]*$','\1/','') -" call Decho("curdir<".curdir."> path<".path.">") +" call Decho("curdir<".curdir."> path<".path.">",'~'.expand("<slnum>")) exe 'NetrwKeepj %s/\t\(\S\+\)\t/\t'.escape(path,"/\n\r\\").'\1\t/e' call histdel("/",-1) wq! @@ -7393,14 +7502,14 @@ endfun " --------------------------------------------------------------------- " s:NetrwMarkFileTgt: (invoked by mt) This function sets up a marked file target {{{2 -" Sets up two variables, +" Sets up two variables, " s:netrwmftgt : holds the target directory " s:netrwmftgt_islocal : 0=target directory is remote " 1=target directory is local fun! s:NetrwMarkFileTgt(islocal) " call Dfunc("s:NetrwMarkFileTgt(islocal=".a:islocal.")") let svpos = netrw#SavePosn() - let curdir = b:netrw_curdir + let curdir = s:NetrwGetCurdir(a:islocal) let hadtgt = exists("s:netrwmftgt") if !exists("w:netrw_bannercnt") let w:netrw_bannercnt= b:netrw_bannercnt @@ -7408,9 +7517,10 @@ fun! s:NetrwMarkFileTgt(islocal) " set up target if line(".") < w:netrw_bannercnt +" call Decho("set up target: line(.) < w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) " if cursor in banner region, use b:netrw_curdir for the target unless its already the target if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") && s:netrwmftgt == b:netrw_curdir -" call Decho("cursor in banner region, and target already is <".b:netrw_curdir.">: removing target") +" call Decho("cursor in banner region, and target already is <".b:netrw_curdir.">: removing target",'~'.expand("<slnum>")) unlet s:netrwmftgt s:netrwmftgt_islocal if g:netrw_fastbrowse <= 1 call s:LocalBrowseRefresh() @@ -7421,50 +7531,87 @@ fun! s:NetrwMarkFileTgt(islocal) return else let s:netrwmftgt= b:netrw_curdir -" call Decho("inbanner: s:netrwmftgt<".s:netrwmftgt.">") +" call Decho("inbanner: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) endif else " get word under cursor. " * If directory, use it for the target. " * If file, use b:netrw_curdir for the target +" call Decho("get word under cursor",'~'.expand("<slnum>")) let curword= s:NetrwGetWord() let tgtdir = s:ComposePath(curdir,curword) - if a:islocal && isdirectory(tgtdir) + if a:islocal && isdirectory(s:NetrwFile(tgtdir)) let s:netrwmftgt = tgtdir -" call Decho("local isdir: s:netrwmftgt<".s:netrwmftgt.">") +" call Decho("local isdir: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) elseif !a:islocal && tgtdir =~ '/$' let s:netrwmftgt = tgtdir -" call Decho("remote isdir: s:netrwmftgt<".s:netrwmftgt.">") +" call Decho("remote isdir: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) else let s:netrwmftgt = curdir -" call Decho("isfile: s:netrwmftgt<".s:netrwmftgt.">") +" call Decho("isfile: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) endif endif if a:islocal " simplify the target (eg. /abc/def/../ghi -> /abc/ghi) let s:netrwmftgt= simplify(s:netrwmftgt) -" call Decho("simplify: s:netrwmftgt<".s:netrwmftgt.">") +" call Decho("simplify: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) endif if g:netrw_cygwin - let s:netrwmftgt= substitute(system("cygpath ".shellescape(s:netrwmftgt)),'\n$','','') + let s:netrwmftgt= substitute(system("cygpath ".s:ShellEscape(s:netrwmftgt)),'\n$','','') let s:netrwmftgt= substitute(s:netrwmftgt,'\n$','','') endif let s:netrwmftgt_islocal= a:islocal + " need to do refresh so that the banner will be updated + " s:LocalBrowseRefresh handles all local-browsing buffers when not fast browsing if g:netrw_fastbrowse <= 1 +" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse.", so refreshing all local netrw buffers") call s:LocalBrowseRefresh() endif - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) +" call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) + if w:netrw_liststyle == s:TREELIST + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,w:netrw_treetop)) + else + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) + endif call netrw#RestorePosn(svpos) if !hadtgt sil! NetrwKeepj norm! j endif +" call Decho("getmatches=".string(getmatches()),'~'.expand("<slnum>")) +" call Decho("s:netrwmarkfilelist=".(exists("s:netrwmarkfilelist")? string(s:netrwmarkfilelist) : 'n/a'),'~'.expand("<slnum>")) " call Dret("s:NetrwMarkFileTgt : netrwmftgt<".(exists("s:netrwmftgt")? s:netrwmftgt : "").">") endfun " --------------------------------------------------------------------- +" s:NetrwGetCurdir: gets current directory and sets up b:netrw_curdir if necessary {{{2 +fun! s:NetrwGetCurdir(islocal) +" call Dfunc("s:NetrwGetCurdir(islocal=".a:islocal.")") + + if w:netrw_liststyle == s:TREELIST + let b:netrw_curdir = s:NetrwTreePath(w:netrw_treetop) +" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used s:NetrwTreeDir)",'~'.expand("<slnum>")) + elseif !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() +" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used getcwd)",'~'.expand("<slnum>")) + endif + +" call Decho("b:netrw_curdir<".b:netrw_curdir."> ".((b:netrw_curdir !~ '\<\a\{3,}://')? "does not match" : "matches")." url pattern") + if b:netrw_curdir !~ '\<\a\{3,}://' + let curdir= b:netrw_curdir +" call Decho("g:netrw_keepdir=".g:netrw_keepdir) + if g:netrw_keepdir == 0 + call s:NetrwLcd(curdir) + endif + endif + +" call Dret("s:NetrwGetCurdir <".curdir.">") + return b:netrw_curdir +endfun + +" --------------------------------------------------------------------- " s:NetrwOpenFile: query user for a filename and open it {{{2 fun! s:NetrwOpenFile(islocal) " call Dfunc("s:NetrwOpenFile(islocal=".a:islocal.")") @@ -7477,7 +7624,10 @@ fun! s:NetrwOpenFile(islocal) if exists("g:netrw_quiet") let netrw_quiet_keep = g:netrw_quiet endif - let g:netrw_quiet = 1 + let g:netrw_quiet = 1 + " save position for benefit of Rexplore + let s:rexposn_{bufnr("%")}= netrw#SavePosn() +" call Decho("setting s:rexposn_".bufnr("%")."<".bufname("%")."> to SavePosn",'~'.expand("<slnum>")) if b:netrw_curdir =~ '/$' exe "NetrwKeepj e ".fnameescape(b:netrw_curdir.fname) else @@ -7497,14 +7647,84 @@ fun! s:NetrwOpenFile(islocal) endfun " --------------------------------------------------------------------- -" s:NetrwUnmarkList: delete local marked file lists and remove their contents from the global marked-file list {{{2 -" User access provided by the <mu> mapping. (see :help netrw-mu) +" netrw#Shrink: shrinks/expands a netrw or Lexplorer window {{{2 +" For the mapping to this function be made via +" netrwPlugin, you'll need to have had +" g:netrw_usetab set to non-zero. +fun! netrw#Shrink() +" call Dfunc("netrw#Shrink() ft<".&ft."> winwidth=".winwidth(0)." lexbuf#".((exists("t:netrw_lexbufnr"))? t:netrw_lexbufnr : 'n/a')) + let curwin = winnr() + let wiwkeep = &wiw + set wiw=1 + + if &ft == "netrw" + if winwidth(0) > g:netrw_wiw + let t:netrw_winwidth= winwidth(0) + exe "vert resize ".g:netrw_wiw + wincmd l + if winnr() == curwin + wincmd h + endif +" call Decho("vert resize 0",'~'.expand("<slnum>")) + else + exe "vert resize ".t:netrw_winwidth +" call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>")) + endif + + elseif exists("t:netrw_lexbufnr") + exe bufwinnr(t:netrw_lexbufnr)."wincmd w" + if winwidth(bufwinnr(t:netrw_lexbufnr)) > g:netrw_wiw + let t:netrw_winwidth= winwidth(0) + exe "vert resize ".g:netrw_wiw + wincmd l + if winnr() == curwin + wincmd h + endif +" call Decho("vert resize 0",'~'.expand("<slnum>")) + elseif winwidth(bufwinnr(t:netrw_lexbufnr)) >= 0 + exe "vert resize ".t:netrw_winwidth +" call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>")) + else + call netrw#Lexplore(0,0) + endif + + else + call netrw#Lexplore(0,0) + endif + let wiw= wiwkeep + +" call Dret("netrw#Shrink") +endfun + +" --------------------------------------------------------------------- +" s:NetSortSequence: allows user to edit the sorting sequence {{{2 +fun! s:NetSortSequence(islocal) +" call Dfunc("NetSortSequence(islocal=".a:islocal.")") + + let ykeep= @@ + let svpos= netrw#SavePosn() + call inputsave() + let newsortseq= input("Edit Sorting Sequence: ",g:netrw_sort_sequence) + call inputrestore() + + " refresh the listing + let g:netrw_sort_sequence= newsortseq + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) + NetrwKeepj call netrw#RestorePosn(svpos) + let @@= ykeep + +" call Dret("NetSortSequence") +endfun + +" --------------------------------------------------------------------- +" s:NetrwUnmarkList: delete local marked file list and remove their contents from the global marked-file list {{{2 +" User access provided by the <mF> mapping. (see :help netrw-mF) " Used by many MarkFile functions. fun! s:NetrwUnmarkList(curbufnr,curdir) " call Dfunc("s:NetrwUnmarkList(curbufnr=".a:curbufnr." curdir<".a:curdir.">)") " remove all files in local marked-file list from global list - if exists("s:netrwmarkfilelist_{a:curbufnr}") + if exists("s:netrwmarkfilelist") for mfile in s:netrwmarkfilelist_{a:curbufnr} let dfile = s:ComposePath(a:curdir,mfile) " prepend directory to mfile let idx = index(s:netrwmarkfilelist,dfile) " get index in list of dfile @@ -7513,7 +7733,7 @@ fun! s:NetrwUnmarkList(curbufnr,curdir) if s:netrwmarkfilelist == [] unlet s:netrwmarkfilelist endif - + " getting rid of the local marked-file lists is easy unlet s:netrwmarkfilelist_{a:curbufnr} endif @@ -7544,7 +7764,7 @@ fun! s:NetrwUnmarkAll2() let redir END let netrwmarkfilelist_list= split(netrwmarkfilelist_let,'\n') " convert let string into a let list - call filter(netrwmarkfilelist_list,"v:val =~ '^s:netrwmarkfilelist_'") " retain only those vars that start as s:netrwmarkfilelist_ + call filter(netrwmarkfilelist_list,"v:val =~ '^s:netrwmarkfilelist_'") " retain only those vars that start as s:netrwmarkfilelist_ call map(netrwmarkfilelist_list,"substitute(v:val,'\\s.*$','','')") " remove what the entries are equal to for flist in netrwmarkfilelist_list let curbufnr= substitute(flist,'s:netrwmarkfilelist_','','') @@ -7555,22 +7775,36 @@ fun! s:NetrwUnmarkAll2() endfun " --------------------------------------------------------------------- -" s:NetrwUnMarkFile: {{{2 +" s:NetrwUnMarkFile: called via mu map; unmarks *all* marked files, both global and buffer-local {{{2 +" +" Marked files are in two types of lists: +" s:netrwmarkfilelist -- holds complete paths to all marked files +" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr()) +" +" Marked files suitable for use with 2match are in: +" s:netrwmarkfilemtch_# -- used with 2match to display marked files fun! s:NetrwUnMarkFile(islocal) " call Dfunc("s:NetrwUnMarkFile(islocal=".a:islocal.")") let svpos = netrw#SavePosn() let curbufnr = bufnr("%") - " unmark marked file list (although I expect s:NetrwUpload() - " to do it, I'm just making sure) - if exists("s:netrwmarkfilelist_{bufnr('%')}") -" call Decho("unlet'ing: s:netrwmarkfile[list|mtch]_".bufnr("%")) + " unmark marked file list + " (although I expect s:NetrwUpload() to do it, I'm just making sure) + if exists("s:netrwmarkfilelist") +" " call Decho("unlet'ing: s:netrwmarkfilelist",'~'.expand("<slnum>")) unlet s:netrwmarkfilelist - unlet s:netrwmarkfilelist_{curbufnr} - unlet s:netrwmarkfilemtch_{curbufnr} - 2match none endif + let ibuf= 1 + while ibuf < bufnr("$") + if exists("s:netrwmarkfilelist_".ibuf) + unlet s:netrwmarkfilelist_{ibuf} + unlet s:netrwmarkfilemtch_{ibuf} + endif + let ibuf = ibuf + 1 + endwhile + 2match none + " call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) call netrw#RestorePosn(svpos) " call Dret("s:NetrwUnMarkFile") @@ -7588,7 +7822,7 @@ fun! s:NetrwMenu(domenu) " call Dfunc("NetrwMenu(domenu=".a:domenu.")") if !exists("s:netrw_menu_enabled") && a:domenu -" call Decho("initialize menu") +" call Decho("initialize menu",'~'.expand("<slnum>")) let s:netrw_menu_enabled= 1 exe 'sil! menu '.g:NetrwMenuPriority.'.1 '.g:NetrwTopLvlMenu.'Help<tab><F1> <F1>' exe 'sil! menu '.g:NetrwMenuPriority.'.5 '.g:NetrwTopLvlMenu.'-Sep1- :' @@ -7654,6 +7888,7 @@ fun! s:NetrwMenu(domenu) exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.1 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Name<tab>s :let g:netrw_sort_by="name"<cr><c-L>' exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.2 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Time<tab>s :let g:netrw_sort_by="time"<cr><c-L>' exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Size<tab>s :let g:netrw_sort_by="size"<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Exten<tab>s :let g:netrw_sort_by="exten"<cr><c-L>' exe 'sil! menu '.g:NetrwMenuPriority.'.17 '.g:NetrwTopLvlMenu.'Rename\ File/Directory<tab>R R' exe 'sil! menu '.g:NetrwMenuPriority.'.18 '.g:NetrwTopLvlMenu.'Set\ Current\ Directory<tab>c c' let s:netrw_menucnt= 28 @@ -7667,9 +7902,9 @@ fun! s:NetrwMenu(domenu) exe curwin."wincmd w" if s:netrwcnt <= 1 -" call Decho("clear menus") +" call Decho("clear menus",'~'.expand("<slnum>")) exe 'sil! unmenu '.g:NetrwTopLvlMenu -" call Decho('exe sil! unmenu '.g:NetrwTopLvlMenu.'*') +" call Decho('exe sil! unmenu '.g:NetrwTopLvlMenu.'*','~'.expand("<slnum>")) sil! unlet s:netrw_menu_enabled endif endif @@ -7687,7 +7922,7 @@ fun! s:NetrwObtain(islocal) let ykeep= @@ if exists("s:netrwmarkfilelist_{bufnr('%')}") - let islocal= s:netrwmarkfilelist_{bufnr('%')}[1] !~ '^\a\+://' + let islocal= s:netrwmarkfilelist_{bufnr('%')}[1] !~ '^\a\{3,}://' call netrw#Obtain(islocal,s:netrwmarkfilelist_{bufnr('%')}) call s:NetrwUnmarkList(bufnr('%'),b:netrw_curdir) else @@ -7720,32 +7955,32 @@ fun! s:NetrwPrevWinOpen(islocal) let choice = 0 let s:treedir = s:NetrwTreeDir(a:islocal) let curdir = s:treedir -" call Decho("winnr($)#".lastwinnr." curword<".curword.">") +" call Decho("winnr($)#".lastwinnr." curword<".curword.">",'~'.expand("<slnum>")) let didsplit = 0 if lastwinnr == 1 " if only one window, open a new one first -" call Decho("only one window, so open a new one (g:netrw_alto=".g:netrw_alto.")") +" call Decho("only one window, so open a new one (g:netrw_alto=".g:netrw_alto.")",'~'.expand("<slnum>")) if g:netrw_preview " vertically split preview window let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize -" call Decho("exe ".(g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s") +" call Decho("exe ".(g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s",'~'.expand("<slnum>")) exe (g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s" else " horizontally split preview window let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize -" call Decho("exe ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s") +" call Decho("exe ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s",'~'.expand("<slnum>")) exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" endif let didsplit = 1 -" call Decho("did split") +" call Decho("did split",'~'.expand("<slnum>")) else NetrwKeepj call s:SaveBufVars() let eikeep= &ei setl ei=all wincmd p -" call Decho("wincmd p (now in win#".winnr().") curdir<".curdir.">") +" call Decho("wincmd p (now in win#".winnr().") curdir<".curdir.">",'~'.expand("<slnum>")) " prevwinnr: the window number of the "prev" window " prevbufnr: the buffer number of the buffer in the "prev" window @@ -7756,22 +7991,22 @@ fun! s:NetrwPrevWinOpen(islocal) let prevmod = &mod let bnrcnt = 0 NetrwKeepj call s:RestoreBufVars() -" call Decho("after wincmd p: win#".winnr()." win($)#".winnr("$")." origwin#".origwin." &mod=".&mod." bufname(%)<".bufname("%")."> prevbufnr=".prevbufnr) +" call Decho("after wincmd p: win#".winnr()." win($)#".winnr("$")." origwin#".origwin." &mod=".&mod." bufname(%)<".bufname("%")."> prevbufnr=".prevbufnr,'~'.expand("<slnum>")) " if the previous window's buffer has been changed (ie. its modified flag is set), " and it doesn't appear in any other extant window, then ask the " user if s/he wants to abandon modifications therein. if prevmod -" call Decho("detected that prev window's buffer has been modified: prevbufnr=".prevbufnr." winnr()#".winnr()) +" call Decho("detected that prev window's buffer has been modified: prevbufnr=".prevbufnr." winnr()#".winnr(),'~'.expand("<slnum>")) windo if winbufnr(0) == prevbufnr | let bnrcnt=bnrcnt+1 | endif -" call Decho("prevbufnr=".prevbufnr." bnrcnt=".bnrcnt." buftype=".&bt." winnr()=".winnr()." prevwinnr#".prevwinnr) +" call Decho("prevbufnr=".prevbufnr." bnrcnt=".bnrcnt." buftype=".&bt." winnr()=".winnr()." prevwinnr#".prevwinnr,'~'.expand("<slnum>")) exe prevwinnr."wincmd w" if bnrcnt == 1 && &hidden == 0 " only one copy of the modified buffer in a window, and " hidden not set, so overwriting will lose the modified file. Ask first... let choice = confirm("Save modified buffer<".prevbufname."> first?","&Yes\n&No\n&Cancel") -" call Decho("(NetrwPrevWinOpen) prevbufname<".prevbufname."> choice=".choice." current-winnr#".winnr()) +" call Decho("(NetrwPrevWinOpen) prevbufname<".prevbufname."> choice=".choice." current-winnr#".winnr(),'~'.expand("<slnum>")) let &ei= eikeep if choice == 1 @@ -7779,7 +8014,7 @@ fun! s:NetrwPrevWinOpen(islocal) let v:errmsg= "" sil w if v:errmsg != "" - call netrw#ErrorMsg(s:ERROR,"unable to write <".prevbufname.">!",30) + call netrw#ErrorMsg(s:ERROR,"unable to write <".(exists("prevbufname")? prevbufname : 'n/a').">!",30) exe origwin."wincmd w" let &ei = eikeep let @@ = ykeep @@ -7789,12 +8024,12 @@ fun! s:NetrwPrevWinOpen(islocal) elseif choice == 2 " No -- don't worry about changed file, just browse anyway -" call Decho("don't worry about chgd file, just browse anyway (winnr($)#".winnr("$").")") +" call Decho("don't worry about chgd file, just browse anyway (winnr($)#".winnr("$").")",'~'.expand("<slnum>")) echomsg "**note** changes to ".prevbufname." abandoned" else " Cancel -- don't do this -" call Decho("cancel, don't browse, switch to win#".origwin) +" call Decho("cancel, don't browse, switch to win#".origwin,'~'.expand("<slnum>")) exe origwin."wincmd w" let &ei= eikeep let @@ = ykeep @@ -7828,46 +8063,46 @@ endfun fun! s:NetrwUpload(fname,tgt,...) " call Dfunc("s:NetrwUpload(fname<".((type(a:fname) == 1)? a:fname : string(a:fname))."> tgt<".a:tgt.">) a:0=".a:0) - if a:tgt =~ '^\a\+://' - let tgtdir= substitute(a:tgt,'^\a\+://[^/]\+/\(.\{-}\)$','\1','') + if a:tgt =~ '^\a\{3,}://' + let tgtdir= substitute(a:tgt,'^\a\{3,}://[^/]\+/\(.\{-}\)$','\1','') else let tgtdir= substitute(a:tgt,'^\(.*\)/[^/]*$','\1','') endif -" call Decho("tgtdir<".tgtdir.">") +" call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>")) if a:0 > 0 let fromdir= a:1 else let fromdir= getcwd() endif -" call Decho("fromdir<".fromdir.">") +" call Decho("fromdir<".fromdir.">",'~'.expand("<slnum>")) if type(a:fname) == 1 " handle uploading a single file using NetWrite -" call Decho("handle uploading a single file via NetWrite") +" call Decho("handle uploading a single file via NetWrite",'~'.expand("<slnum>")) 1split -" call Decho("exe e ".fnameescape(a:fname)) - exe "NetrwKeepj e ".fnameescape(a:fname) -" call Decho("now locally editing<".expand("%").">, has ".line("$")." lines") +" call Decho("exe e ".fnameescape(s:NetrwFile(a:fname)),'~'.expand("<slnum>")) + exe "NetrwKeepj e ".fnameescape(s:NetrwFile(a:fname)) +" call Decho("now locally editing<".expand("%").">, has ".line("$")." lines",'~'.expand("<slnum>")) if a:tgt =~ '/$' let wfname= substitute(a:fname,'^.*/','','') -" call Decho("exe w! ".fnameescape(wfname)) +" call Decho("exe w! ".fnameescape(wfname),'~'.expand("<slnum>")) exe "w! ".fnameescape(a:tgt.wfname) else -" call Decho("writing local->remote: exe w ".fnameescape(a:tgt)) +" call Decho("writing local->remote: exe w ".fnameescape(a:tgt),'~'.expand("<slnum>")) exe "w ".fnameescape(a:tgt) -" call Decho("done writing local->remote") +" call Decho("done writing local->remote",'~'.expand("<slnum>")) endif q! elseif type(a:fname) == 3 " handle uploading a list of files via scp -" call Decho("handle uploading a list of files via scp") +" call Decho("handle uploading a list of files via scp",'~'.expand("<slnum>")) let curdir= getcwd() if a:tgt =~ '^scp:' call s:NetrwLcd(fromdir) let filelist= deepcopy(s:netrwmarkfilelist_{bufnr('%')}) - let args = join(map(filelist,"shellescape(v:val, 1)")) + let args = join(map(filelist,"s:ShellEscape(v:val, 1)")) if exists("g:netrw_port") && g:netrw_port != "" let useport= " ".g:netrw_scpport." ".g:netrw_port else @@ -7875,7 +8110,7 @@ fun! s:NetrwUpload(fname,tgt,...) endif let machine = substitute(a:tgt,'^scp://\([^/:]\+\).*$','\1','') let tgt = substitute(a:tgt,'^scp://[^/]\+/\(.*\)$','\1','') - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.shellescape(useport,1)." ".args." ".shellescape(machine.":".tgt,1)) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".args." ".s:ShellEscape(machine.":".tgt,1)) call s:NetrwLcd(curdir) elseif a:tgt =~ '^ftp:' @@ -7885,35 +8120,35 @@ fun! s:NetrwUpload(fname,tgt,...) " handle uploading a list of files via ftp+.netrc let netrw_fname = b:netrw_fname sil NetrwKeepj new -" call Decho("filter input window#".winnr()) +" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) if tgtdir == "" let tgtdir= '/' endif NetrwKeepj call setline(line("$")+1,'cd "'.tgtdir.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) for fname in a:fname - NetrwKeepj call setline(line("$")+1,'put "'.fname.'"') -" call Decho("filter input: ".getline('$')) + NetrwKeepj call setline(line("$")+1,'put "'.s:NetrwFile(fname).'"') +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endfor if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)." ".shellescape(g:netrw_port,1)) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) else -" call Decho("filter input window#".winnr()) - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)) +" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) endif " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) sil NetrwKeepj g/Local directory now/d @@ -7933,42 +8168,42 @@ fun! s:NetrwUpload(fname,tgt,...) if exists("g:netrw_port") && g:netrw_port != "" NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) else NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("g:netrw_uid") && g:netrw_uid != "" if exists("g:netrw_ftp") && g:netrw_ftp == 1 NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) if exists("s:netrw_passwd") NetrwKeepj call setline(line("$")+1,'"'.s:netrw_passwd.'"') endif -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) elseif exists("s:netrw_passwd") NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif endif NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) if exists("b:netrw_fname") && b:netrw_fname != "" NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endif for fname in a:fname NetrwKeepj call setline(line("$")+1,'put "'.fname.'"') -" call Decho("filter input: ".getline('$')) +" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) endfor " perform ftp: @@ -8009,7 +8244,7 @@ fun! s:NetrwPreview(path) range NetrwKeepj call s:NetrwOptionSave("s:") NetrwKeepj call s:NetrwSafeOptions() if has("quickfix") - if !isdirectory(a:path) + if !isdirectory(s:NetrwFile(a:path)) if g:netrw_preview && !g:netrw_alto let pvhkeep = &pvh let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize @@ -8036,15 +8271,16 @@ fun! s:NetrwRefresh(islocal,dirname) " call Dfunc("s:NetrwRefresh(islocal<".a:islocal.">,dirname=".a:dirname.") hide=".g:netrw_hide." sortdir=".g:netrw_sort_direction) " at the current time (Mar 19, 2007) all calls to NetrwRefresh() call NetrwBrowseChgDir() first. setl ma noro -" call Decho("setl ma noro") -" call Decho("clear buffer<".expand("%")."> with :%d") +" call Decho("setl ma noro",'~'.expand("<slnum>")) +" call Decho("clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) let ykeep = @@ " save the cursor position before refresh. let screenposn = netrw#SavePosn() -" call Decho("win#".winnr().": ".winheight(0)."x".winwidth(0)." curfile<".expand("%").">") -" call Decho("clearing buffer prior to refresh") - sil! NetrwKeepj %d + +" call Decho("win#".winnr().": ".winheight(0)."x".winwidth(0)." curfile<".expand("%").">",'~'.expand("<slnum>")) +" call Decho("clearing buffer prior to refresh",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ if a:islocal NetrwKeepj call netrw#LocalBrowseCheck(a:dirname) else @@ -8056,10 +8292,10 @@ fun! s:NetrwRefresh(islocal,dirname) " restore file marks if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != "" -" call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/") +" call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/",'~'.expand("<slnum>")) exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" else -" call Decho("2match none") +" call Decho("2match none (bufnr(%)=".bufnr("%")."<".bufname("%").">)",'~'.expand("<slnum>")) 2match none endif @@ -8076,26 +8312,26 @@ fun! s:NetrwRefreshDir(islocal,dirname) " call Dfunc("s:NetrwRefreshDir(islocal=".a:islocal." dirname<".a:dirname.">) g:netrw_fastbrowse=".g:netrw_fastbrowse) if g:netrw_fastbrowse == 0 " slowest mode (keep buffers refreshed, local or remote) -" call Decho("slowest mode: keep buffers refreshed, local or remote") +" call Decho("slowest mode: keep buffers refreshed, local or remote",'~'.expand("<slnum>")) let tgtwin= bufwinnr(a:dirname) -" call Decho("tgtwin= bufwinnr(".a:dirname.")=".tgtwin) +" call Decho("tgtwin= bufwinnr(".a:dirname.")=".tgtwin,'~'.expand("<slnum>")) if tgtwin > 0 " tgtwin is being displayed, so refresh it let curwin= winnr() -" call Decho("refresh tgtwin#".tgtwin." (curwin#".curwin.")") +" call Decho("refresh tgtwin#".tgtwin." (curwin#".curwin.")",'~'.expand("<slnum>")) exe tgtwin."wincmd w" - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) exe curwin."wincmd w" elseif bufnr(a:dirname) > 0 let bn= bufnr(a:dirname) -" call Decho("bd bufnr(".a:dirname.")=".bn) - exe "sil bd ".bn +" call Decho("bd bufnr(".a:dirname.")=".bn,'~'.expand("<slnum>")) + exe "sil keepj bd ".bn endif elseif g:netrw_fastbrowse <= 1 -" call Decho("medium-speed mode: refresh local buffers only") +" call Decho("medium-speed mode: refresh local buffers only",'~'.expand("<slnum>")) NetrwKeepj call s:LocalBrowseRefresh() endif " call Dret("s:NetrwRefreshDir") @@ -8109,7 +8345,7 @@ endfun fun! s:NetrwSetChgwin(...) " call Dfunc("s:NetrwSetChgwin() v:count=".v:count) if a:0 > 0 -" call Decho("a:1<".a:1.">") +" call Decho("a:1<".a:1.">",'~'.expand("<slnum>")) if a:1 == "" " :NetrwC win# let g:netrw_chgwin= winnr() else " :NetrwC @@ -8120,6 +8356,7 @@ fun! s:NetrwSetChgwin(...) else " C let g:netrw_chgwin= winnr() endif + echo "editing window now set to window#".g:netrw_chgwin " call Dret("s:NetrwSetChgwin : g:netrw_chgwin=".g:netrw_chgwin) endfun @@ -8159,7 +8396,7 @@ fun! s:NetrwSetSort() else let spriority= priority.g:netrw_sepchr endif -" call Decho("priority=".priority." spriority<".spriority."> seq<".seq."> seqlist<".seqlist.">") +" call Decho("priority=".priority." spriority<".spriority."> seq<".seq."> seqlist<".seqlist.">",'~'.expand("<slnum>")) " sanity check if w:netrw_bannercnt > line("$") @@ -8235,7 +8472,7 @@ fun! s:NetrwSortStyle(islocal) NetrwKeepj call s:NetrwSaveWordPosn() let svpos= netrw#SavePosn() - let g:netrw_sort_by= (g:netrw_sort_by =~ 'n')? 'time' : (g:netrw_sort_by =~ 't')? 'size' : 'name' + let g:netrw_sort_by= (g:netrw_sort_by =~ '^n')? 'time' : (g:netrw_sort_by =~ '^t')? 'size' : (g:netrw_sort_by =~ '^siz')? 'exten' : 'name' NetrwKeepj norm! 0 NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) NetrwKeepj call netrw#RestorePosn(svpos) @@ -8261,7 +8498,7 @@ fun! s:NetrwSplit(mode) " remote and o let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize if winsz == 0|let winsz= ""|endif -" call Decho("exe ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s") +" call Decho("exe ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s",'~'.expand("<slnum>")) exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" let s:didsplit= 1 NetrwKeepj call s:RestoreWinVars() @@ -8271,7 +8508,7 @@ fun! s:NetrwSplit(mode) elseif a:mode == 1 " remote and t let newdir = s:NetrwBrowseChgDir(0,s:NetrwGetWord()) -" call Decho("tabnew") +" call Decho("tabnew",'~'.expand("<slnum>")) tabnew let s:didsplit= 1 NetrwKeepj call s:RestoreWinVars() @@ -8282,7 +8519,7 @@ fun! s:NetrwSplit(mode) " remote and v let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize if winsz == 0|let winsz= ""|endif -" call Decho("exe ".(g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v") +" call Decho("exe ".(g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v",'~'.expand("<slnum>")) exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v" let s:didsplit= 1 NetrwKeepj call s:RestoreWinVars() @@ -8293,7 +8530,7 @@ fun! s:NetrwSplit(mode) " local and o let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize if winsz == 0|let winsz= ""|endif -" call Decho("exe ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s") +" call Decho("exe ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s",'~'.expand("<slnum>")) exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" let s:didsplit= 1 NetrwKeepj call s:RestoreWinVars() @@ -8312,9 +8549,9 @@ fun! s:NetrwSplit(mode) setl ei=all exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>" exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>" - let &ei= eikeep - let netrw_curdir= s:NetrwTreeDir(0) -" call Decho("tabnew") + let &ei = eikeep + let netrw_curdir = s:NetrwTreeDir(0) +" call Decho("tabnew",'~'.expand("<slnum>")) tabnew let b:netrw_curdir = netrw_curdir let s:didsplit = 1 @@ -8332,7 +8569,7 @@ fun! s:NetrwSplit(mode) " local and v let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize if winsz == 0|let winsz= ""|endif -" call Decho("exe ".(g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v") +" call Decho("exe ".(g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v",'~'.expand("<slnum>")) exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v" let s:didsplit= 1 NetrwKeepj call s:RestoreWinVars() @@ -8359,21 +8596,29 @@ fun! s:NetrwTgtMenu() " the following test assures that gvim is running, has menus available, and has menus enabled. if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu if exists("g:NetrwTopLvlMenu") -" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)") +" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>")) exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Targets' endif if !exists("s:netrw_initbookhist") call s:NetrwBookHistRead() endif + " try to cull duplicate entries + let tgtdict={} + " target bookmarked places if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0 -" call Decho("installing bookmarks as easy targets") +" call Decho("installing bookmarks as easy targets",'~'.expand("<slnum>")) let cnt= 1 for bmd in g:netrw_bookmarklist + if has_key(tgtdict,bmd) + let cnt= cnt + 1 + continue + endif + let tgtdict[bmd]= cnt let ebmd= escape(bmd,g:netrw_menu_escape) " show bookmarks for goto menu -" call Decho("menu: Targets: ".bmd) +" call Decho("menu: Targets: ".bmd,'~'.expand("<slnum>")) exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.1.".cnt." ".g:NetrwTopLvlMenu.'Targets.'.ebmd." :call netrw#MakeTgt('".bmd."')\<cr>" let cnt= cnt + 1 endfor @@ -8381,14 +8626,19 @@ fun! s:NetrwTgtMenu() " target directory browsing history if exists("g:netrw_dirhistmax") && g:netrw_dirhistmax > 0 -" call Decho("installing history as easy targets (histmax=".g:netrw_dirhistmax.")") +" call Decho("installing history as easy targets (histmax=".g:netrw_dirhistmax.")",'~'.expand("<slnum>")) let histcnt = 1 while histcnt <= g:netrw_dirhistmax let priority = g:netrw_dirhist_cnt + histcnt if exists("g:netrw_dirhist_{histcnt}") let histentry = g:netrw_dirhist_{histcnt} - let ehistentry = escape(histentry,g:netrw_menu_escape) -" call Decho("menu: Targets: ".histentry) + if has_key(tgtdict,histentry) + let histcnt = histcnt + 1 + continue + endif + let tgtdict[histentry] = histcnt + let ehistentry = escape(histentry,g:netrw_menu_escape) +" call Decho("menu: Targets: ".histentry,'~'.expand("<slnum>")) exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.2.".priority." ".g:NetrwTopLvlMenu.'Targets.'.ehistentry." :call netrw#MakeTgt('".histentry."')\<cr>" endif let histcnt = histcnt + 1 @@ -8411,63 +8661,72 @@ fun! s:NetrwTreeDir(islocal) " call Dret("s:NetrwTreeDir ".treedir) return treedir endif + if !exists("b:netrw_curdir") || b:netrw_curdir == "" let b:netrw_curdir= getcwd() endif + let treedir = b:netrw_curdir -" call Decho("set initial treedir<".treedir.">") +" call Decho("set initial treedir<".treedir.">",'~'.expand("<slnum>")) let s:treecurpos= netrw#SavePosn() if w:netrw_liststyle == s:TREELIST -" call Decho("w:netrw_liststyle is TREELIST:") -" call Decho("line#".line(".")." getline(.)<".getline('.')."> treecurpos<".string(s:treecurpos).">") +" call Decho("w:netrw_liststyle is TREELIST:",'~'.expand("<slnum>")) +" call Decho("line#".line(".")." getline(.)<".getline('.')."> treecurpos<".string(s:treecurpos).">",'~'.expand("<slnum>")) " extract tree directory if on a line specifying a subdirectory (ie. ends with "/") let curline= substitute(getline('.'),"\t -->.*$",'','') if curline =~ '/$' -" call Decho("extract tree subdirectory from current line") +" call Decho("extract tree subdirectory from current line",'~'.expand("<slnum>")) let treedir= substitute(getline('.'),'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') -" call Decho("treedir<".treedir.">") +" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) + elseif curline =~ '@$' +" call Decho("handle symbolic link from current line",'~'.expand("<slnum>")) + let treedir= resolve(substitute(substitute(getline('.'),'@.*$','','e'),'^|*\s*','','e')) +" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) else -" call Decho("do not extract tree subdirectory from current line and set treedir to empty") +" call Decho("do not extract tree subdirectory from current line and set treedir to empty",'~'.expand("<slnum>")) let treedir= "" endif " detect user attempting to close treeroot -" call Decho("check if user is attempting to close treeroot") -" call Decho(".win#".winnr()." buf#".bufnr("%")."<".bufname("%").">") -" call Decho(".getline(".line(".").")<".getline('.').'> '.((getline('.') =~ '^'.s:treedepthstring)? '=~' : '!~').' ^'.s:treedepthstring) +" call Decho("check if user is attempting to close treeroot",'~'.expand("<slnum>")) +" call Decho(".win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) +" call Decho(".getline(".line(".").")<".getline('.').'> '.((getline('.') =~ '^'.s:treedepthstring)? '=~' : '!~').' ^'.s:treedepthstring,'~'.expand("<slnum>")) if curline !~ '^'.s:treedepthstring && getline('.') != '..' -" call Decho(".user may have attempted to close treeroot") +" call Decho(".user may have attempted to close treeroot",'~'.expand("<slnum>")) " now force a refresh -" call Decho(".force refresh: clear buffer<".expand("%")."> with :%d") - sil! NetrwKeepj %d -" call Dret("s:NetrwTreeDir <".treedir."> : (side effect) s:treecurpos<".string(s:treecurpos).">") +" call Decho(".force refresh: clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ +" call Dret("s:NetrwTreeDir <".treedir."> : (side effect) s:treecurpos<".(exists("s:treecurpos")? string(s:treecurpos) : 'n/a').">") return b:netrw_curdir " else " Decho -" call Decho(".user did not attempt to close treeroot") +" call Decho(".user did not attempt to close treeroot",'~'.expand("<slnum>")) endif -" call Decho("islocal=".a:islocal." curline<".curline.">") -" call Decho("after subst<".substitute(curline,'^'.s:treedepthstring.'\+ \(.*\)$','\1','').">") +" call Decho("islocal=".a:islocal." curline<".curline.">",'~'.expand("<slnum>")) +" call Decho("after subst<".substitute(curline,'^'.s:treedepthstring.'\+ \(.*\)$','\1','').">",'~'.expand("<slnum>")) let potentialdir= substitute(curline,'^'.s:treedepthstring.'* \(.*\)@$','\1','') -" call Decho("potentialdir<".potentialdir."> isdir=".isdirectory(potentialdir)) - - if a:islocal && curline =~ '@$' && isdirectory(potentialdir) - let newdir = w:netrw_treetop.'/'.potentialdir - let treedir = s:NetrwTreePath(newdir) - let w:netrw_treetop = newdir -" call Decho("newdir <".newdir.">") - else +" call Decho("potentialdir<".potentialdir."> isdir=".isdirectory(s:NetrwFile(potentialdir)),'~'.expand("<slnum>")) + + " COMBAK: a symbolic link may point anywhere -- so it will be used to start a new treetop +" if a:islocal && curline =~ '@$' && isdirectory(s:NetrwFile(potentialdir)) +" let newdir = w:netrw_treetop.'/'.potentialdir +" " call Decho("apply NetrwTreePath to newdir<".newdir.">",'~'.expand("<slnum>")) +" let treedir = s:NetrwTreePath(newdir) +" let w:netrw_treetop = newdir +" " call Decho("newdir <".newdir.">",'~'.expand("<slnum>")) +" else +" call Decho("apply NetrwTreePath to treetop<".w:netrw_treetop.">",'~'.expand("<slnum>")) let treedir = s:NetrwTreePath(w:netrw_treetop) - endif +" endif endif " sanity maintenance: keep those //s away... let treedir= substitute(treedir,'//$','/','') -" call Decho("treedir<".treedir.">") +" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) -" call Dret("s:NetrwTreeDir <".treedir."> : (side effect) s:treecurpos<".string(s:treecurpos).">") +" call Dret("s:NetrwTreeDir <".treedir."> : (side effect) s:treecurpos<".(exists("s:treecurpos")? string(s:treecurpos) : 'n/a').">") return treedir endfun @@ -8482,9 +8741,9 @@ fun! s:NetrwTreeDisplay(dir,depth) " install ../ and shortdir if a:depth == "" call setline(line("$")+1,'../') -" call Decho("setline#".line("$")." ../ (depth is zero)") +" call Decho("setline#".line("$")." ../ (depth is zero)",'~'.expand("<slnum>")) endif - if a:dir =~ '^\a\+://' + if a:dir =~ '^\a\{3,}://' if a:dir == w:netrw_treetop let shortdir= a:dir else @@ -8495,29 +8754,30 @@ fun! s:NetrwTreeDisplay(dir,depth) let shortdir= substitute(a:dir,'^.*/','','e') call setline(line("$")+1,a:depth.shortdir.'/') endif -" call Decho("setline#".line("$")." shortdir<".a:depth.shortdir.">") +" call Decho("setline#".line("$")." shortdir<".a:depth.shortdir.">",'~'.expand("<slnum>")) " append a / to dir if its missing one let dir= a:dir - if dir !~ '/$' - let dir= dir.'/' - endif " display subtrees (if any) let depth= s:treedepthstring.a:depth +" call Decho("display subtrees with depth<".depth."> and current leaves",'~'.expand("<slnum>")) -" call Decho("display subtrees with depth<".depth."> and current leaves") - for entry in w:netrw_treedict[a:dir] - let direntry= substitute(dir.entry,'/$','','e') -" call Decho("dir<".dir."> entry<".entry."> direntry<".direntry.">") +" call Decho("w:netrw_treedict[".dir."]=".string(w:netrw_treedict[dir]),'~'.expand("<slnum>")) + for entry in w:netrw_treedict[dir] + let direntry= substitute(dir.'/'.entry,'[@/]$','','e') +" call Decho("dir<".dir."> entry<".entry."> direntry<".direntry.">",'~'.expand("<slnum>")) if entry =~ '/$' && has_key(w:netrw_treedict,direntry) -" call Decho("<".direntry."> is a key in treedict - display subtree for it") +" call Decho("<".direntry."> is a key in treedict - display subtree for it",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwTreeDisplay(direntry,depth) elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/') -" call Decho("<".direntry."/> is a key in treedict - display subtree for it") +" call Decho("<".direntry."/> is a key in treedict - display subtree for it",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwTreeDisplay(direntry.'/',depth) + elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@') +" call Decho("<".direntry."/> is a key in treedict - display subtree for it",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwTreeDisplay(direntry.'/',depth) else -" call Decho("<".entry."> is not a key in treedict (no subtree)") +" call Decho("<".entry."> is not a key in treedict (no subtree)",'~'.expand("<slnum>")) sil! NetrwKeepj call setline(line("$")+1,depth.entry) endif endfor @@ -8527,34 +8787,36 @@ endfun " --------------------------------------------------------------------- " s:NetrwTreeListing: displays tree listing from treetop on down, using NetrwTreeDisplay() {{{2 +" Called by s:PerformListing() fun! s:NetrwTreeListing(dirname) if w:netrw_liststyle == s:TREELIST " call Dfunc("NetrwTreeListing() bufname<".expand("%").">") -" call Decho("curdir<".a:dirname.">") -" call Decho("win#".winnr().": w:netrw_treetop ".(exists("w:netrw_treetop")? "exists" : "doesn't exist")." w:netrw_treedict ".(exists("w:netrw_treedict")? "exists" : "doesn't exit")) -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("curdir<".a:dirname.">",'~'.expand("<slnum>")) +" call Decho("win#".winnr().": w:netrw_treetop ".(exists("w:netrw_treetop")? "exists" : "doesn't exist")." w:netrw_treedict ".(exists("w:netrw_treedict")? "exists" : "doesn't exit"),'~'.expand("<slnum>")) +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) " update the treetop -" call Decho("update the treetop") +" call Decho("update the treetop",'~'.expand("<slnum>")) if !exists("w:netrw_treetop") let w:netrw_treetop= a:dirname -" call Decho("w:netrw_treetop<".w:netrw_treetop."> (reusing)") +" call Decho("w:netrw_treetop<".w:netrw_treetop."> (reusing)",'~'.expand("<slnum>")) elseif (w:netrw_treetop =~ ('^'.a:dirname) && s:Strlen(a:dirname) < s:Strlen(w:netrw_treetop)) || a:dirname !~ ('^'.w:netrw_treetop) let w:netrw_treetop= a:dirname -" call Decho("w:netrw_treetop<".w:netrw_treetop."> (went up)") +" call Decho("w:netrw_treetop<".w:netrw_treetop."> (went up)",'~'.expand("<slnum>")) endif - " insure that we have at least an empty treedict if !exists("w:netrw_treedict") + " insure that we have a treedict, albeit empty +" call Decho("initializing w:netrw_treedict to empty",'~'.expand("<slnum>")) let w:netrw_treedict= {} endif " update the directory listing for the current directory -" call Decho("updating dictionary with ".a:dirname.":[..directory listing..]") -" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." line($)=".line("$")) +" call Decho("updating dictionary with ".a:dirname.":[..directory listing..]",'~'.expand("<slnum>")) +" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." line($)=".line("$"),'~'.expand("<slnum>")) exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g@^\.\.\=/$@d' let w:netrw_treedict[a:dirname]= getline(w:netrw_bannercnt,line("$")) -" call Decho("w:treedict[".a:dirname."]= ".string(w:netrw_treedict[a:dirname])) +" call Decho("w:treedict[".a:dirname."]= ".string(w:netrw_treedict[a:dirname]),'~'.expand("<slnum>")) exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d" " if past banner, record word @@ -8563,16 +8825,16 @@ fun! s:NetrwTreeListing(dirname) else let fname= "" endif -" call Decho("fname<".fname.">") -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) " display from treetop on down NetrwKeepj call s:NetrwTreeDisplay(w:netrw_treetop,"") -" call Decho("s:NetrwTreeDisplay) setl noma nomod ro") +" call Decho("s:NetrwTreeDisplay) setl noma nomod ro",'~'.expand("<slnum>")) " remove any blank line remaining as line#1 (happens in treelisting mode with banner suppressed) while getline(1) =~ '^\s*$' && byte2line(1) > 0 -" call Decho("deleting blank line") +" call Decho("deleting blank line",'~'.expand("<slnum>")) 1d endwhile @@ -8591,25 +8853,32 @@ endfun fun! s:NetrwTreePath(treetop) " call Dfunc("s:NetrwTreePath() line#".line(".")."<".getline(".").">") let depth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') -" call Decho("(s:NetrwTreePath) depth<".depth."> 1st subst") +" call Decho("depth<".depth."> 1st subst",'~'.expand("<slnum>")) let depth = substitute(depth,'^'.s:treedepthstring,'','') -" call Decho("(s:NetrwTreePath) depth<".depth."> 2nd subst (first depth removed)") - if getline('.') =~ '/$' -" call Decho("extract tree directory from current line") - let treedir= substitute(getline('.'),'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') -" call Decho("(s:NetrwTreePath) treedir<".treedir.">") +" call Decho("depth<".depth."> 2nd subst (first depth removed)",'~'.expand("<slnum>")) + let curline= getline('.') +" call Decho("curline<".curline.'>','~'.expand("<slnum>")) + if curline =~ '/$' +" call Decho("extract tree directory from current line",'~'.expand("<slnum>")) + let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') +" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) + elseif curline =~ '@\s\+-->' +" call Decho("extract tree directory using symbolic link",'~'.expand("<slnum>")) + let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') + let treedir= substitute(treedir,'@\s\+-->.*$','','e') +" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) else -" call Decho("(s:NetrwTreePath) do not extract tree directory from current line and set treedir to empty") +" call Decho("do not extract tree directory from current line and set treedir to empty",'~'.expand("<slnum>")) let treedir= "" endif " construct treedir by searching backwards at correct depth -" call Decho("(s:NetrwTreePath) construct treedir by searching backwards for correct depth") -" call Decho("(s:NetrwTreePath) initial treedir<".treedir."> depth<".depth.">") +" call Decho("construct treedir by searching backwards for correct depth",'~'.expand("<slnum>")) +" call Decho("initial treedir<".treedir."> depth<".depth.">",'~'.expand("<slnum>")) while depth != "" && search('^'.depth.'[^'.s:treedepthstring.'].\{-}/$','bW') let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') let treedir= dirname.treedir let depth = substitute(depth,'^'.s:treedepthstring,'','') -" call Decho("(s:NetrwTreePath) constructing treedir<".treedir.">: dirname<".dirname."> while depth<".depth.">") +" call Decho("constructing treedir<".treedir.">: dirname<".dirname."> while depth<".depth.">",'~'.expand("<slnum>")) endwhile if a:treetop =~ '/$' let treedir= a:treetop.treedir @@ -8632,7 +8901,7 @@ fun! s:NetrwWideListing() " fpl: filenames per line " fpc: filenames per column setl ma noro -" call Decho("setl ma noro") +" call Decho("setl ma noro",'~'.expand("<slnum>")) let b:netrw_cpf= 0 if line("$") >= w:netrw_bannercnt exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif' @@ -8642,22 +8911,23 @@ fun! s:NetrwWideListing() return endif let b:netrw_cpf= b:netrw_cpf + 2 -" call Decho("b:netrw_cpf=max_filename_length+2=".b:netrw_cpf) +" call Decho("b:netrw_cpf=max_filename_length+2=".b:netrw_cpf,'~'.expand("<slnum>")) " determine qty files per line (fpl) let w:netrw_fpl= winwidth(0)/b:netrw_cpf if w:netrw_fpl <= 0 let w:netrw_fpl= 1 endif -" call Decho("fpl= [winwidth=".winwidth(0)."]/[b:netrw_cpf=".b:netrw_cpf.']='.w:netrw_fpl) +" call Decho("fpl= [winwidth=".winwidth(0)."]/[b:netrw_cpf=".b:netrw_cpf.']='.w:netrw_fpl,'~'.expand("<slnum>")) " make wide display - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^.*$/\=escape(printf("%-'.b:netrw_cpf.'s",submatch(0)),"\\")/' + " fpc: files per column of wide listing + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^.*$/\=escape(printf("%-'.b:netrw_cpf.'S",submatch(0)),"\\")/' NetrwKeepj call histdel("/",-1) let fpc = (line("$") - w:netrw_bannercnt + w:netrw_fpl)/w:netrw_fpl let newcolstart = w:netrw_bannercnt + fpc let newcolend = newcolstart + fpc - 1 -" call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]") +" call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("<slnum>")) if has("clipboard") sil! let keepregstar = @* endif @@ -8680,9 +8950,9 @@ fun! s:NetrwWideListing() NetrwKeepj call histdel("/",-1) exe 'nno <buffer> <silent> w :call search(''^.\\|\s\s\zs\S'',''W'')'."\<cr>" exe 'nno <buffer> <silent> b :call search(''^.\\|\s\s\zs\S'',''bW'')'."\<cr>" -" call Decho("NetrwWideListing) setl noma nomod ro") +" call Decho("NetrwWideListing) setl noma nomod ro",'~'.expand("<slnum>")) exe "setl ".g:netrw_bufsettings -" call Decho("(NetrwWideListing) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("(NetrwWideListing) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("NetrwWideListing") return else @@ -8699,26 +8969,27 @@ endfun " --------------------------------------------------------------------- " s:PerformListing: {{{2 fun! s:PerformListing(islocal) -" call Dfunc("s:PerformListing(islocal=".a:islocal.") bufnr(%)=".bufnr("%")."<".bufname("%").">") -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (enter)") +" call Dfunc("s:PerformListing(islocal=".a:islocal.")") +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) +" call Decho("settings: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (enter)",'~'.expand("<slnum>")) " set up syntax highlighting {{{3 -" call Decho("set up syntax highlighting (ie. setl ft=netrw)") +" call Decho("--set up syntax highlighting (ie. setl ft=netrw)",'~'.expand("<slnum>")) sil! setl ft=netrw NetrwKeepj call s:NetrwSafeOptions() setl noro ma -" call Decho("setl noro ma bh=".&bh) +" call Decho("setl noro ma bh=".&bh,'~'.expand("<slnum>")) " if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 " Decho -" call Decho("(netrw) Processing your browsing request...") +" call Decho("(netrw) Processing your browsing request...",'~'.expand("<slnum>")) " endif " Decho -" call Decho('w:netrw_liststyle='.(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a')) +" call Decho('w:netrw_liststyle='.(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>")) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") " force a refresh for tree listings -" call Decho("force refresh for treelisting: clear buffer<".expand("%")."> with :%d") - sil! NetrwKeepj %d +" call Decho("force refresh for treelisting: clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ endif " save current directory on directory history list @@ -8726,21 +8997,34 @@ fun! s:PerformListing(islocal) " Set up the banner {{{3 if g:netrw_banner -" call Decho("set up banner") +" call Decho("--set up banner",'~'.expand("<slnum>")) NetrwKeepj call setline(1,'" ============================================================================') - NetrwKeepj call setline(2,'" Netrw Directory Listing (netrw '.g:loaded_netrw.')') + if exists("g:netrw_pchk") + " this undocumented option allows pchk to run with different versions of netrw without causing spurious + " failure detections. + NetrwKeepj call setline(2,'" Netrw Directory Listing') + else + NetrwKeepj call setline(2,'" Netrw Directory Listing (netrw '.g:loaded_netrw.')') + endif + if exists("g:netrw_pchk") + let curdir= substitute(b:netrw_curdir,expand("$HOME"),'~','') + else + let curdir= b:netrw_curdir + endif if exists("g:netrw_bannerbackslash") && g:netrw_bannerbackslash - NetrwKeepj call setline(3,'" '.substitute(b:netrw_curdir,'/','\\','g')) + NetrwKeepj call setline(3,'" '.substitute(curdir,'/','\\','g')) else - NetrwKeepj call setline(3,'" '.b:netrw_curdir) + NetrwKeepj call setline(3,'" '.curdir) endif let w:netrw_bannercnt= 3 NetrwKeepj exe "sil! NetrwKeepj ".w:netrw_bannercnt else +" call Decho("--no banner",'~'.expand("<slnum>")) NetrwKeepj 1 let w:netrw_bannercnt= 1 endif -" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." win#".winnr()) +" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." win#".winnr(),'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) let sortby= g:netrw_sort_by if g:netrw_sort_direction =~ "^r" @@ -8749,28 +9033,28 @@ fun! s:PerformListing(islocal) " Sorted by... {{{3 if g:netrw_banner -" call Decho("handle specified sorting: g:netrw_sort_by<".g:netrw_sort_by.">") +" call Decho("--handle specified sorting: g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>")) if g:netrw_sort_by =~ "^n" -" call Decho("directories will be sorted by name") +" call Decho("directories will be sorted by name",'~'.expand("<slnum>")) " sorted by name NetrwKeepj put ='\" Sorted by '.sortby NetrwKeepj put ='\" Sort sequence: '.g:netrw_sort_sequence let w:netrw_bannercnt= w:netrw_bannercnt + 2 else -" call Decho("directories will be sorted by size or time") +" call Decho("directories will be sorted by size or time",'~'.expand("<slnum>")) " sorted by size or date NetrwKeepj put ='\" Sorted by '.sortby let w:netrw_bannercnt= w:netrw_bannercnt + 1 endif exe "sil! NetrwKeepj ".w:netrw_bannercnt " else " Decho -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) endif " show copy/move target, if any if g:netrw_banner if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") -" call Decho("show copy/move target<".s:netrwmftgt.">") +" call Decho("--show copy/move target<".s:netrwmftgt.">",'~'.expand("<slnum>")) NetrwKeepj put ='' if s:netrwmftgt_islocal sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (local)') @@ -8779,14 +9063,14 @@ fun! s:PerformListing(islocal) endif let w:netrw_bannercnt= w:netrw_bannercnt + 1 else -" call Decho("s:netrwmftgt does not exist, don't make Copy/Move Tgt") +" call Decho("s:netrwmftgt does not exist, don't make Copy/Move Tgt",'~'.expand("<slnum>")) endif exe "sil! NetrwKeepj ".w:netrw_bannercnt endif " Hiding... -or- Showing... {{{3 if g:netrw_banner -" call Decho("handle hiding/showing (g:netrw_hide=".g:netrw_list_hide." g:netrw_list_hide<".g:netrw_list_hide.">)") +" call Decho("--handle hiding/showing (g:netrw_hide=".g:netrw_list_hide." g:netrw_list_hide<".g:netrw_list_hide.">)",'~'.expand("<slnum>")) if g:netrw_list_hide != "" && g:netrw_hide if g:netrw_hide == 1 NetrwKeepj put ='\" Hiding: '.g:netrw_list_hide @@ -8797,34 +9081,34 @@ fun! s:PerformListing(islocal) endif exe "NetrwKeepj ".w:netrw_bannercnt -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) let quickhelp = g:netrw_quickhelp%len(s:QuickHelp) -" call Decho("quickhelp =".quickhelp) +" call Decho("quickhelp =".quickhelp,'~'.expand("<slnum>")) NetrwKeepj put ='\" Quick Help: <F1>:help '.s:QuickHelp[quickhelp] -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) NetrwKeepj put ='\" ==============================================================================' let w:netrw_bannercnt= w:netrw_bannercnt + 2 " else " Decho -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) endif " bannercnt should index the line just after the banner if g:netrw_banner let w:netrw_bannercnt= w:netrw_bannercnt + 1 exe "sil! NetrwKeepj ".w:netrw_bannercnt -" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." (should index line just after banner) line($)=".line("$")) +" call Decho("--w:netrw_bannercnt=".w:netrw_bannercnt." (should index line just after banner) line($)=".line("$"),'~'.expand("<slnum>")) " else " Decho -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) endif " get list of files -" call Decho("Get list of files - islocal=".a:islocal) +" call Decho("--Get list of files - islocal=".a:islocal,'~'.expand("<slnum>")) if a:islocal NetrwKeepj call s:LocalListing() else " remote NetrwKeepj let badresult= s:NetrwRemoteListing() if badresult -" call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." win#".winnr()." buf#".bufnr("%")."<".bufname("%").">") +" call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) " call Dret("s:PerformListing : error detected by NetrwRemoteListing") return endif @@ -8834,24 +9118,25 @@ fun! s:PerformListing(islocal) if !exists("w:netrw_bannercnt") let w:netrw_bannercnt= 0 endif -" call Decho("g:netrw_banner=".g:netrw_banner." w:netrw_bannercnt=".w:netrw_bannercnt." (banner complete)") -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("--manipulate directory listing (hide, sort)",'~'.expand("<slnum>")) +" call Decho("g:netrw_banner=".g:netrw_banner." w:netrw_bannercnt=".w:netrw_bannercnt." (banner complete)",'~'.expand("<slnum>")) +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) if !g:netrw_banner || line("$") >= w:netrw_bannercnt -" call Decho("manipulate directory listing (hide)") -" call Decho("g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">") +" call Decho("manipulate directory listing (hide)",'~'.expand("<slnum>")) +" call Decho("g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>")) if g:netrw_hide && g:netrw_list_hide != "" NetrwKeepj call s:NetrwListHide() endif if !g:netrw_banner || line("$") >= w:netrw_bannercnt -" call Decho("manipulate directory listing (sort) : g:netrw_sort_by<".g:netrw_sort_by.">") +" call Decho("manipulate directory listing (sort) : g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>")) if g:netrw_sort_by =~ "^n" " sort by name NetrwKeepj call s:NetrwSetSort() if !g:netrw_banner || w:netrw_bannercnt < line("$") -" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")") +" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) if g:netrw_sort_direction =~ 'n' " normal direction sorting exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options @@ -8861,18 +9146,39 @@ fun! s:PerformListing(islocal) endif endif " remove priority pattern prefix -" call Decho("remove priority pattern prefix") +" call Decho("remove priority pattern prefix",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e' NetrwKeepj call histdel("/",-1) + elseif g:netrw_sort_by =~ "^ext" + " sort by extension + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g+/+s/^/001'.g:netrw_sepchr.'/' + NetrwKeepj call histdel("/",-1) + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+[./]+s/^/002'.g:netrw_sepchr.'/' + NetrwKeepj call histdel("/",-1) + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+['.g:netrw_sepchr.'/]+s/^\(.*\.\)\(.\{-\}\)$/\2'.g:netrw_sepchr.'&/e' + NetrwKeepj call histdel("/",-1) + if !g:netrw_banner || w:netrw_bannercnt < line("$") +" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) + if g:netrw_sort_direction =~ 'n' + " normal direction sorting + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options + else + " reverse direction sorting + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options + endif + endif + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^.\{-}'.g:netrw_sepchr.'//e' + NetrwKeepj call histdel("/",-1) + elseif a:islocal if !g:netrw_banner || w:netrw_bannercnt < line("$") -" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction) +" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction,'~'.expand("<slnum>")) if g:netrw_sort_direction =~ 'n' -" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort') +" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort','~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options else -" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort!') +" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort!','~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options endif exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{-}\///e' @@ -8881,62 +9187,72 @@ fun! s:PerformListing(islocal) endif elseif g:netrw_sort_direction =~ 'r' -" call Decho('(s:PerformListing) reverse the sorted listing') +" call Decho('(s:PerformListing) reverse the sorted listing','~'.expand("<slnum>")) if !g:netrw_banner || w:netrw_bannercnt < line('$') exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g/^/m '.w:netrw_bannercnt call histdel("/",-1) endif endif endif -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) " convert to wide/tree listing {{{3 -" call Decho("modify display if wide/tree listing style") -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#1)") +" call Decho("--modify display if wide/tree listing style",'~'.expand("<slnum>")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#1)",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwWideListing() -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#2)") +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#2)",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwTreeListing(b:netrw_curdir) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#3)") +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#3)",'~'.expand("<slnum>")) " resolve symbolic links if local and (thin or tree) if a:islocal && (w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:TREELIST) - g/@$/call s:ShowLink() +" call Decho("--resolve symbolic links if local and thin|tree",'~'.expand("<slnum>")) + g/@$/call s:ShowLink() endif - if exists("w:netrw_bannercnt") && (line("$") > w:netrw_bannercnt || !g:netrw_banner) + if exists("w:netrw_bannercnt") && (line("$") >= w:netrw_bannercnt || !g:netrw_banner) " place cursor on the top-left corner of the file listing -" call Decho("place cursor on top-left corner of file listing") - exe 'sil! NetrwKeepj '.w:netrw_bannercnt +" call Decho("--place cursor on top-left corner of file listing",'~'.expand("<slnum>")) + exe 'sil! '.w:netrw_bannercnt sil! NetrwKeepj norm! 0 +" call Decho(" tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) + else +" call Decho("--did NOT place cursor on top-left corner",'~'.expand("<slnum>")) +" call Decho(" w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a'),'~'.expand("<slnum>")) +" call Decho(" line($)=".line("$"),'~'.expand("<slnum>")) +" call Decho(" g:netrw_banner=".(exists("g:netrw_banner")? g:netrw_banner : 'n/a'),'~'.expand("<slnum>")) endif " record previous current directory let w:netrw_prvdir= b:netrw_curdir -" call Decho("record netrw_prvdir<".w:netrw_prvdir.">") +" call Decho("--record netrw_prvdir<".w:netrw_prvdir.">",'~'.expand("<slnum>")) " save certain window-oriented variables into buffer-oriented variables {{{3 -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#4)") +" call Decho("--save some window-oriented variables into buffer oriented variables",'~'.expand("<slnum>")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#4)",'~'.expand("<slnum>")) NetrwKeepj call s:SetBufWinVars() -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#5)") +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#5)",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwOptionRestore("w:") -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#6)") +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#6)",'~'.expand("<slnum>")) " set display to netrw display settings -" call Decho("set display to netrw display settings (".g:netrw_bufsettings.")") +" call Decho("--set display to netrw display settings (".g:netrw_bufsettings.")",'~'.expand("<slnum>")) exe "setl ".g:netrw_bufsettings -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#7)") +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#7)",'~'.expand("<slnum>")) if g:netrw_liststyle == s:LONGLIST -" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1)) +" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) exe "setl ts=".(g:netrw_maxfilenamelen+1) endif if exists("s:treecurpos") -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#8)") +" call Decho("s:treecurpos exists; restore posn",'~'.expand("<slnum>")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (internal#8)",'~'.expand("<slnum>")) NetrwKeepj call netrw#RestorePosn(s:treecurpos) unlet s:treecurpos endif -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (return)") +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (return)",'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) " call Dret("s:PerformListing : curpos<".string(getpos(".")).">") endfun @@ -8947,7 +9263,7 @@ fun! s:SetupNetrwStatusLine(statline) if !exists("s:netrw_setup_statline") let s:netrw_setup_statline= 1 -" call Decho("do first-time status line setup") +" call Decho("do first-time status line setup",'~'.expand("<slnum>")) if !exists("s:netrw_users_stl") let s:netrw_users_stl= &stl @@ -8961,7 +9277,7 @@ fun! s:SetupNetrwStatusLine(statline) redir @a try hi User9 - catch /^Vim\%((\a\+)\)\=:E411/ + catch /^Vim\%((\a\{3,})\)\=:E411/ if &bg == "dark" hi User9 ctermfg=yellow ctermbg=blue guifg=yellow guibg=blue else @@ -8977,7 +9293,7 @@ fun! s:SetupNetrwStatusLine(statline) " make sure statusline is displayed let &stl=a:statline setl laststatus=2 -" call Decho("stl=".&stl) +" call Decho("stl=".&stl,'~'.expand("<slnum>")) redraw " call Dret("SetupNetrwStatusLine : stl=".&stl) @@ -8994,7 +9310,7 @@ endfun " enforced here. fun! s:NetrwRemoteFtpCmd(path,listcmd) " call Dfunc("NetrwRemoteFtpCmd(path<".a:path."> listcmd<".a:listcmd.">) w:netrw_method=".(exists("w:netrw_method")? w:netrw_method : (exists("b:netrw_method")? b:netrw_method : "???"))) -" call Decho("line($)=".line("$")." w:netrw_bannercnt=".w:netrw_bannercnt) +" call Decho("line($)=".line("$")." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) " sanity check: {{{3 if !exists("w:netrw_method") if exists("b:netrw_method") @@ -9009,11 +9325,11 @@ fun! s:NetrwRemoteFtpCmd(path,listcmd) " WinXX ftp uses unix style input, so set ff to unix " {{{3 let ffkeep= &ff setl ma ff=unix noro -" call Decho("setl ma ff=unix noro") +" call Decho("setl ma ff=unix noro",'~'.expand("<slnum>")) " clear off any older non-banner lines " {{{3 " note that w:netrw_bannercnt indexes the line after the banner -" call Decho('exe sil! NetrwKeepj '.w:netrw_bannercnt.",$d (clear off old non-banner lines)") +" call Decho('exe sil! NetrwKeepj '.w:netrw_bannercnt.",$d (clear off old non-banner lines)",'~'.expand("<slnum>")) exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d" "......................................... @@ -9024,16 +9340,16 @@ fun! s:NetrwRemoteFtpCmd(path,listcmd) endif if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif NetrwKeepj call setline(line("$")+1,a:listcmd) -" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."))' +" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))' if exists("g:netrw_port") && g:netrw_port != "" -" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)." ".shellescape(g:netrw_port,1)) - exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)." ".shellescape(g:netrw_port,1) +" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1),'~'.expand("<slnum>")) + exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1) else -" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1)) - exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".shellescape(g:netrw_machine,1) +" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1),'~'.expand("<slnum>")) + exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1) endif "......................................... @@ -9048,7 +9364,7 @@ fun! s:NetrwRemoteFtpCmd(path,listcmd) " handle userid and password let host= substitute(g:netrw_machine,'\..*$','','') -" call Decho("host<".host.">") +" call Decho("host<".host.">",'~'.expand("<slnum>")) if exists("s:netrw_hup") && exists("s:netrw_hup[host]") call NetUserPass("ftp:".host) endif @@ -9068,7 +9384,7 @@ fun! s:NetrwRemoteFtpCmd(path,listcmd) endif if exists("g:netrw_ftpextracmd") NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('.')) +" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) endif NetrwKeepj call setline(line("$")+1,a:listcmd) @@ -9077,11 +9393,11 @@ fun! s:NetrwRemoteFtpCmd(path,listcmd) " -n unix : DON'T use <.netrc>, even though it exists " -n win32: quit being obnoxious about password if exists("w:netrw_bannercnt") -" exe w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."))' +" exe w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))' call s:NetrwExe(s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) " else " Decho -" call Decho("WARNING: w:netrw_bannercnt doesn't exist!") -" g/^./call Decho("SKIPPING ftp#".line(".").": ".getline(".")) +" call Decho("WARNING: w:netrw_bannercnt doesn't exist!",'~'.expand("<slnum>")) +" g/^./call Decho("SKIPPING ftp#".line(".").": ".getline("."),'~'.expand("<slnum>")) endif "......................................... @@ -9148,9 +9464,9 @@ fun! s:NetrwRemoteListing() " sanity check: if exists("b:netrw_method") && b:netrw_method =~ '[235]' -" call Decho("b:netrw_method=".b:netrw_method) +" call Decho("b:netrw_method=".b:netrw_method,'~'.expand("<slnum>")) if !executable("ftp") -" call Decho("ftp is not executable") +" call Decho("ftp is not executable",'~'.expand("<slnum>")) if !exists("g:netrw_quiet") call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ftp",18) endif @@ -9160,7 +9476,7 @@ fun! s:NetrwRemoteListing() endif elseif !exists("g:netrw_list_cmd") || g:netrw_list_cmd == '' -" call Decho("g:netrw_list_cmd<",(exists("g:netrw_list_cmd")? 'n/a' : "-empty-").">") +" call Decho("g:netrw_list_cmd<",(exists("g:netrw_list_cmd")? 'n/a' : "-empty-").">",'~'.expand("<slnum>")) if !exists("g:netrw_quiet") if g:netrw_list_cmd == "" NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your g:netrw_list_cmd is empty; perhaps ".g:netrw_ssh_cmd." is not executable on your system",47) @@ -9173,16 +9489,16 @@ fun! s:NetrwRemoteListing() " call Dret("s:NetrwRemoteListing -1") return -1 endif " (remote handling sanity check) -" call Decho("passed remote listing sanity checks") +" call Decho("passed remote listing sanity checks",'~'.expand("<slnum>")) if exists("b:netrw_method") -" call Decho("setting w:netrw_method to b:netrw_method<".b:netrw_method.">") +" call Decho("setting w:netrw_method to b:netrw_method<".b:netrw_method.">",'~'.expand("<slnum>")) let w:netrw_method= b:netrw_method endif if s:method == "ftp" " use ftp to get remote file listing {{{3 -" call Decho("use ftp to get remote file listing") +" call Decho("use ftp to get remote file listing",'~'.expand("<slnum>")) let s:method = "ftp" let listcmd = g:netrw_ftp_list_cmd if g:netrw_sort_by =~ '^t' @@ -9190,9 +9506,9 @@ fun! s:NetrwRemoteListing() elseif g:netrw_sort_by =~ '^s' let listcmd= g:netrw_ftp_sizelist_cmd endif -" call Decho("listcmd<".listcmd."> (using g:netrw_ftp_list_cmd)") +" call Decho("listcmd<".listcmd."> (using g:netrw_ftp_list_cmd)",'~'.expand("<slnum>")) call s:NetrwRemoteFtpCmd(s:path,listcmd) -" exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("raw listing: ".getline("."))' +" exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("raw listing: ".getline("."),''~''.expand("<slnum>"))' " report on missing file or directory messages if search('[Nn]o such file or directory\|Failed to change directory') @@ -9210,7 +9526,7 @@ fun! s:NetrwRemoteListing() if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || w:netrw_liststyle == s:TREELIST " shorten the listing -" call Decho("generate short listing") +" call Decho("generate short listing",'~'.expand("<slnum>")) exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt " cleanup @@ -9225,21 +9541,21 @@ fun! s:NetrwRemoteListing() let line1= line(".") exe "sil! NetrwKeepj ".w:netrw_bannercnt let line2= search('\.\.\/\%(\s\|$\)','cnW') -" call Decho("search(".'\.\.\/\%(\s\|$\)'."','cnW')=".line2." w:netrw_bannercnt=".w:netrw_bannercnt) +" call Decho("search(".'\.\.\/\%(\s\|$\)'."','cnW')=".line2." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) if line2 == 0 -" call Decho("netrw is putting ../ into listing") +" call Decho("netrw is putting ../ into listing",'~'.expand("<slnum>")) sil! NetrwKeepj put='../' endif exe "sil! NetrwKeepj ".line1 sil! NetrwKeepj norm! 0 -" call Decho("line1=".line1." line2=".line2." line(.)=".line(".")) +" call Decho("line1=".line1." line2=".line2." line(.)=".line("."),'~'.expand("<slnum>")) if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup -" call Decho("M$ ftp cleanup") +" call Decho("M$ ftp cleanup",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+//' NetrwKeepj call histdel("/",-1) else " normal ftp cleanup -" call Decho("normal ftp cleanup") +" call Decho("normal ftp cleanup",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2/e' exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*/$#/#e' exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*$#/#e' @@ -9251,12 +9567,12 @@ fun! s:NetrwRemoteListing() else " use ssh to get remote file listing {{{3 -" call Decho("use ssh to get remote file listing: s:path<".s:path.">") +" call Decho("use ssh to get remote file listing: s:path<".s:path.">",'~'.expand("<slnum>")) let listcmd= s:MakeSshCmd(g:netrw_list_cmd) -" call Decho("listcmd<".listcmd."> (using g:netrw_list_cmd)") +" call Decho("listcmd<".listcmd."> (using g:netrw_list_cmd)",'~'.expand("<slnum>")) if g:netrw_scp_cmd =~ '^pscp' -" call Decho("1: exe r! ".shellescape(listcmd.s:path, 1)) - exe "NetrwKeepj r! ".listcmd.shellescape(s:path, 1) +" call Decho("1: exe r! ".s:ShellEscape(listcmd.s:path, 1),'~'.expand("<slnum>")) + exe "NetrwKeepj r! ".listcmd.s:ShellEscape(s:path, 1) " remove rubbish and adjust listing format of 'pscp' to 'ssh ls -FLa' like sil! NetrwKeepj g/^Listing directory/NetrwKeepj d sil! NetrwKeepj g/^d[-rwx][-rwx][-rwx]/NetrwKeepj s+$+/+e @@ -9270,18 +9586,18 @@ fun! s:NetrwRemoteListing() endif else if s:path == "" -" call Decho("2: exe r! ".listcmd) +" call Decho("2: exe r! ".listcmd,'~'.expand("<slnum>")) exe "NetrwKeepj keepalt r! ".listcmd else -" call Decho("3: exe r! ".listcmd.' '.shellescape(fnameescape(s:path),1)) - exe "NetrwKeepj keepalt r! ".listcmd.' '.shellescape(fnameescape(s:path),1) -" call Decho("listcmd<".listcmd."> path<".s:path.">") +" call Decho("3: exe r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1),'~'.expand("<slnum>")) + exe "NetrwKeepj keepalt r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1) +" call Decho("listcmd<".listcmd."> path<".s:path.">",'~'.expand("<slnum>")) endif endif " cleanup if g:netrw_ssh_browse_reject != "" -" call Decho("cleanup: exe sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d") +" call Decho("cleanup: exe sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d",'~'.expand("<slnum>")) exe "sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d" NetrwKeepj call histdel("/",-1) endif @@ -9289,7 +9605,7 @@ fun! s:NetrwRemoteListing() if w:netrw_liststyle == s:LONGLIST " do a long listing; these substitutions need to be done prior to sorting {{{3 -" call Decho("fix long listing:") +" call Decho("fix long listing:",'~'.expand("<slnum>")) if s:method == "ftp" " cleanup @@ -9312,12 +9628,12 @@ fun! s:NetrwRemoteListing() endif if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup -" call Decho("M$ ftp site listing cleanup") +" call Decho("M$ ftp site listing cleanup",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+\)\(\w.*\)$/\2\t\1/' elseif exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") -" call Decho("normal ftp site listing cleanup: bannercnt=".w:netrw_bannercnt." line($)=".line("$")) +" call Decho("normal ftp site listing cleanup: bannercnt=".w:netrw_bannercnt." line($)=".line("$"),'~'.expand("<slnum>")) exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/ -> .*$//e' - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2\t\1/e' + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2 \t\1/e' exe 'sil NetrwKeepj '.w:netrw_bannercnt NetrwKeepj call histdel("/",-1) NetrwKeepj call histdel("/",-1) @@ -9326,7 +9642,7 @@ fun! s:NetrwRemoteListing() endif " if exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") " Decho -" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("listing: ".getline("."))' +" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("listing: ".getline("."),''~''.expand("<slnum>"))' " endif " Decho " call Dret("s:NetrwRemoteListing 0") @@ -9337,13 +9653,13 @@ endfun " s:NetrwRemoteRm: remove/delete a remote file or directory {{{2 fun! s:NetrwRemoteRm(usrhost,path) range " call Dfunc("s:NetrwRemoteRm(usrhost<".a:usrhost."> path<".a:path.">) virtcol=".virtcol(".")) -" call Decho("firstline=".a:firstline." lastline=".a:lastline) +" call Decho("firstline=".a:firstline." lastline=".a:lastline,'~'.expand("<slnum>")) let svpos= netrw#SavePosn() let all= 0 if exists("s:netrwmarkfilelist_{bufnr('%')}") " remove all marked files -" call Decho("remove all marked files with bufnr#".bufnr("%")) +" call Decho("remove all marked files with bufnr#".bufnr("%"),'~'.expand("<slnum>")) for fname in s:netrwmarkfilelist_{bufnr("%")} let ok= s:NetrwRemoteRmFile(a:path,fname,all) if ok =~ 'q\%[uit]' @@ -9356,10 +9672,12 @@ fun! s:NetrwRemoteRm(usrhost,path) range else " remove files specified by range -" call Decho("remove files specified by range") +" call Decho("remove files specified by range",'~'.expand("<slnum>")) " preparation for removing multiple files/directories - let ctr= a:firstline + let keepsol = &l:sol + setl nosol + let ctr = a:firstline " remove multiple files and directories while ctr <= a:lastline @@ -9372,10 +9690,11 @@ fun! s:NetrwRemoteRm(usrhost,path) range endif let ctr= ctr + 1 endwhile + let &l:sol = keepsol endif " refresh the (remote) directory listing -" call Decho("refresh remote directory listing") +" call Decho("refresh remote directory listing",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./')) NetrwKeepj call netrw#RestorePosn(svpos) @@ -9392,10 +9711,10 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) if a:rmfile !~ '^"' && (a:rmfile =~ '@$' || a:rmfile !~ '[\/]$') " attempt to remove file -" call Decho("attempt to remove file (all=".all.")") +" call Decho("attempt to remove file (all=".all.")",'~'.expand("<slnum>")) if !all echohl Statement -" call Decho("case all=0:") +" call Decho("case all=0:",'~'.expand("<slnum>")) call inputsave() let ok= input("Confirm deletion of file<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") call inputrestore() @@ -9410,47 +9729,53 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) endif if all || ok =~ 'y\%[es]' || ok == "" -" call Decho("case all=".all." or ok<".ok.">".(exists("w:netrw_method")? ': netrw_method='.w:netrw_method : "")) +" call Decho("case all=".all." or ok<".ok.">".(exists("w:netrw_method")? ': netrw_method='.w:netrw_method : ""),'~'.expand("<slnum>")) if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) -" call Decho("case ftp:") +" call Decho("case ftp:",'~'.expand("<slnum>")) let path= a:path - if path =~ '^\a\+://' - let path= substitute(path,'^\a\+://[^/]\+/','','') + if path =~ '^\a\{3,}://' + let path= substitute(path,'^\a\{3,}://[^/]\+/','','') endif sil! NetrwKeepj .,$d call s:NetrwRemoteFtpCmd(path,"delete ".'"'.a:rmfile.'"') else -" call Decho("case ssh: g:netrw_rm_cmd<".g:netrw_rm_cmd.">") +" call Decho("case ssh: g:netrw_rm_cmd<".g:netrw_rm_cmd.">",'~'.expand("<slnum>")) let netrw_rm_cmd= s:MakeSshCmd(g:netrw_rm_cmd) -" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">") +" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>")) if !exists("b:netrw_curdir") NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53) let ok="q" else let remotedir= substitute(b:netrw_curdir,'^.*//[^/]\+/\(.*\)$','\1','') -" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">") -" call Decho("remotedir<".remotedir.">") -" call Decho("rmfile<".a:rmfile.">") +" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>")) +" call Decho("remotedir<".remotedir.">",'~'.expand("<slnum>")) +" call Decho("rmfile<".a:rmfile.">",'~'.expand("<slnum>")) if remotedir != "" - let netrw_rm_cmd= netrw_rm_cmd." ".shellescape(fnameescape(remotedir.a:rmfile)) + let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(remotedir.a:rmfile)) else - let netrw_rm_cmd= netrw_rm_cmd." ".shellescape(fnameescape(a:rmfile)) + let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(a:rmfile)) endif -" call Decho("call system(".netrw_rm_cmd.")") +" call Decho("call system(".netrw_rm_cmd.")",'~'.expand("<slnum>")) let ret= system(netrw_rm_cmd) - if ret != 0 - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60) + if v:shell_error != 0 + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir + call netrw#ErrorMsg(s:ERROR,"remove failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-c)",102) + else + call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60) + endif + else if ret != 0 + call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60) endif -" call Decho("returned=".ret." errcode=".v:shell_error) +" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) endif endif elseif ok =~ 'q\%[uit]' -" call Decho("ok==".ok) +" call Decho("ok==".ok,'~'.expand("<slnum>")) endif else " attempt to remove directory -" call Decho("attempt to remove directory") +" call Decho("attempt to remove directory",'~'.expand("<slnum>")) if !all call inputsave() let ok= input("Confirm deletion of directory<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") @@ -9469,17 +9794,17 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rmdir ".a:rmfile) else let rmfile = substitute(a:path.a:rmfile,'/$','','') - let netrw_rmdir_cmd = s:MakeSshCmd(netrw#WinPath(g:netrw_rmdir_cmd)).' '.shellescape(netrw#WinPath(rmfile)) -" call Decho("attempt to remove dir: system(".netrw_rmdir_cmd.")") + let netrw_rmdir_cmd = s:MakeSshCmd(netrw#WinPath(g:netrw_rmdir_cmd)).' '.s:ShellEscape(netrw#WinPath(rmfile)) +" call Decho("attempt to remove dir: system(".netrw_rmdir_cmd.")",'~'.expand("<slnum>")) let ret= system(netrw_rmdir_cmd) -" call Decho("returned=".ret." errcode=".v:shell_error) +" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) if v:shell_error != 0 -" call Decho("v:shell_error not 0") - let netrw_rmf_cmd= s:MakeSshCmd(netrw#WinPath(g:netrw_rmf_cmd)).' '.shellescape(netrw#WinPath(substitute(rmfile,'[\/]$','','e'))) -" call Decho("2nd attempt to remove dir: system(".netrw_rmf_cmd.")") +" call Decho("v:shell_error not 0",'~'.expand("<slnum>")) + let netrw_rmf_cmd= s:MakeSshCmd(netrw#WinPath(g:netrw_rmf_cmd)).' '.s:ShellEscape(netrw#WinPath(substitute(rmfile,'[\/]$','','e'))) +" call Decho("2nd attempt to remove dir: system(".netrw_rmf_cmd.")",'~'.expand("<slnum>")) let ret= system(netrw_rmf_cmd) -" call Decho("returned=".ret." errcode=".v:shell_error) +" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) if v:shell_error != 0 && !exists("g:netrw_quiet") NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",22) @@ -9488,7 +9813,7 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) endif elseif ok =~ 'q\%[uit]' -" call Decho("ok==".ok) +" call Decho("ok==".ok,'~'.expand("<slnum>")) endif endif @@ -9509,10 +9834,10 @@ fun! s:NetrwRemoteRename(usrhost,path) range " rename files given by the markfilelist if exists("s:netrwmarkfilelist_{bufnr('%')}") for oldname in s:netrwmarkfilelist_{bufnr("%")} -" call Decho("oldname<".oldname.">") +" call Decho("oldname<".oldname.">",'~'.expand("<slnum>")) if exists("subfrom") let newname= substitute(oldname,subfrom,subto,'') -" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">") +" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">",'~'.expand("<slnum>")) else call inputsave() let newname= input("Moving ".oldname." to : ",oldname) @@ -9521,16 +9846,16 @@ fun! s:NetrwRemoteRename(usrhost,path) range let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','') let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','') let newname = substitute(oldname,subfrom,subto,'') -" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">") +" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">",'~'.expand("<slnum>")) endif endif - + if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname) else - let oldname= shellescape(a:path.oldname) - let newname= shellescape(a:path.newname) -" call Decho("system(netrw#WinPath(".rename_cmd.") ".oldname.' '.newname.")") + let oldname= s:ShellEscape(a:path.oldname) + let newname= s:ShellEscape(a:path.newname) +" call Decho("system(netrw#WinPath(".rename_cmd.") ".oldname.' '.newname.")",'~'.expand("<slnum>")) let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname) endif @@ -9540,11 +9865,13 @@ fun! s:NetrwRemoteRename(usrhost,path) range else " attempt to rename files/directories + let keepsol= &l:sol + setl nosol while ctr <= a:lastline exe "NetrwKeepj ".ctr let oldname= s:NetrwGetWord() -" call Decho("oldname<".oldname.">") +" call Decho("oldname<".oldname.">",'~'.expand("<slnum>")) call inputsave() let newname= input("Moving ".oldname." to : ",oldname) @@ -9553,14 +9880,15 @@ fun! s:NetrwRemoteRename(usrhost,path) range if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname) else - let oldname= shellescape(a:path.oldname) - let newname= shellescape(a:path.newname) -" call Decho("system(netrw#WinPath(".rename_cmd.") ".oldname.' '.newname.")") + let oldname= s:ShellEscape(a:path.oldname) + let newname= s:ShellEscape(a:path.newname) +" call Decho("system(netrw#WinPath(".rename_cmd.") ".oldname.' '.newname.")",'~'.expand("<slnum>")) let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname) endif let ctr= ctr + 1 endwhile + let &l:sol= keepsol endif " refresh the directory @@ -9589,37 +9917,37 @@ fun! netrw#FileUrlRead(fname) " call Dfunc("netrw#FileUrlRead(fname<".a:fname.">)") let fname = a:fname if fname =~ '^file://localhost/' -" call Decho('converting file://localhost/ -to- file:///') +" call Decho('converting file://localhost/ -to- file:///','~'.expand("<slnum>")) let fname= substitute(fname,'^file://localhost/','file:///','') -" call Decho("fname<".fname.">") +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) endif if (has("win32") || has("win95") || has("win64") || has("win16")) if fname =~ '^file:///\=\a[|:]/' -" call Decho('converting file:///\a|/ -to- file://\a:/') +" call Decho('converting file:///\a|/ -to- file://\a:/','~'.expand("<slnum>")) let fname = substitute(fname,'^file:///\=\(\a\)[|:]/','file://\1:/','') -" call Decho("fname<".fname.">") +" call Decho("fname<".fname.">",'~'.expand("<slnum>")) endif endif let fname2396 = netrw#RFC2396(fname) let fname2396e= fnameescape(fname2396) let plainfname= substitute(fname2396,'file://\(.*\)','\1',"") if (has("win32") || has("win95") || has("win64") || has("win16")) -" call Decho("windows exception for plainfname") +" call Decho("windows exception for plainfname",'~'.expand("<slnum>")) if plainfname =~ '^/\+\a:' -" call Decho('removing leading "/"s') +" call Decho('removing leading "/"s','~'.expand("<slnum>")) let plainfname= substitute(plainfname,'^/\+\(\a:\)','\1','') endif endif -" call Decho("fname2396<".fname2396.">") -" call Decho("plainfname<".plainfname.">") +" call Decho("fname2396<".fname2396.">",'~'.expand("<slnum>")) +" call Decho("plainfname<".plainfname.">",'~'.expand("<slnum>")) exe "sil doau BufReadPre ".fname2396e exe 'NetrwKeepj r '.plainfname exe 'sil! bdelete '.plainfname exe 'keepalt file! '.plainfname NetrwKeepj 1d -" call Decho("setl nomod") +" call Decho("setl nomod",'~'.expand("<slnum>")) setl nomod -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("netrw#FileUrlRead") exe "sil doau BufReadPost ".fname2396e endfun @@ -9627,6 +9955,7 @@ endfun " --------------------------------------------------------------------- " netrw#LocalBrowseCheck: {{{2 fun! netrw#LocalBrowseCheck(dirname) + " This function is called by netrwPlugin.vim's s:LocalBrowse() and by s:NetrwRexplore() " unfortunate interaction -- split window debugging can't be " used here, must use D-echoRemOn or D-echoTabOn -- the BufEnter " event triggers another call to LocalBrowseCheck() when attempts @@ -9635,33 +9964,32 @@ fun! netrw#LocalBrowseCheck(dirname) " would hit when re-entering netrw windows, creating unexpected " refreshes (and would do so in the middle of NetrwSaveOptions(), too) " call Dfunc("netrw#LocalBrowseCheck(dirname<".a:dirname.">") -" call Decho("isdir<".a:dirname.">=".isdirectory(a:dirname).((exists("s:treeforceredraw")? " treeforceredraw" : ""))) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("isdir<".a:dirname.">=".isdirectory(s:NetrwFile(a:dirname)).((exists("s:treeforceredraw")? " treeforceredraw" : "")).expand("<slnum>")) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) " call Dredir("ls!","ls!") - norm! m` +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) +" call Decho("current buffer#".bufnr("%")."<".bufname("%")."> ft=".&ft,'~'.expand("<slnum>")) let ykeep= @@ - if isdirectory(a:dirname) -" call Decho("is-directory ft<".&ft."> b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : " doesn't exist")."> dirname<".a:dirname.">"." line($)=".line("$")." ft<".&ft."> g:netrw_fastbrowse=".g:netrw_fastbrowse) - let svposn= netrw#SavePosn() + if isdirectory(s:NetrwFile(a:dirname)) +" call Decho("is-directory ft<".&ft."> b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : " doesn't exist")."> dirname<".a:dirname.">"." line($)=".line("$")." ft<".&ft."> g:netrw_fastbrowse=".g:netrw_fastbrowse,'~'.expand("<slnum>")) if &ft != "netrw" || (exists("b:netrw_curdir") && b:netrw_curdir != a:dirname) || g:netrw_fastbrowse <= 1 -" call Decho("case 1 : ft=".&ft) +" call Decho("case 1 : ft=".&ft,'~'.expand("<slnum>")) +" call Decho("s:rexposn_".bufnr("%")."<".bufname("%")."> ".(exists("s:rexposn_".bufnr("%"))? "exists" : "does not exist"),'~'.expand("<slnum>")) sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) - NetrwKeepj keepalt call netrw#RestorePosn(svposn) elseif &ft == "netrw" && line("$") == 1 -" call Decho("case 2 (ft≡netrw && line($)≡1)") +" call Decho("case 2 (ft≡netrw && line($)≡1)",'~'.expand("<slnum>")) sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) - NetrwKeepj keepalt call netrw#RestorePosn(svposn) elseif exists("s:treeforceredraw") -" call Decho("case 3 (treeforceredraw)") +" call Decho("case 3 (treeforceredraw)",'~'.expand("<slnum>")) unlet s:treeforceredraw sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) - NetrwKeepj keepalt call netrw#RestorePosn(svposn) endif +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Dret("netrw#LocalBrowseCheck") return endif @@ -9670,18 +9998,19 @@ fun! netrw#LocalBrowseCheck(dirname) " IF g:netrw_fastbrowse is zero (ie. slow browsing selected) " AND IF the listing style is not a tree listing if exists("g:netrw_fastbrowse") && g:netrw_fastbrowse == 0 && g:netrw_liststyle != s:TREELIST -" call Decho("wiping out currently unused netrw buffers") +" call Decho("wiping out currently unused netrw buffers",'~'.expand("<slnum>")) let ibuf = 1 let buflast = bufnr("$") while ibuf <= buflast - if bufwinnr(ibuf) == -1 && isdirectory(bufname(ibuf)) - exe "sil! keepalt ".ibuf."bw!" + if bufwinnr(ibuf) == -1 && isdirectory(s:NetrwFile(bufname(ibuf))) + exe "sil! keepj keepalt ".ibuf."bw!" endif let ibuf= ibuf + 1 endwhile endif let @@= ykeep -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " not a directory, ignore it " call Dret("netrw#LocalBrowseCheck : not a directory, ignoring it; dirname<".a:dirname.">") endfun @@ -9693,8 +10022,8 @@ endfun " on the chance that s/he removed/created a file/directory with it. fun! s:LocalBrowseRefresh() " call Dfunc("s:LocalBrowseRefresh() tabpagenr($)=".tabpagenr("$")) -" call Decho("s:netrw_browselist =".(exists("s:netrw_browselist")? string(s:netrw_browselist) : '<n/a>')) -" call Decho("w:netrw_bannercnt =".(exists("w:netrw_bannercnt")? string(w:netrw_bannercnt) : '<n/a>')) +" call Decho("s:netrw_browselist =".(exists("s:netrw_browselist")? string(s:netrw_browselist) : '<n/a>'),'~'.expand("<slnum>")) +" call Decho("w:netrw_bannercnt =".(exists("w:netrw_bannercnt")? string(w:netrw_bannercnt) : '<n/a>'),'~'.expand("<slnum>")) " determine which buffers currently reside in a tab if !exists("s:netrw_browselist") @@ -9719,36 +10048,38 @@ fun! s:LocalBrowseRefresh() let itab = itab + 1 tabn endwhile -" call Decho("buftablist".string(buftablist)) -" call Decho("s:netrw_browselist<".(exists("s:netrw_browselist")? string(s:netrw_browselist) : "").">") +" call Decho("buftablist".string(buftablist),'~'.expand("<slnum>")) +" call Decho("s:netrw_browselist<".(exists("s:netrw_browselist")? string(s:netrw_browselist) : "").">",'~'.expand("<slnum>")) " GO through all buffers on netrw_browselist (ie. just local-netrw buffers): " | refresh any netrw window " | wipe out any non-displaying netrw buffer let curwin = winnr() let ibl = 0 for ibuf in s:netrw_browselist -" call Decho("bufwinnr(".ibuf.") index(buftablist,".ibuf.")=".index(buftablist,ibuf)) +" call Decho("bufwinnr(".ibuf.") index(buftablist,".ibuf.")=".index(buftablist,ibuf),'~'.expand("<slnum>")) if bufwinnr(ibuf) == -1 && index(buftablist,ibuf) == -1 " wipe out any non-displaying netrw buffer -" call Decho("wiping buf#".ibuf,"<".bufname(ibuf).">") - exe "sil! bd ".fnameescape(ibuf) +" call Decho("wiping buf#".ibuf,"<".bufname(ibuf).">",'~'.expand("<slnum>")) + exe "sil! keepj bd ".fnameescape(ibuf) call remove(s:netrw_browselist,ibl) -" call Decho("browselist=".string(s:netrw_browselist)) +" call Decho("browselist=".string(s:netrw_browselist),'~'.expand("<slnum>")) continue elseif index(tabpagebuflist(),ibuf) != -1 " refresh any netrw buffer -" call Decho("refresh buf#".ibuf.'-> win#'.bufwinnr(ibuf)) +" call Decho("refresh buf#".ibuf.'-> win#'.bufwinnr(ibuf),'~'.expand("<slnum>")) exe bufwinnr(ibuf)."wincmd w" if getline(".") =~ 'Quick Help' " decrement g:netrw_quickhelp to prevent refresh from changing g:netrw_quickhelp " (counteracts s:NetrwBrowseChgDir()'s incrementing) let g:netrw_quickhelp= g:netrw_quickhelp - 1 endif -" call Decho("#3: quickhelp=".g:netrw_quickhelp) +" call Decho("#3: quickhelp=".g:netrw_quickhelp,'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./')) endif let ibl= ibl + 1 +" call Decho("bottom of s:netrw_browselist for loop: ibl=".ibl,'~'.expand("<slnum>")) endfor +" call Decho("restore window: exe ".curwin."wincmd w",'~'.expand("<slnum>")) exe curwin."wincmd w" let @@= ykeep @@ -9759,10 +10090,10 @@ endfun " s:LocalFastBrowser: handles setting up/taking down fast browsing for the local browser {{{2 " " g:netrw_ Directory Is -" fastbrowse Local Remote +" fastbrowse Local Remote " slow 0 D D D=Deleting a buffer implies it will not be re-used (slow) " med 1 D H H=Hiding a buffer implies it may be re-used (fast) -" fast 2 H H +" fast 2 H H " " Deleting a buffer means that it will be re-loaded when examined, hence "slow". " Hiding a buffer means that it will be re-used when examined, hence "fast". @@ -9775,21 +10106,21 @@ endfun " =2: autocmds installed (doesn't ignore any FocusGained events) fun! s:LocalFastBrowser() " call Dfunc("LocalFastBrowser() g:netrw_fastbrowse=".g:netrw_fastbrowse) -" call Decho("s:netrw_events ".(exists("s:netrw_events")? "exists" : 'n/a')) -" call Decho("autocmd: ShellCmdPost ".(exists("#ShellCmdPost")? "installed" : "not installed")) -" call Decho("autocmd: FocusGained ".(exists("#FocusGained")? "installed" : "not installed")) +" call Decho("s:netrw_events ".(exists("s:netrw_events")? "exists" : 'n/a'),'~'.expand("<slnum>")) +" call Decho("autocmd: ShellCmdPost ".(exists("#ShellCmdPost")? "installed" : "not installed"),'~'.expand("<slnum>")) +" call Decho("autocmd: FocusGained ".(exists("#FocusGained")? "installed" : "not installed"),'~'.expand("<slnum>")) " initialize browselist, a list of buffer numbers that the local browser has used if !exists("s:netrw_browselist") -" call Decho("initialize s:netrw_browselist") +" call Decho("initialize s:netrw_browselist",'~'.expand("<slnum>")) let s:netrw_browselist= [] endif " append current buffer to fastbrowse list if empty(s:netrw_browselist) || bufnr("%") > s:netrw_browselist[-1] -" call Decho("appendng current buffer to browselist") +" call Decho("appendng current buffer to browselist",'~'.expand("<slnum>")) call add(s:netrw_browselist,bufnr("%")) -" call Decho("browselist=".string(s:netrw_browselist)) +" call Decho("browselist=".string(s:netrw_browselist),'~'.expand("<slnum>")) endif " enable autocmd events to handle refreshing/removing local browser buffers @@ -9803,10 +10134,10 @@ fun! s:LocalFastBrowser() augroup AuNetrwEvent au! if (has("win32") || has("win95") || has("win64") || has("win16")) -" call Decho("installing autocmd: ShellCmdPost") +" call Decho("installing autocmd: ShellCmdPost",'~'.expand("<slnum>")) au ShellCmdPost * call s:LocalBrowseRefresh() else -" call Decho("installing autocmds: ShellCmdPost FocusGained") +" call Decho("installing autocmds: ShellCmdPost FocusGained",'~'.expand("<slnum>")) au ShellCmdPost,FocusGained * call s:LocalBrowseRefresh() endif augroup END @@ -9814,7 +10145,7 @@ fun! s:LocalFastBrowser() " user must have changed fastbrowse to its fast setting, so remove " the associated autocmd events elseif g:netrw_fastbrowse > 1 && exists("#ShellCmdPost") && exists("s:netrw_events") -" call Decho("remove AuNetrwEvent autcmd group") +" call Decho("remove AuNetrwEvent autcmd group",'~'.expand("<slnum>")) unlet s:netrw_events augroup AuNetrwEvent au! @@ -9829,70 +10160,78 @@ endfun " s:LocalListing: does the job of "ls" for local directories {{{2 fun! s:LocalListing() " call Dfunc("s:LocalListing()") -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> modified=".&modified." modifiable=".&modifiable." readonly=".&readonly) +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) +" call Decho("modified=".&modified." modifiable=".&modifiable." readonly=".&readonly,'~'.expand("<slnum>")) +" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) -" if exists("b:netrw_curdir") |call Decho('b:netrw_curdir<'.b:netrw_curdir.">") |else|call Decho("b:netrw_curdir doesn't exist") |endif -" if exists("g:netrw_sort_by")|call Decho('g:netrw_sort_by<'.g:netrw_sort_by.">")|else|call Decho("g:netrw_sort_by doesn't exist")|endif -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" if exists("b:netrw_curdir") |call Decho('b:netrw_curdir<'.b:netrw_curdir.">") |else|call Decho("b:netrw_curdir doesn't exist",'~'.expand("<slnum>")) |endif +" if exists("g:netrw_sort_by")|call Decho('g:netrw_sort_by<'.g:netrw_sort_by.">")|else|call Decho("g:netrw_sort_by doesn't exist",'~'.expand("<slnum>"))|endif +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) " get the list of files contained in the current directory let dirname = b:netrw_curdir let dirnamelen = strlen(b:netrw_curdir) - let filelist = glob(s:ComposePath(dirname,"*"),0,1) - let filelist = filelist + glob(s:ComposePath(dirname,".*"),0,1) -" call Decho("filelist=".string(filelist)) + if v:version == 704 && has("patch656") +" call Decho("using glob with patch656",'~'.expand("<slnum>")) + let filelist = glob(s:ComposePath(dirname,"*"),0,1,1) + let filelist = filelist + glob(s:ComposePath(dirname,".*"),0,1,1) + else +" call Decho("using glob without patch656",'~'.expand("<slnum>")) + let filelist = glob(s:ComposePath(dirname,"*"),0,1) + let filelist = filelist + glob(s:ComposePath(dirname,".*"),0,1) + endif +" call Decho("filelist=".string(filelist),'~'.expand("<slnum>")) if g:netrw_cygwin == 0 && (has("win32") || has("win95") || has("win64") || has("win16")) -" call Decho("filelist=".string(filelist)) +" call Decho("filelist=".string(filelist),'~'.expand("<slnum>")) elseif index(filelist,'..') == -1 && b:netrw_curdir !~ '/' " include ../ in the glob() entry if its missing -" call Decho("forcibly including on \"..\"") +" call Decho("forcibly including on \"..\"",'~'.expand("<slnum>")) let filelist= filelist+[s:ComposePath(b:netrw_curdir,"../")] -" call Decho("filelist=".string(filelist)) +" call Decho("filelist=".string(filelist),'~'.expand("<slnum>")) endif -" call Decho("before while: dirname<".dirname.">") -" call Decho("before while: dirnamelen<".dirnamelen.">") -" call Decho("before while: filelist=".string(filelist)) +" call Decho("before while: dirname<".dirname.">",'~'.expand("<slnum>")) +" call Decho("before while: dirnamelen<".dirnamelen.">",'~'.expand("<slnum>")) +" call Decho("before while: filelist=".string(filelist),'~'.expand("<slnum>")) if get(g:, 'netrw_dynamic_maxfilenamelen', 0) let filelistcopy = map(deepcopy(filelist),'fnamemodify(v:val, ":t")') let g:netrw_maxfilenamelen = max(map(filelistcopy,'len(v:val)')) + 1 -" call Decho("dynamic_maxfilenamelen: filenames =".string(filelistcopy)) -" call Decho("dynamic_maxfilenamelen: g:netrw_maxfilenamelen=".g:netrw_maxfilenamelen) +" call Decho("dynamic_maxfilenamelen: filenames =".string(filelistcopy),'~'.expand("<slnum>")) +" call Decho("dynamic_maxfilenamelen: g:netrw_maxfilenamelen=".g:netrw_maxfilenamelen,'~'.expand("<slnum>")) endif -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")") +" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) for filename in filelist -" call Decho(" ") -" call Decho("for filename in filelist: filename<".filename.">") +" call Decho(" ",'~'.expand("<slnum>")) +" call Decho("for filename in filelist: filename<".filename.">",'~'.expand("<slnum>")) if getftype(filename) == "link" " indicate a symbolic link -" call Decho("indicate <".filename."> is a symbolic link with trailing @") +" call Decho("indicate <".filename."> is a symbolic link with trailing @",'~'.expand("<slnum>")) let pfile= filename."@" elseif getftype(filename) == "socket" " indicate a socket -" call Decho("indicate <".filename."> is a socket with trailing =") +" call Decho("indicate <".filename."> is a socket with trailing =",'~'.expand("<slnum>")) let pfile= filename."=" elseif getftype(filename) == "fifo" " indicate a fifo -" call Decho("indicate <".filename."> is a fifo with trailing |") +" call Decho("indicate <".filename."> is a fifo with trailing |",'~'.expand("<slnum>")) let pfile= filename."|" - elseif isdirectory(filename) + elseif isdirectory(s:NetrwFile(filename)) " indicate a directory -" call Decho("indicate <".filename."> is a directory with trailing /") +" call Decho("indicate <".filename."> is a directory with trailing /",'~'.expand("<slnum>")) let pfile= filename."/" - elseif exists("b:netrw_curdir") && b:netrw_curdir !~ '^.*://' && !isdirectory(filename) + elseif exists("b:netrw_curdir") && b:netrw_curdir !~ '^.*://' && !isdirectory(s:NetrwFile(filename)) if (has("win32") || has("win95") || has("win64") || has("win16")) if filename =~ '\.[eE][xX][eE]$' || filename =~ '\.[cC][oO][mM]$' || filename =~ '\.[bB][aA][tT]$' " indicate an executable -" call Decho("indicate <".filename."> is executable with trailing *") +" call Decho("indicate <".filename."> is executable with trailing *",'~'.expand("<slnum>")) let pfile= filename."*" else " normal file @@ -9900,7 +10239,7 @@ fun! s:LocalListing() endif elseif executable(filename) " indicate an executable -" call Decho("indicate <".filename."> is executable with trailing *") +" call Decho("indicate <".filename."> is executable with trailing *",'~'.expand("<slnum>")) let pfile= filename."*" else " normal file @@ -9911,45 +10250,45 @@ fun! s:LocalListing() " normal file let pfile= filename endif -" call Decho("pfile<".pfile."> (after *@/ appending)") +" call Decho("pfile<".pfile."> (after *@/ appending)",'~'.expand("<slnum>")) if pfile =~ '//$' let pfile= substitute(pfile,'//$','/','e') -" call Decho("change // to /: pfile<".pfile.">") +" call Decho("change // to /: pfile<".pfile.">",'~'.expand("<slnum>")) endif let pfile= strpart(pfile,dirnamelen) let pfile= substitute(pfile,'^[/\\]','','e') -" call Decho("filename<".filename.">") -" call Decho("pfile <".pfile.">") +" call Decho("filename<".filename.">",'~'.expand("<slnum>")) +" call Decho("pfile <".pfile.">",'~'.expand("<slnum>")) if w:netrw_liststyle == s:LONGLIST let sz = getfsize(filename) let fsz = strpart(" ",1,15-strlen(sz)).sz let pfile= pfile."\t".fsz." ".strftime(g:netrw_timefmt,getftime(filename)) -" call Decho("sz=".sz." fsz=".fsz) +" call Decho("sz=".sz." fsz=".fsz,'~'.expand("<slnum>")) endif if g:netrw_sort_by =~ "^t" " sort by time (handles time up to 1 quintillion seconds, US) -" call Decho("getftime(".filename.")=".getftime(filename)) +" call Decho("getftime(".filename.")=".getftime(filename),'~'.expand("<slnum>")) let t = getftime(filename) let ft = strpart("000000000000000000",1,18-strlen(t)).t -" call Decho("exe NetrwKeepj put ='".ft.'/'.filename."'") +" call Decho("exe NetrwKeepj put ='".ft.'/'.filename."'",'~'.expand("<slnum>")) let ftpfile= ft.'/'.pfile sil! NetrwKeepj put=ftpfile elseif g:netrw_sort_by =~ "^s" " sort by size (handles file sizes up to 1 quintillion bytes, US) -" call Decho("getfsize(".filename.")=".getfsize(filename)) +" call Decho("getfsize(".filename.")=".getfsize(filename),'~'.expand("<slnum>")) let sz = getfsize(filename) let fsz = strpart("000000000000000000",1,18-strlen(sz)).sz -" call Decho("exe NetrwKeepj put ='".fsz.'/'.filename."'") +" call Decho("exe NetrwKeepj put ='".fsz.'/'.filename."'",'~'.expand("<slnum>")) let fszpfile= fsz.'/'.pfile sil! NetrwKeepj put =fszpfile else " sort by name -" call Decho("exe NetrwKeepj put ='".pfile."'") +" call Decho("exe NetrwKeepj put ='".pfile."'",'~'.expand("<slnum>")) sil! NetrwKeepj put=pfile endif endfor @@ -9958,7 +10297,7 @@ fun! s:LocalListing() sil! NetrwKeepj g/^$/d sil! NetrwKeepj %s/\r$//e call histdel("/",-1) -" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1)) +" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) exe "setl ts=".(g:netrw_maxfilenamelen+1) " call Dret("s:LocalListing") @@ -9978,9 +10317,9 @@ fun! s:NetrwLocalExecute(cmd) endif let optargs= input(":!".a:cmd,"","file") -" call Decho("optargs<".optargs.">") +" call Decho("optargs<".optargs.">",'~'.expand("<slnum>")) let result= system(a:cmd.optargs) -" call Decho("result) +" call Decho("result,'~'.expand("<slnum>")) " strip any ansi escape sequences off let result = substitute(result,"\e\\[[0-9;]*m","","g") @@ -9998,32 +10337,39 @@ fun! s:NetrwLocalRename(path) range " call Dfunc("NetrwLocalRename(path<".a:path.">)") " preparation for removing multiple files/directories - let ykeep = @@ - let ctr = a:firstline - let svpos = netrw#SavePosn() + let ykeep = @@ + let ctr = a:firstline + let svpos = netrw#SavePosn() " rename files given by the markfilelist if exists("s:netrwmarkfilelist_{bufnr('%')}") for oldname in s:netrwmarkfilelist_{bufnr("%")} -" call Decho("oldname<".oldname.">") +" call Decho("oldname<".oldname.">",'~'.expand("<slnum>")) if exists("subfrom") let newname= substitute(oldname,subfrom,subto,'') -" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">") +" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">",'~'.expand("<slnum>")) else call inputsave() - let newname= input("Moving ".oldname." to : ",oldname) + let newname= input("Moving ".oldname." to : ",oldname,"file") call inputrestore() + if newname =~ '' + " two ctrl-x's : ignore all of string preceding the ctrl-x's + let newname = substitute(newname,'^.*','','') + elseif newname =~ '' + " one ctrl-x : ignore portion of string preceding ctrl-x but after last / + let newname = substitute(newname,'[^/]*','','') + endif if newname =~ '^s/' let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','') let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','') -" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">") +" call Decho("subfrom<".subfrom."> subto<".subto."> newname<".newname.">",'~'.expand("<slnum>")) let newname = substitute(oldname,subfrom,subto,'') endif endif call rename(oldname,newname) endfor call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) - + else " attempt to rename files/directories @@ -10043,21 +10389,21 @@ fun! s:NetrwLocalRename(path) range NetrwKeepj norm! 0 let oldname= s:ComposePath(a:path,curword) -" call Decho("oldname<".oldname.">") +" call Decho("oldname<".oldname.">",'~'.expand("<slnum>")) call inputsave() let newname= input("Moving ".oldname." to : ",substitute(oldname,'/*$','','e')) call inputrestore() call rename(oldname,newname) -" call Decho("renaming <".oldname."> to <".newname.">") +" call Decho("renaming <".oldname."> to <".newname.">",'~'.expand("<slnum>")) let ctr= ctr + 1 endwhile endif " refresh the directory -" call Decho("refresh the directory listing") +" call Decho("refresh the directory listing",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./')) NetrwKeepj call netrw#RestorePosn(svpos) let @@= ykeep @@ -10069,7 +10415,7 @@ endfun " s:NetrwLocalRm: {{{2 fun! s:NetrwLocalRm(path) range " call Dfunc("s:NetrwLocalRm(path<".a:path.">)") -" call Decho("firstline=".a:firstline." lastline=".a:lastline) +" call Decho("firstline=".a:firstline." lastline=".a:lastline,'~'.expand("<slnum>")) " preparation for removing multiple files/directories let ykeep = @@ @@ -10079,7 +10425,7 @@ fun! s:NetrwLocalRm(path) range if exists("s:netrwmarkfilelist_{bufnr('%')}") " remove all marked files -" call Decho("remove all marked files") +" call Decho("remove all marked files",'~'.expand("<slnum>")) for fname in s:netrwmarkfilelist_{bufnr("%")} let ok= s:NetrwLocalRmFile(a:path,fname,all) if ok =~ 'q\%[uit]' || ok == "no" @@ -10092,8 +10438,10 @@ fun! s:NetrwLocalRm(path) range else " remove (multiple) files and directories -" call Decho("remove files in range [".a:firstline.",".a:lastline."]") +" call Decho("remove files in range [".a:firstline.",".a:lastline."]",'~'.expand("<slnum>")) + let keepsol= &l:sol + setl nosol let ctr = a:firstline while ctr <= a:lastline exe "NetrwKeepj ".ctr @@ -10116,10 +10464,11 @@ fun! s:NetrwLocalRm(path) range endif let ctr= ctr + 1 endwhile + let &l:sol= keepsol endif " refresh the directory -" call Decho("bufname<".bufname("%").">") +" call Decho("bufname<".bufname("%").">",'~'.expand("<slnum>")) if bufname("%") != "NetrwMessage" NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./')) NetrwKeepj call netrw#RestorePosn(svpos) @@ -10134,16 +10483,16 @@ endfun " Give confirmation prompt unless all==1 fun! s:NetrwLocalRmFile(path,fname,all) " call Dfunc("s:NetrwLocalRmFile(path<".a:path."> fname<".a:fname."> all=".a:all) - + let all= a:all let ok = "" NetrwKeepj norm! 0 - let rmfile= s:ComposePath(a:path,a:fname) -" call Decho("rmfile<".rmfile.">") + let rmfile= s:NetrwFile(s:ComposePath(a:path,a:fname)) +" call Decho("rmfile<".rmfile.">",'~'.expand("<slnum>")) if rmfile !~ '^"' && (rmfile =~ '@$' || rmfile !~ '[\/]$') " attempt to remove file -" call Decho("attempt to remove file<".rmfile.">") +" call Decho("attempt to remove file<".rmfile.">",'~'.expand("<slnum>")) if !all echohl Statement call inputsave() @@ -10153,9 +10502,9 @@ fun! s:NetrwLocalRmFile(path,fname,all) if ok == "" let ok="no" endif -" call Decho("response: ok<".ok.">") +" call Decho("response: ok<".ok.">",'~'.expand("<slnum>")) let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') -" call Decho("response: ok<".ok."> (after sub)") +" call Decho("response: ok<".ok."> (after sub)",'~'.expand("<slnum>")) if ok =~ 'a\%[ll]' let all= 1 endif @@ -10163,7 +10512,7 @@ fun! s:NetrwLocalRmFile(path,fname,all) if all || ok =~ 'y\%[es]' || ok == "" let ret= s:NetrwDelete(rmfile) -" call Decho("errcode=".v:shell_error." ret=".ret) +" call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>")) endif else @@ -10184,19 +10533,19 @@ fun! s:NetrwLocalRmFile(path,fname,all) let rmfile= substitute(rmfile,'[\/]$','','e') if all || ok =~ 'y\%[es]' || ok == "" -" call Decho("1st attempt: system(netrw#WinPath(".g:netrw_localrmdir.') '.shellescape(rmfile).')') - call system(netrw#WinPath(g:netrw_localrmdir).' '.shellescape(rmfile)) -" call Decho("v:shell_error=".v:shell_error) +" call Decho("1st attempt: system(netrw#WinPath(".g:netrw_localrmdir.') '.s:ShellEscape(rmfile).')','~'.expand("<slnum>")) + call system(netrw#WinPath(g:netrw_localrmdir).' '.s:ShellEscape(rmfile)) +" call Decho("v:shell_error=".v:shell_error,'~'.expand("<slnum>")) if v:shell_error != 0 -" call Decho("2nd attempt to remove directory<".rmfile.">") +" call Decho("2nd attempt to remove directory<".rmfile.">",'~'.expand("<slnum>")) let errcode= s:NetrwDelete(rmfile) -" call Decho("errcode=".errcode) +" call Decho("errcode=".errcode,'~'.expand("<slnum>")) if errcode != 0 if has("unix") -" call Decho("3rd attempt to remove directory<".rmfile.">") - call system("rm ".shellescape(rmfile)) +" call Decho("3rd attempt to remove directory<".rmfile.">",'~'.expand("<slnum>")) + call system("rm ".s:ShellEscape(rmfile)) if v:shell_error != 0 && !exists("g:netrw_quiet") call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",34) let ok="no" @@ -10232,6 +10581,18 @@ fun! netrw#Access(ilist) return s:netrwmftgt endfun +" --------------------------------------------------------------------- +" netrw#Call: allows user-specified mappings to call internal netrw functions {{{2 +fun! netrw#Call(funcname,...) +" call Dfunc("netrw#Call(funcname<".a:funcname.">,".string(a:000).")") + if a:0 > 0 + exe "call s:".a:funcname."(".string(a:000).")" + else + exe "call s:".a:funcname."()" + endif +" call Dret("netrw#Call") +endfun + " ------------------------------------------------------------------------ " netrw#RestorePosn: restores the cursor and file position as saved by netrw#SavePosn() {{{2 fun! netrw#RestorePosn(...) @@ -10247,22 +10608,22 @@ fun! netrw#RestorePosn(...) if a:0 > 0 exe "keepj ".a:1 endif -" "call Decho("a:1 = ".((a:0 > 0)? a:1 : 'n/a')) -" "call Decho("liststyle = ".(exists("liststyle")? liststyle : 'n/a'). " w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a')) +" "call Decho("a:1 = ".((a:0 > 0)? a:1 : 'n/a'),'~'.expand("<slnum>")) +" "call Decho("liststyle = ".(exists("liststyle")? liststyle : 'n/a'). " w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>")) if exists("liststyle") && exists("w:netrw_liststyle") && liststyle != w:netrw_liststyle let usesrch= 1 else let usesrch= 0 endif -" "call Decho("winh = ".(exists("w:netrw_winh")? w:netrw_winh : -1)) -" "call Decho("winw = ".(exists("w:netrw_winw")? w:netrw_winw : -1)) -" "call Decho("cur winheight=".winheight(0)." winwidth=".winwidth(0)) -" "call Decho("w:netrw_winfile = ".(exists("w:netrw_winfile")? w:netrw_winfile : 'n/a')) +" "call Decho("winh = ".(exists("w:netrw_winh")? w:netrw_winh : -1),'~'.expand("<slnum>")) +" "call Decho("winw = ".(exists("w:netrw_winw")? w:netrw_winw : -1),'~'.expand("<slnum>")) +" "call Decho("cur winheight=".winheight(0)." winwidth=".winwidth(0),'~'.expand("<slnum>")) +" "call Decho("w:netrw_winfile = ".(exists("w:netrw_winfile")? w:netrw_winfile : 'n/a'),'~'.expand("<slnum>")) " restore window if exists("w:netrw_winnr") -" "call Decho("restore window: exe sil! ".w:netrw_winnr."wincmd w") +" "call Decho("restore window: exe sil! ".w:netrw_winnr."wincmd w",'~'.expand("<slnum>")) exe "sil! ".w:netrw_winnr."wincmd w" endif " if v:shell_error == 0 @@ -10273,34 +10634,34 @@ fun! netrw#RestorePosn(...) " restore top-of-screen line if exists("w:netrw_hline") -" "call Decho("restore topofscreen: exe keepj norm! ".w:netrw_hline."G0z") +" "call Decho("restore topofscreen: exe keepj norm! ".w:netrw_hline."G0z",'~'.expand("<slnum>")) exe "keepj norm! ".w:netrw_hline."G0z\<CR>" endif " restore position " when the window's height x width has changed, the line,col is no longer useful if w:netrw_winh == winheight(0) && w:netrw_winw == winwidth(0) && exists("w:netrw_line") && exists("w:netrw_col") && !usesrch -" "call Decho("using posn: exe keepj norm! ".w:netrw_line."G0".w:netrw_col."|") +" "call Decho("using posn: exe keepj norm! ".w:netrw_line."G0".w:netrw_col."|",'~'.expand("<slnum>")) exe "keepj norm! ".w:netrw_line."G0".w:netrw_col."\<bar>" elseif exists("w:netrw_winfile") if !search('\<'.escape(w:netrw_winfile,g:netrw_fname_escape),'cw') if exists("w:netrw_bannercnt") -" "call Decho("using bannercnt: win#".winnr()." ".winheight(0)."x".winwidth(0)." w:netrw_winfile<".w:netrw_winfile.">") +" "call Decho("using bannercnt: win#".winnr()." ".winheight(0)."x".winwidth(0)." w:netrw_winfile<".w:netrw_winfile.">",'~'.expand("<slnum>")) exe "keepj ".w:netrw_bannercnt norm! 0 else " go to upper left corner -" "call Decho("goto ulc: win#".winnr()." ".winheight(0)."x".winwidth(0)." w:netrw_winfile<".w:netrw_winfile.">") +" "call Decho("goto ulc: win#".winnr()." ".winheight(0)."x".winwidth(0)." w:netrw_winfile<".w:netrw_winfile.">",'~'.expand("<slnum>")) keepj 1 norm! 0 endif else -" "call Decho("used search: w:netrw_winfile<".w:netrw_winfile.">") +" "call Decho("used search: w:netrw_winfile<".w:netrw_winfile.">",'~'.expand("<slnum>")) endif else -" "call Decho("goto ulc: win#".winnr()." ".winheight(0)."x".winwidth(0)) +" "call Decho("goto ulc: win#".winnr()." ".winheight(0)."x".winwidth(0),'~'.expand("<slnum>")) keepj 1 norm! 0 endif @@ -10310,6 +10671,39 @@ fun! netrw#RestorePosn(...) endfun " --------------------------------------------------------------------- +" netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2 +" I expect this function to be used in +" :PChkAssert netrw#Expose("netrwmarkfilelist") +" for example. +fun! netrw#Expose(varname) +" call Dfunc("netrw#Expose(varname<".a:varname.">)") + exe "let retval= s:".a:varname + if exists("g:netrw_pchk") + if type(retval) == 3 + let retval = copy(retval) + let i = 0 + while i < len(retval) + let retval[i]= substitute(retval[i],expand("$HOME"),'~','') + let i = i + 1 + endwhile + endif +" call Dret("netrw#Expose ".string(retval)) + return string(retval) + endif + +" call Dret("netrw#Expose ".string(retval)) + return retval +endfun + +" --------------------------------------------------------------------- +" netrw#Modify: allows UserMaps to set (modify) script-local variables {{{2 +fun! netrw#Modify(varname,newvalue) +" call Dfunc("netrw#Modify(varname<".a:varname.">,newvalue<".string(a:newvalue).">)") + exe "let s:".a:varname."= ".string(a:newvalue) +" call Dret("netrw#Modify") +endfun + +" --------------------------------------------------------------------- " netrw#RFC2396: converts %xx into characters {{{2 fun! netrw#RFC2396(fname) " call Dfunc("netrw#RFC2396(fname<".a:fname.">)") @@ -10321,27 +10715,27 @@ endfun " --------------------------------------------------------------------- " netrw#SavePosn: saves position of cursor on screen {{{2 fun! netrw#SavePosn() -" call Dfunc("netrw#SavePosn() line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()) +" call Dfunc("netrw#SavePosn() win#".winnr()." line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()) " Save current line and column let w:netrw_winnr= winnr() let w:netrw_line = line(".") let w:netrw_col = virtcol(".") -" "call Decho("currently, win#".w:netrw_winnr." line#".w:netrw_line." col#".w:netrw_col) +" "call Decho("currently, win#".w:netrw_winnr." line#".w:netrw_line." col#".w:netrw_col,'~'.expand("<slnum>")) " save filename under cursor -" "call Decho("line#".line(".")." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')) +" "call Decho("line#".line(".")." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a'),'~'.expand("<slnum>")) if exists("w:netrw_bannercnt") && line(".") >= w:netrw_bannercnt && &ft == "netrw" let winfile = "|let w:netrw_winfile=\"".fnameescape(s:NetrwGetWord())."\"" else let winfile= "" endif -" "call Decho("winfile<".winfile.">") +" "call Decho("winfile<".winfile.">",'~'.expand("<slnum>")) if exists("w:netrw_liststyle") let liststyle = "|let liststyle=".w:netrw_liststyle else let liststyle= "" endif -" "call Decho("liststyle=".liststyle) +" "call Decho("liststyle=".liststyle,'~'.expand("<slnum>")) " Save top-of-screen line keepj norm! H0 @@ -10356,11 +10750,51 @@ fun! netrw#SavePosn() let ret = "let w:netrw_winnr=".w:netrw_winnr."|let w:netrw_line=".w:netrw_line."|let w:netrw_col=".w:netrw_col."|let w:netrw_hline=".w:netrw_hline."|let w:netrw_winh=".w:netrw_winh."|let w:netrw_winw=".w:netrw_winw.liststyle.winfile keepj call netrw#RestorePosn() -" call Dret("netrw#SavePosn : winnr=".(exists("w:netrw_winnr")? w:netrw_winnr : "n/a")." line=".(exists("w:netrw_line")? w:netrw_line : "n/a")." col=".(exists("w:netrw_col")? w:netrw_col : "n/a")." hline=".(exists("w:netrw_hline")? w:netrw_hline : "n/a")) +" call Dret("netrw#SavePosn : win#=".(exists("w:netrw_winnr")? w:netrw_winnr : "n/a")." line=".(exists("w:netrw_line")? w:netrw_line : "n/a")." col=".(exists("w:netrw_col")? w:netrw_col : "n/a")." hline=".(exists("w:netrw_hline")? w:netrw_hline : "n/a")) return ret endfun " --------------------------------------------------------------------- +" netrw#UserMaps: supports user-specified maps {{{2 +" see :help function() +" +" g:Netrw_UserMaps is a List with members such as: +" [[keymap sequence, function reference],...] +" +" The referenced function may return a string, +" refresh : refresh the display +" -other- : this string will be executed +" or it may return a List of strings. +" +" Each keymap-sequence will be set up with a nnoremap +" to invoke netrw#UserMaps(islocal). +" Related functions: +" netrw#Expose(varname) -- see s:varname variables +" netrw#Modify(varname,newvalue) -- modify value of s:varname variable +" netrw#Call(funcname,...) -- call internal netrw function with optional arguments +fun! netrw#UserMaps(islocal) +" call Dfunc("netrw#UserMaps(islocal=".a:islocal.")") +" call Decho("g:Netrw_UserMaps ".(exists("g:Netrw_UserMaps")? "exists" : "does NOT exist"),'~'.expand("<slnum>")) + + " set up usermaplist + if exists("g:Netrw_UserMaps") && type(g:Netrw_UserMaps) == 3 +" call Decho("g:Netrw_UserMaps has type 3<List>",'~'.expand("<slnum>")) + for umap in g:Netrw_UserMaps +" call Decho("type(umap[0]<".string(umap[0]).">)=".type(umap[0])." (should be 1=string)",'~'.expand("<slnum>")) +" call Decho("type(umap[1])=".type(umap[1])." (should be 1=string)",'~'.expand("<slnum>")) + " if umap[0] is a string and umap[1] is a string holding a function name + if type(umap[0]) == 1 && type(umap[1]) == 1 +" call Decho("nno <buffer> <silent> ".umap[0]." :call s:UserMaps(".a:islocal.",".string(umap[1]).")<cr>",'~'.expand("<slnum>")) + exe "nno <buffer> <silent> ".umap[0]." :call <SID>UserMaps(".a:islocal.",'".umap[1]."')<cr>" + else + call netrw#ErrorMsg(s:WARNING,"ignoring usermap <".string(umap[0])."> -- not a [string,funcref] entry",99) + endif + endfor + endif +" call Dret("netrw#UserMaps") +endfun + +" --------------------------------------------------------------------- " netrw#WinPath: tries to insure that the path is windows-acceptable, whether cygwin is used or not {{{2 fun! netrw#WinPath(path) " call Dfunc("netrw#WinPath(path<".a:path.">)") @@ -10386,7 +10820,7 @@ fun! s:ComposePath(base,subdir) " call Dfunc("s:ComposePath(base<".a:base."> subdir<".a:subdir.">)") if has("amiga") -" call Decho("amiga") +" call Decho("amiga",'~'.expand("<slnum>")) let ec = a:base[s:Strlen(a:base)-1] if ec != '/' && ec != ':' let ret = a:base . "/" . a:subdir @@ -10395,19 +10829,19 @@ fun! s:ComposePath(base,subdir) endif elseif a:subdir =~ '^\a:[/\\][^/\\]' && (has("win32") || has("win95") || has("win64") || has("win16")) -" call Decho("windows") +" call Decho("windows",'~'.expand("<slnum>")) let ret= a:subdir elseif a:base =~ '^\a:[/\\][^/\\]' && (has("win32") || has("win95") || has("win64") || has("win16")) -" call Decho("windows") +" call Decho("windows",'~'.expand("<slnum>")) if a:base =~ '[/\\]$' let ret= a:base.a:subdir else - let ret= a:base."/".a:subdir + let ret= a:base.'/'.a:subdir endif - elseif a:base =~ '^\a\+://' -" call Decho("remote linux/macos") + elseif a:base =~ '^\a\{3,}://' +" call Decho("remote linux/macos",'~'.expand("<slnum>")) let urlbase = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\1','') let curpath = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\2','') if a:subdir == '../' @@ -10420,12 +10854,12 @@ fun! s:ComposePath(base,subdir) else let ret= urlbase.curpath.a:subdir endif -" call Decho("urlbase<".urlbase.">") -" call Decho("curpath<".curpath.">") -" call Decho("ret<".ret.">") +" call Decho("urlbase<".urlbase.">",'~'.expand("<slnum>")) +" call Decho("curpath<".curpath.">",'~'.expand("<slnum>")) +" call Decho("ret<".ret.">",'~'.expand("<slnum>")) else -" call Decho("local linux/macos") +" call Decho("local linux/macos",'~'.expand("<slnum>")) let ret = substitute(a:base."/".a:subdir,"//","/","g") if a:base =~ '^//' " keeping initial '//' for the benefit of network share listing support @@ -10471,9 +10905,9 @@ fun! s:FileReadable(fname) " call Dfunc("s:FileReadable(fname<".a:fname.">)") if g:netrw_cygwin - let ret= filereadable(substitute(a:fname,g:netrw_cygdrive.'/\(.\)','\1:/','')) + let ret= filereadable(s:NetrwFile(substitute(a:fname,g:netrw_cygdrive.'/\(.\)','\1:/',''))) else - let ret= filereadable(a:fname) + let ret= filereadable(s:NetrwFile(a:fname)) endif " call Dret("s:FileReadable ".ret) @@ -10490,14 +10924,14 @@ fun! s:GetTempfile(fname) if !exists("b:netrw_tmpfile") " get a brand new temporary filename let tmpfile= tempname() -" call Decho("tmpfile<".tmpfile."> : from tempname()") +" call Decho("tmpfile<".tmpfile."> : from tempname()",'~'.expand("<slnum>")) let tmpfile= substitute(tmpfile,'\','/','ge') -" call Decho("tmpfile<".tmpfile."> : chgd any \\ -> /") +" call Decho("tmpfile<".tmpfile."> : chgd any \\ -> /",'~'.expand("<slnum>")) " sanity check -- does the temporary file's directory exist? - if !isdirectory(substitute(tmpfile,'[^/]\+$','','e')) -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") + if !isdirectory(s:NetrwFile(substitute(tmpfile,'[^/]\+$','','e'))) +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your <".substitute(tmpfile,'[^/]\+$','','e')."> directory is missing!",2) " call Dret("s:GetTempfile getcwd<".getcwd().">") return "" @@ -10505,7 +10939,7 @@ fun! s:GetTempfile(fname) " let netrw#NetSource() know about the tmpfile let s:netrw_tmpfile= tmpfile " used by netrw#NetSource() and netrw#BrowseX() -" call Decho("tmpfile<".tmpfile."> s:netrw_tmpfile<".s:netrw_tmpfile.">") +" call Decho("tmpfile<".tmpfile."> s:netrw_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>")) " o/s dependencies if g:netrw_cygwin != 0 @@ -10518,17 +10952,17 @@ fun! s:GetTempfile(fname) let tmpfile = tmpfile endif let b:netrw_tmpfile= tmpfile -" call Decho("o/s dependent fixed tempname<".tmpfile.">") +" call Decho("o/s dependent fixed tempname<".tmpfile.">",'~'.expand("<slnum>")) else " re-use temporary filename let tmpfile= b:netrw_tmpfile -" call Decho("tmpfile<".tmpfile."> re-using") +" call Decho("tmpfile<".tmpfile."> re-using",'~'.expand("<slnum>")) endif " use fname's suffix for the temporary file if a:fname != "" if a:fname =~ '\.[^./]\+$' -" call Decho("using fname<".a:fname.">'s suffix") +" call Decho("using fname<".a:fname.">'s suffix",'~'.expand("<slnum>")) if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$' let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') elseif a:fname =~ '.txz$' @@ -10536,16 +10970,16 @@ fun! s:GetTempfile(fname) else let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') endif -" call Decho("suffix<".suffix.">") +" call Decho("suffix<".suffix.">",'~'.expand("<slnum>")) let tmpfile= substitute(tmpfile,'\.tmp$','','e') -" call Decho("chgd tmpfile<".tmpfile."> (removed any .tmp suffix)") +" call Decho("chgd tmpfile<".tmpfile."> (removed any .tmp suffix)",'~'.expand("<slnum>")) let tmpfile .= suffix -" call Decho("chgd tmpfile<".tmpfile."> (added ".suffix." suffix) netrw_fname<".b:netrw_fname.">") +" call Decho("chgd tmpfile<".tmpfile."> (added ".suffix." suffix) netrw_fname<".b:netrw_fname.">",'~'.expand("<slnum>")) let s:netrw_tmpfile= tmpfile " supports netrw#NetSource() endif endif -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:GetTempfile <".tmpfile.">") return tmpfile endfun @@ -10582,7 +11016,7 @@ fun! s:MakeBookmark(fname) if index(g:netrw_bookmarklist,a:fname) == -1 " curdir not currently in g:netrw_bookmarklist, so include it - if isdirectory(a:fname) && a:fname !~ '/$' + if isdirectory(s:NetrwFile(a:fname)) && a:fname !~ '/$' call add(g:netrw_bookmarklist,a:fname.'/') elseif a:fname !~ '/' call add(g:netrw_bookmarklist,getcwd()."/".a:fname) @@ -10598,13 +11032,13 @@ endfun " --------------------------------------------------------------------- " s:MergeBookmarks: merge current bookmarks with saved bookmarks {{{2 fun! s:MergeBookmarks() -" call Dfunc("s:MergeBookmarks()") +" call Dfunc("s:MergeBookmarks() : merge current bookmarks into .netrwbook") " get bookmarks from .netrwbook file let savefile= s:NetrwHome()."/.netrwbook" - if filereadable(savefile) -" call Decho("merge bookmarks (active and file)") + if filereadable(s:NetrwFile(savefile)) +" call Decho("merge bookmarks (active and file)",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwBookHistSave() -" call Decho("bookmark delete savefile<".savefile.">") +" call Decho("bookmark delete savefile<".savefile.">",'~'.expand("<slnum>")) NetrwKeepj call delete(savefile) endif " call Dret("s:MergeBookmarks") @@ -10641,13 +11075,13 @@ fun! s:NetrwCursor() if &ft != "netrw" " if the current window isn't a netrw directory listing window, then use user cursorline/column " settings. Affects when netrw is used to read/write a file using scp/ftp/etc. -" call Decho("case ft!=netrw: use user cul,cuc") +" call Decho("case ft!=netrw: use user cul,cuc",'~'.expand("<slnum>")) let &l:cursorline = s:netrw_usercul let &l:cursorcolumn = s:netrw_usercuc elseif g:netrw_cursor == 4 " all styles: cursorline, cursorcolumn -" call Decho("case g:netrw_cursor==4: setl cul cuc") +" call Decho("case g:netrw_cursor==4: setl cul cuc",'~'.expand("<slnum>")) setl cursorline setl cursorcolumn @@ -10655,11 +11089,11 @@ fun! s:NetrwCursor() " thin-long-tree: cursorline, user's cursorcolumn " wide : cursorline, cursorcolumn if w:netrw_liststyle == s:WIDELIST -" call Decho("case g:netrw_cursor==3 and wide: setl cul cuc") +" call Decho("case g:netrw_cursor==3 and wide: setl cul cuc",'~'.expand("<slnum>")) setl cursorline setl cursorcolumn else -" call Decho("case g:netrw_cursor==3 and not wide: setl cul (use user's cuc)") +" call Decho("case g:netrw_cursor==3 and not wide: setl cul (use user's cuc)",'~'.expand("<slnum>")) setl cursorline let &l:cursorcolumn = s:netrw_usercuc endif @@ -10667,7 +11101,7 @@ fun! s:NetrwCursor() elseif g:netrw_cursor == 2 " thin-long-tree: cursorline, user's cursorcolumn " wide : cursorline, user's cursorcolumn -" call Decho("case g:netrw_cursor==2: setl cuc (use user's cul)") +" call Decho("case g:netrw_cursor==2: setl cuc (use user's cul)",'~'.expand("<slnum>")) let &l:cursorcolumn = s:netrw_usercuc setl cursorline @@ -10676,16 +11110,16 @@ fun! s:NetrwCursor() " wide : cursorline, user's cursorcolumn let &l:cursorcolumn = s:netrw_usercuc if w:netrw_liststyle == s:WIDELIST -" call Decho("case g:netrw_cursor==2 and wide: setl cul (use user's cuc)") +" call Decho("case g:netrw_cursor==2 and wide: setl cul (use user's cuc)",'~'.expand("<slnum>")) setl cursorline else -" call Decho("case g:netrw_cursor==2 and not wide: (use user's cul,cuc)") +" call Decho("case g:netrw_cursor==2 and not wide: (use user's cul,cuc)",'~'.expand("<slnum>")) let &l:cursorline = s:netrw_usercul endif else " all styles: user's cursorline, user's cursorcolumn -" call Decho("default: (use user's cul,cuc)") +" call Decho("default: (use user's cul,cuc)",'~'.expand("<slnum>")) let &l:cursorline = s:netrw_usercul let &l:cursorcolumn = s:netrw_usercuc endif @@ -10722,11 +11156,11 @@ fun! s:NetrwDelete(path) let result = delete(path) let &shellslash = sskeep else -" call Decho("exe let result= ".a:cmd."('".path."')") +" call Decho("exe let result= ".a:cmd."('".path."')",'~'.expand("<slnum>")) let result= delete(path) endif else -" call Decho("let result= delete(".path.")") +" call Decho("let result= delete(".path.")",'~'.expand("<slnum>")) let result= delete(path) endif if result < 0 @@ -10741,10 +11175,10 @@ endfun " s:NetrwEnew: opens a new buffer, passes netrw buffer variables through {{{2 fun! s:NetrwEnew(...) " call Dfunc("s:NetrwEnew() a:0=".a:0." bufnr($)=".bufnr("$")) -" call Decho("curdir<".((a:0>0)? a:1 : "")."> buf#".bufnr("%")."<".bufname("%").">") +" call Decho("curdir<".((a:0>0)? a:1 : "")."> buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) " grab a function-local-variable copy of buffer variables -" call Decho("make function-local copy of netrw variables") +" call Decho("make function-local copy of netrw variables",'~'.expand("<slnum>")) if exists("b:netrw_bannercnt") |let netrw_bannercnt = b:netrw_bannercnt |endif if exists("b:netrw_browser_active") |let netrw_browser_active = b:netrw_browser_active |endif if exists("b:netrw_cpf") |let netrw_cpf = b:netrw_cpf |endif @@ -10763,15 +11197,15 @@ fun! s:NetrwEnew(...) if exists("b:netrw_prvdir") |let netrw_prvdir = b:netrw_prvdir |endif NetrwKeepj call s:NetrwOptionRestore("w:") -" call Decho("generate a buffer with NetrwKeepj keepalt enew!") +" call Decho("generate a buffer with NetrwKeepj keepalt enew!",'~'.expand("<slnum>")) let netrw_keepdiff= &l:diff noswapfile NetrwKeepj keepalt enew! let &l:diff= netrw_keepdiff -" call Decho("bufnr($)=".bufnr("$")." winnr($)=".winnr("$")) +" call Decho("bufnr($)=".bufnr("$")." winnr($)=".winnr("$"),'~'.expand("<slnum>")) NetrwKeepj call s:NetrwOptionSave("w:") " copy function-local-variables to buffer variable equivalents -" call Decho("copy function-local variables back to buffer netrw variables") +" call Decho("copy function-local variables back to buffer netrw variables",'~'.expand("<slnum>")) if exists("netrw_bannercnt") |let b:netrw_bannercnt = netrw_bannercnt |endif if exists("netrw_browser_active") |let b:netrw_browser_active = netrw_browser_active |endif if exists("netrw_cpf") |let b:netrw_cpf = netrw_cpf |endif @@ -10841,7 +11275,7 @@ fun! s:NetrwInsureWinVars() endwhile exe "keepalt ".curwin."wincmd w" if exists("winvars") -" call Decho("copying w#".iwin." window variables to w#".curwin) +" call Decho("copying w#".iwin." window variables to w#".curwin,'~'.expand("<slnum>")) for k in keys(winvars) let w:{k}= winvars[k] endfor @@ -10860,7 +11294,7 @@ fun! s:NetrwLcd(newdir) catch /^Vim\%((\a\+)\)\=:E344/ " Vim's lcd fails with E344 when attempting to go above the 'root' of a Windows share. " Therefore, detect if a Windows share is present, and if E344 occurs, just settle at - " 'root' (ie. '\'). The share name may start with either backslashes ('\\Foo') or + " 'root' (ie. '\'). The share name may start with either backslashes ('\\Foo') or " forward slashes ('//Foo'), depending on whether backslashes have been converted to " forward slashes by earlier code; so check for both. if (has("win32") || has("win95") || has("win64") || has("win16")) && !g:netrw_cygwin @@ -10875,9 +11309,9 @@ fun! s:NetrwLcd(newdir) let a:newdir= w:netrw_prvdir else call s:NetrwOptionRestore("w:") -" call Decho("setl noma nomod nowrap") +" call Decho("setl noma nomod nowrap",'~'.expand("<slnum>")) exe "setl ".g:netrw_bufsettings -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)") +" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) let a:newdir= dirname " call Dret("s:NetrwBrowse : reusing buffer#".(exists("bufnum")? bufnum : 'N/A')."<".dirname."> getcwd<".getcwd().">") return @@ -10939,12 +11373,12 @@ fun! s:RemotePathAnalysis(dirname) let s:machine = substitute(s:machine,dirpat,'\2','') endif -" call Decho("set up s:method <".s:method .">") -" call Decho("set up s:user <".s:user .">") -" call Decho("set up s:machine<".s:machine.">") -" call Decho("set up s:port <".s:port.">") -" call Decho("set up s:path <".s:path .">") -" call Decho("set up s:fname <".s:fname .">") +" call Decho("set up s:method <".s:method .">",'~'.expand("<slnum>")) +" call Decho("set up s:user <".s:user .">",'~'.expand("<slnum>")) +" call Decho("set up s:machine<".s:machine.">",'~'.expand("<slnum>")) +" call Decho("set up s:port <".s:port.">",'~'.expand("<slnum>")) +" call Decho("set up s:path <".s:path .">",'~'.expand("<slnum>")) +" call Decho("set up s:fname <".s:fname .">",'~'.expand("<slnum>")) " call Dret("s:RemotePathAnalysis") endfun @@ -10954,7 +11388,7 @@ endfun " Returns status " Runs system() on " [cd REMOTEDIRPATH;] a:cmd -" Note that it doesn't do shellescape(a:cmd)! +" Note that it doesn't do s:ShellEscape(a:cmd)! fun! s:RemoteSystem(cmd) " call Dfunc("s:RemoteSystem(cmd<".a:cmd.">)") if !executable(g:netrw_ssh_cmd) @@ -10965,12 +11399,12 @@ fun! s:RemoteSystem(cmd) let cmd = s:MakeSshCmd(g:netrw_ssh_cmd." USEPORT HOSTNAME") let remotedir= substitute(b:netrw_curdir,'^.*//[^/]\+/\(.*\)$','\1','') if remotedir != "" - let cmd= cmd.' cd '.shellescape(remotedir).";" + let cmd= cmd.' cd '.s:ShellEscape(remotedir).";" else let cmd= cmd.' ' endif let cmd= cmd.a:cmd -" call Decho("call system(".cmd.")") +" call Decho("call system(".cmd.")",'~'.expand("<slnum>")) let ret= system(cmd) endif " call Dret("s:RemoteSystem ".ret) @@ -11009,23 +11443,26 @@ endfun " is true) and a command, :Rexplore, which call this function. " " s:nbcd_curpos_{bufnr('%')} is set up by s:NetrwBrowseChgDir() +" +" s:rexposn_BUFNR used to save/restore cursor position fun! s:NetrwRexplore(islocal,dirname) if exists("s:netrwdrag") return endif " call Dfunc("s:NetrwRexplore() w:netrw_rexlocal=".w:netrw_rexlocal." w:netrw_rexdir<".w:netrw_rexdir.">") -" call Decho("ft=".&ft." win#".winnr()." w:netrw_rexfile<".(exists("w:netrw_rexfile")? w:netrw_rexfile : 'n/a').">") +" call Decho("currently in bufname<".bufname("%").">",'~'.expand("<slnum>")) +" call Decho("ft=".&ft." win#".winnr()." w:netrw_rexfile<".(exists("w:netrw_rexfile")? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>")) if &ft == "netrw" && exists("w:netrw_rexfile") && w:netrw_rexfile != "" " a :Rex while in a netrw buffer means: edit the file in w:netrw_rexfile -" call Decho("in netrw buffer, will edit file<".w:netrw_rexfile.">") +" call Decho("in netrw buffer, will edit file<".w:netrw_rexfile.">",'~'.expand("<slnum>")) exe "NetrwKeepj e ".w:netrw_rexfile unlet w:netrw_rexfile " call Dret("s:NetrwRexplore returning from netrw to buf#".bufnr("%")."<".bufname("%")."> (ft=".&ft.")") return " else " Decho -" call Decho("treating as not-netrw-buffer: ft=".&ft.((&ft == "netrw")? " == netrw" : "!= netrw")) -" call Decho("treating as not-netrw-buffer: w:netrw_rexfile<".((exists("w:netrw_rexfile"))? w:netrw_rexfile : 'n/a').">") +" call Decho("treating as not-netrw-buffer: ft=".&ft.((&ft == "netrw")? " == netrw" : "!= netrw"),'~'.expand("<slnum>")) +" call Decho("treating as not-netrw-buffer: w:netrw_rexfile<".((exists("w:netrw_rexfile"))? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>")) endif " --------------------------- @@ -11034,43 +11471,37 @@ fun! s:NetrwRexplore(islocal,dirname) " record current file so :Rex can return to it from netrw let w:netrw_rexfile= expand("%") +" call Decho("set w:netrw_rexfile<".w:netrw_rexfile."> (win#".winnr().")",'~'.expand("<slnum>")) if !exists("w:netrw_rexlocal") " call Dret("s:NetrwRexplore w:netrw_rexlocal doesn't exist (".&ft.")") return endif -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) if w:netrw_rexlocal - if g:netrw_keepj =~ "keepj" - keepj call netrw#LocalBrowseCheck(w:netrw_rexdir) - else - call netrw#LocalBrowseCheck(w:netrw_rexdir) - endif - elseif g:netrw_keepj =~ "keepj" - keepj call s:NetrwBrowse(0,w:netrw_rexdir) + NetrwKeepj call netrw#LocalBrowseCheck(w:netrw_rexdir) else - call s:NetrwBrowse(0,w:netrw_rexdir) + NetrwKeepj call s:NetrwBrowse(0,w:netrw_rexdir) endif if exists("s:initbeval") setl beval endif if exists("s:rexposn_".bufnr("%")) -" call Decho("restore posn, then unlet s:rexposn_".bufnr('%')) - if g:netrw_keepj =~ "keepj" - keepj call netrw#RestorePosn(s:rexposn_{bufnr('%')}) - else - call netrw#RestorePosn(s:rexposn_{bufnr('%')}) +" call Decho("restore posn, then unlet s:rexposn_".bufnr('%')."<".bufname("%").">",'~'.expand("<slnum>")) + " restore position in directory listing + NetrwKeepj call netrw#RestorePosn(s:rexposn_{bufnr('%')}) + if exists("s:rexposn_".bufnr('%')) + unlet s:rexposn_{bufnr('%')} endif - unlet s:rexposn_{bufnr('%')} else -" call Decho("s:rexposn_".bufnr('%')." doesn't exist") +" call Decho("s:rexposn_".bufnr('%')."<".bufname("%")."> doesn't exist",'~'.expand("<slnum>")) endif if exists("s:explore_match") exe "2match netrwMarkFile /".s:explore_match."/" endif -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo) +" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) " call Dret("s:NetrwRexplore : ft=".&ft) endfun @@ -11141,8 +11572,10 @@ endfun " s:SetRexDir: set directory for :Rexplore {{{2 fun! s:SetRexDir(islocal,dirname) " call Dfunc("s:SetRexDir(islocal=".a:islocal." dirname<".a:dirname.">)") - let w:netrw_rexdir = a:dirname - let w:netrw_rexlocal = a:islocal + let w:netrw_rexdir = a:dirname + let w:netrw_rexlocal = a:islocal + let s:rexposn_{bufnr("%")} = netrw#SavePosn() +" call Decho("setting s:rexposn_".bufnr("%")."<".bufname("%")."> to SavePosn",'~'.expand("<slnum>")) " call Dret("s:SetRexDir : win#".winnr()." ".(a:islocal? "local" : "remote")." dir: ".a:dirname) endfun @@ -11150,22 +11583,23 @@ endfun " s:ShowLink: used to modify thin and tree listings to show links {{{2 fun! s:ShowLink() " " call Dfunc("s:ShowLink()") -" " call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist").">") -" " call Decho(printf("line#%4d: %s",line("."),getline("."))) +" " call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist").">",'~'.expand("<slnum>")) +" " call Decho(printf("line#%4d: %s",line("."),getline(".")),'~'.expand("<slnum>")) if exists("b:netrw_curdir") norm! $?\a let fname = b:netrw_curdir.'/'.s:NetrwGetWord() let resname = resolve(fname) - if resname =~ '^\M'.b:netrw_curdir +" " call Decho("fname <".fname.">",'~'.expand("<slnum>")) +" " call Decho("resname <".resname.">",'~'.expand("<slnum>")) +" " call Decho("b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) + if resname =~ '^\M'.b:netrw_curdir.'/' let dirlen = strlen(b:netrw_curdir) let resname = strpart(resname,dirlen+1) -" " call Decho("resname<".resname."> (b:netrw_curdir elided)") -" " else " Decho -" " call Decho("resname<".fname.">") +" " call Decho("resname<".resname."> (b:netrw_curdir elided)",'~'.expand("<slnum>")) endif let modline = getline(".")."\t --> ".resname -" " call Decho("fname <".fname.">") -" " call Decho("modline<".modline.">") +" " call Decho("fname <".fname.">",'~'.expand("<slnum>")) +" " call Decho("modline<".modline.">",'~'.expand("<slnum>")) setl noro ma call setline(".",modline) setl ro noma nomod @@ -11203,22 +11637,22 @@ fun! s:Strlen(x) if v:version >= 703 && exists("*strdisplaywidth") let ret= strdisplaywidth(a:x) - + elseif type(g:Align_xstrlen) == 1 " allow user to specify a function to compute the string length (ie. let g:Align_xstrlen="mystrlenfunc") exe "let ret= ".g:Align_xstrlen."('".substitute(a:x,"'","''","g")."')" - + elseif g:Align_xstrlen == 1 " number of codepoints (Latin a + combining circumflex is two codepoints) " (comment from TM, solution from NW) let ret= strlen(substitute(a:x,'.','c','g')) - + elseif g:Align_xstrlen == 2 " number of spacing codepoints (Latin a + combining circumflex is one spacing " codepoint; a hard tab is one; wide and narrow CJK are one each; etc.) " (comment from TM, solution from TM) let ret=strlen(substitute(a:x, '.\Z', 'x', 'g')) - + elseif g:Align_xstrlen == 3 " virtual length (counting, for instance, tabs as anything between 1 and " 'tabstop', wide CJK as 2 rather than 1, Arabic alif as zero when immediately @@ -11231,7 +11665,7 @@ fun! s:Strlen(x) d NetrwKeepj norm! k let &l:mod= modkeep - + else " at least give a decent default let ret= strlen(a:x) @@ -11241,6 +11675,16 @@ fun! s:Strlen(x) endfun " --------------------------------------------------------------------- +" s:ShellEscape: shellescape(), or special windows handling {{{2 +fun! s:ShellEscape(s, ...) + if (has('win32') || has('win64')) && $SHELL == '' && &shellslash + return printf('"%s"', substitute(a:s, '"', '""', 'g')) + endif + let f = a:0 > 0 ? a:1 : 0 + return shellescape(a:s, f) +endfun + +" --------------------------------------------------------------------- " s:TreeListMove: {{{2 fun! s:TreeListMove(dir) " call Dfunc("s:TreeListMove(dir<".a:dir.">)") @@ -11249,33 +11693,33 @@ fun! s:TreeListMove(dir) let nxtline = (line(".") < line("$"))? getline(line(".")+1) : '' let curindent= substitute(curline,'^\([| ]*\).\{-}$','\1','') let indentm1 = substitute(curindent,'^'.s:treedepthstring.' ','','') -" call Decho("prvline <".prvline."> #".line(".")-1) -" call Decho("curline <".curline."> #".line(".")) -" call Decho("nxtline <".nxtline."> #".line(".")+1) -" call Decho("curindent<".curindent.">") -" call Decho("indentm1 <".indentm1.">") +" call Decho("prvline <".prvline."> #".line(".")-1,'~'.expand("<slnum>")) +" call Decho("curline <".curline."> #".line("."),'~'.expand("<slnum>")) +" call Decho("nxtline <".nxtline."> #".line(".")+1,'~'.expand("<slnum>")) +" call Decho("curindent<".curindent.">",'~'.expand("<slnum>")) +" call Decho("indentm1 <".indentm1.">",'~'.expand("<slnum>")) if curline !~ '/$' -" call Decho('regfile') +" call Decho('regfile','~'.expand("<slnum>")) if a:dir == '[' && prvline != '' NetrwKeepj norm! 0 let nl = search('^'.indentm1.'[^'.s:treedepthstring.']','bWe') " search backwards from regular file -" call Decho("regfile srch back: ".nl) +" call Decho("regfile srch back: ".nl,'~'.expand("<slnum>")) elseif a:dir == ']' && nxtline != '' NetrwKeepj norm! $ let nl = search('^'.indentm1.'[^'.s:treedepthstring.']','We') " search forwards from regular file -" call Decho("regfile srch fwd: ".nl) +" call Decho("regfile srch fwd: ".nl,'~'.expand("<slnum>")) endif elseif a:dir == '[' && prvline != '' NetrwKeepj norm! 0 let curline= line(".") let nl = search('^'.curindent.'[^'.s:treedepthstring.']','bWe') " search backwards From directory, same indentation -" call Decho("dir srch back ind: ".nl) +" call Decho("dir srch back ind: ".nl,'~'.expand("<slnum>")) if nl != 0 if line(".") == curline-1 let nl= search('^'.indentm1.'[^'.s:treedepthstring.']','bWe') " search backwards from directory, indentation - 1 -" call Decho("dir srch back ind-1: ".nl) +" call Decho("dir srch back ind-1: ".nl,'~'.expand("<slnum>")) endif endif @@ -11283,11 +11727,11 @@ fun! s:TreeListMove(dir) NetrwKeepj norm! $ let curline = line(".") let nl = search('^'.curindent.'[^'.s:treedepthstring.']','We') " search forwards from directory, same indentation -" call Decho("dir srch fwd ind: ".nl) +" call Decho("dir srch fwd ind: ".nl,'~'.expand("<slnum>")) if nl != 0 if line(".") == curline+1 let nl= search('^'.indentm1.'[^'.s:treedepthstring.']','We') " search forwards from directory, indentation - 1 -" call Decho("dir srch fwd ind-1: ".nl) +" call Decho("dir srch fwd ind-1: ".nl,'~'.expand("<slnum>")) endif endif @@ -11333,6 +11777,48 @@ fun! s:UseBufWinVars() endfun " --------------------------------------------------------------------- +" s:UserMaps: supports user-defined UserMaps {{{2 +" * calls a user-supplied funcref(islocal,curdir) +" * interprets result +" See netrw#UserMaps() +fun! s:UserMaps(islocal,funcname) +" call Dfunc("s:UserMaps(islocal=".a:islocal.",funcname<".a:funcname.">)") + + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + let Funcref = function(a:funcname) + let result = Funcref(a:islocal) + + if type(result) == 1 + " if result from user's funcref is a string... +" call Decho("result string from user funcref<".result.">",'~'.expand("<slnum>")) + if result == "refresh" +" call Decho("refreshing display",'~'.expand("<slnum>")) + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) + elseif result != "" +" call Decho("executing result<".result.">",'~'.expand("<slnum>")) + exe result + endif + + elseif type(result) == 3 + " if result from user's funcref is a List... +" call Decho("result List from user funcref<".string(result).">",'~'.expand("<slnum>")) + for action in result + if action == "refresh" +" call Decho("refreshing display",'~'.expand("<slnum>")) + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) + elseif action != "" +" call Decho("executing action<".action.">",'~'.expand("<slnum>")) + exe action + endif + endfor + endif + +" call Dret("s:UserMaps") +endfun + +" --------------------------------------------------------------------- " Settings Restoration: {{{1 let &cpo= s:keepcpo unlet s:keepcpo diff --git a/runtime/autoload/phpcomplete.vim b/runtime/autoload/phpcomplete.vim index 5ddad88873..7f25d9df33 100644 --- a/runtime/autoload/phpcomplete.vim +++ b/runtime/autoload/phpcomplete.vim @@ -3,7 +3,7 @@ " Maintainer: Dávid Szabó ( complex857 AT gmail DOT com ) " Previous Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) " URL: https://github.com/shawncplus/phpcomplete.vim -" Last Change: 2014 Dec 01 +" Last Change: 2015 Jul 13 " " OPTIONS: " @@ -141,71 +141,80 @@ function! phpcomplete#CompletePHP(findstart, base) " {{{ if a:base != "" let context = substitute(context, '\s*[$a-zA-Z_0-9\x7f-\xff]*$', '', '') end + else + let context = '' end - let [current_namespace, imports] = phpcomplete#GetCurrentNameSpace(getline(0, line('.'))) + try + let winheight = winheight(0) + let winnr = winnr() - if context =~? '^use\s' - return phpcomplete#CompleteUse(a:base) - endif + let [current_namespace, imports] = phpcomplete#GetCurrentNameSpace(getline(0, line('.'))) + + if context =~? '^use\s' || context ==? 'use' + return phpcomplete#CompleteUse(a:base) + endif - if context =~ '\(->\|::\)$' - " {{{ - " Get name of the class - let classname = phpcomplete#GetClassName(line('.'), context, current_namespace, imports) + if context =~ '\(->\|::\)$' + " {{{ + " Get name of the class + let classname = phpcomplete#GetClassName(line('.'), context, current_namespace, imports) - " Get location of class definition, we have to iterate through all - if classname != '' - if classname =~ '\' - " split the last \ segment as a classname, everything else is the namespace - let classname_parts = split(classname, '\') - let namespace = join(classname_parts[0:-2], '\') - let classname = classname_parts[-1] + " Get location of class definition, we have to iterate through all + if classname != '' + if classname =~ '\' + " split the last \ segment as a classname, everything else is the namespace + let classname_parts = split(classname, '\') + let namespace = join(classname_parts[0:-2], '\') + let classname = classname_parts[-1] + else + let namespace = '\' + endif + let classlocation = phpcomplete#GetClassLocation(classname, namespace) else - let namespace = '\' + let classlocation = '' endif - let classlocation = phpcomplete#GetClassLocation(classname, namespace) - else - let classlocation = '' - endif - if classlocation != '' - if classlocation == 'VIMPHP_BUILTINOBJECT' && has_key(g:php_builtin_classes, tolower(classname)) - return phpcomplete#CompleteBuiltInClass(context, classname, a:base) - endif + if classlocation != '' + if classlocation == 'VIMPHP_BUILTINOBJECT' && has_key(g:php_builtin_classes, tolower(classname)) + return phpcomplete#CompleteBuiltInClass(context, classname, a:base) + endif - if filereadable(classlocation) - let classfile = readfile(classlocation) - let classcontent = '' - let classcontent .= "\n".phpcomplete#GetClassContents(classlocation, classname) - let sccontent = split(classcontent, "\n") - let visibility = expand('%:p') == fnamemodify(classlocation, ':p') ? 'private' : 'public' + if filereadable(classlocation) + let classfile = readfile(classlocation) + let classcontent = '' + let classcontent .= "\n".phpcomplete#GetClassContents(classlocation, classname) + let sccontent = split(classcontent, "\n") + let visibility = expand('%:p') == fnamemodify(classlocation, ':p') ? 'private' : 'public' - return phpcomplete#CompleteUserClass(context, a:base, sccontent, visibility) + return phpcomplete#CompleteUserClass(context, a:base, sccontent, visibility) + endif endif - endif - return phpcomplete#CompleteUnknownClass(a:base, context) - " }}} - elseif context =~? 'implements' - return phpcomplete#CompleteClassName(a:base, ['i'], current_namespace, imports) - elseif context =~? 'extends\s\+.\+$' - return ['implements'] - elseif context =~? 'extends' - let kinds = context =~? 'class\s' ? ['c'] : ['i'] - return phpcomplete#CompleteClassName(a:base, kinds, current_namespace, imports) - elseif context =~? 'class [a-zA-Z_\x7f-\xff\\][a-zA-Z_0-9\x7f-\xff\\]*' - " special case when you've typed the class keyword and the name too, only extends and implements allowed there - return filter(['extends', 'implements'], 'stridx(v:val, a:base) == 0') - elseif context =~? 'new' - return phpcomplete#CompleteClassName(a:base, ['c'], current_namespace, imports) - endif - - if a:base =~ '^\$' - return phpcomplete#CompleteVariable(a:base) - else - return phpcomplete#CompleteGeneral(a:base, current_namespace, imports) - endif + return phpcomplete#CompleteUnknownClass(a:base, context) + " }}} + elseif context =~? 'implements' + return phpcomplete#CompleteClassName(a:base, ['i'], current_namespace, imports) + elseif context =~? 'extends\s\+.\+$' && a:base == '' + return ['implements'] + elseif context =~? 'extends' + let kinds = context =~? 'class\s' ? ['c'] : ['i'] + return phpcomplete#CompleteClassName(a:base, kinds, current_namespace, imports) + elseif context =~? 'class [a-zA-Z_\x7f-\xff\\][a-zA-Z_0-9\x7f-\xff\\]*' + " special case when you've typed the class keyword and the name too, only extends and implements allowed there + return filter(['extends', 'implements'], 'stridx(v:val, a:base) == 0') + elseif context =~? 'new' + return phpcomplete#CompleteClassName(a:base, ['c'], current_namespace, imports) + endif + + if a:base =~ '^\$' + return phpcomplete#CompleteVariable(a:base) + else + return phpcomplete#CompleteGeneral(a:base, current_namespace, imports) + endif + finally + silent! exec winnr.'resize '.winheight + endtry endfunction " }}} @@ -244,12 +253,13 @@ function! phpcomplete#CompleteUse(base) " {{{ if has_key(tag, 'namespace') let patched_ctags_detected = 1 endif + if tag.kind ==? 'n' && tag.name =~? '^'.namespace_match_pattern let patched_ctags_detected = 1 call add(namespaced_matches, {'word': tag.name, 'kind': 'n', 'menu': tag.filename, 'info': tag.filename }) - elseif has_key(tag, 'namespace') && (tag.kind ==? 'c' || tag.kind ==? 'i') && tag.namespace ==? namespace_for_class + elseif has_key(tag, 'namespace') && (tag.kind ==? 'c' || tag.kind ==? 'i' || tag.kind ==? 't') && tag.namespace ==? namespace_for_class call add(namespaced_matches, {'word': namespace_for_class.'\'.tag.name, 'kind': tag.kind, 'menu': tag.filename, 'info': tag.filename }) - elseif (tag.kind ==? 'c' || tag.kind ==? 'i') + elseif (tag.kind ==? 'c' || tag.kind ==? 'i' || tag.kind ==? 't') call add(no_namespace_matches, {'word': namespace_for_class.'\'.tag.name, 'kind': tag.kind, 'menu': tag.filename, 'info': tag.filename }) endif endfor @@ -272,6 +282,10 @@ function! phpcomplete#CompleteUse(base) " {{{ endfor endif + for comp in res + let comp.word = substitute(comp.word, '^\\', '', '') + endfor + return res endfunction " }}} @@ -304,7 +318,7 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze') if f_name =~? '^'.substitute(a:base, '\\', '\\\\', 'g') let f_args = matchstr(i, - \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\({\|$\)') + \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\(;\|{\|$\)') let int_functions[f_name.'('] = f_args.')' endif endfor @@ -326,6 +340,7 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ let ext_functions = {} let ext_constants = {} let ext_classes = {} + let ext_traits = {} let ext_interfaces = {} let ext_namespaces = {} @@ -420,7 +435,7 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ endif endif endif - elseif tag.kind ==? 'c' || tag.kind ==? 'i' + elseif tag.kind ==? 'c' || tag.kind ==? 'i' || tag.kind ==? 't' let info = ' - '.tag.filename let key = '' @@ -441,6 +456,8 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ let ext_classes[key] = info elseif tag.kind ==? 'i' let ext_interfaces[key] = info + elseif tag.kind ==? 't' + let ext_traits[key] = info endif endif endif @@ -463,7 +480,7 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ endfor for [interfacename, info] in items(g:php_builtin_interfacenames) if interfacename =~? '^'.base - let builtin_interfaces[leading_slash.interfacename] = info + let builtin_interfaces[leading_slash.g:php_builtin_interfaces[tolower(interfacename)].name] = info endif endfor endif @@ -511,6 +528,8 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ else let ext_interfaces[imported_name] = ' '.import.name.' - '.import.filename endif + elseif import.kind ==? 't' + let ext_traits[imported_name] = ' '.import.name.' - '.import.filename endif " no builtin interfaces @@ -540,6 +559,9 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ " Add external interfaces call extend(all_values, ext_interfaces) + " Add external traits + call extend(all_values, ext_traits) + " Add built-in classes call extend(all_values, builtin_classnames) @@ -566,6 +588,8 @@ function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{ elseif has_key(ext_interfaces, i) || has_key(builtin_interfaces, i) let info = has_key(ext_interfaces, i) ? ext_interfaces[i] : builtin_interfaces[i].' - builtin' let final_list += [{'word':i, 'kind': 'i', 'menu': info, 'info': i.info}] + elseif has_key(ext_traits, i) + let final_list += [{'word':i, 'kind': 't', 'menu': ext_traits[i], 'info': ext_traits[i]}] elseif has_key(int_constants, i) || has_key(builtin_constants, i) let info = has_key(int_constants, i) ? int_constants[i] : ' - builtin' let final_list += [{'word':i, 'kind': 'd', 'menu': info, 'info': i.info}] @@ -622,7 +646,7 @@ function! phpcomplete#CompleteUnknownClass(base, context) " {{{ let f_name = matchstr(i, \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze') let f_args = matchstr(i, - \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\({\|$\)') + \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\(;\|{\|$\)') let int_functions[f_name.'('] = f_args.')' endfor @@ -784,7 +808,7 @@ function! phpcomplete#CompleteClassName(base, kinds, current_namespace, imports) let tags = [] if len(tag_match_pattern) >= g:phpcomplete_min_num_of_chars_for_namespace_completion - let tags = phpcomplete#GetTaglist('^'.tag_match_pattern) + let tags = phpcomplete#GetTaglist('^\c'.tag_match_pattern) endif if len(tags) @@ -861,6 +885,39 @@ function! phpcomplete#CompareCompletionRow(i1, i2) " {{{ endfunction " }}} +function! s:getNextCharWithPos(filelines, current_pos) " {{{ + let line_no = a:current_pos[0] + let col_no = a:current_pos[1] + let last_line = a:filelines[len(a:filelines) - 1] + let end_pos = [len(a:filelines) - 1, strlen(last_line) - 1] + if line_no > end_pos[0] || line_no == end_pos[0] && col_no > end_pos[1] + return ['EOF', 'EOF'] + endif + + " we've not reached the end of the current line break + if col_no + 1 < strlen(a:filelines[line_no]) + let col_no += 1 + else + " we've reached the end of the current line, jump to the next + " non-blank line (blank lines have no position where we can read from, + " not even a whitespace. The newline char does not positionable by vim + let line_no += 1 + while strlen(a:filelines[line_no]) == 0 + let line_no += 1 + endwhile + + let col_no = 0 + endif + + " return 'EOF' string to signal end of file, normal results only one char + " in length + if line_no == end_pos[0] && col_no > end_pos[1] + return ['EOF', 'EOF'] + endif + + return [[line_no, col_no], a:filelines[line_no][col_no]] +endfunction " }}} + function! phpcomplete#EvaluateModifiers(modifiers, required_modifiers, prohibited_modifiers) " {{{ " if theres no modifier, and no modifier is allowed and no modifier is required if len(a:modifiers) == 0 && len(a:required_modifiers) == 0 @@ -924,7 +981,7 @@ function! phpcomplete#CompleteUserClass(context, base, sccontent, visibility) " let f_name = matchstr(i, \ 'function\s*&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze') let f_args = matchstr(i, - \ 'function\s*&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\({\|\_$\)') + \ 'function\s*&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\(;\|{\|\_$\)') if f_name != '' && stridx(f_name, '__') != 0 let c_functions[f_name.'('] = f_args if g:phpcomplete_parse_docblock_comments @@ -1322,8 +1379,8 @@ function! phpcomplete#GetCallChainReturnType(classname_candidate, class_candidat " Get Structured information of all classes and subclasses including namespace and includes " try to find the method's return type in docblock comment for classstructure in classcontents - let doclock_target_pattern = 'function\s\+&\?'.method.'\|\(public\|private\|protected\|var\).\+\$'.method - let doc_str = phpcomplete#GetDocBlock(split(classstructure.content, '\n'), doclock_target_pattern) + let docblock_target_pattern = 'function\s\+&\?'.method.'\|\(public\|private\|protected\|var\).\+\$'.method + let doc_str = phpcomplete#GetDocBlock(split(classstructure.content, '\n'), docblock_target_pattern) if doc_str != '' break endif @@ -1475,21 +1532,19 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor return '' endif - if line =~? '\v^\s*(abstract\s+|final\s+)*\s*class' - let class_name = matchstr(line, '\c\s*class\s*\zs'.class_name_pattern.'\ze') + if line =~? '\v^\s*(abstract\s+|final\s+)*\s*class\s' + let class_name = matchstr(line, '\cclass\s\+\zs'.class_name_pattern.'\ze') let extended_class = matchstr(line, '\cclass\s\+'.class_name_pattern.'\s\+extends\s\+\zs'.class_name_pattern.'\ze') let classname_candidate = a:context =~? 'parent::' ? extended_class : class_name - else - let i += 1 - continue + if classname_candidate != '' + let [classname_candidate, class_candidate_namespace] = phpcomplete#GetCallChainReturnType(classname_candidate, class_candidate_namespace, class_candidate_imports, methodstack) + " return absolute classname, without leading \ + return (class_candidate_namespace == '\' || class_candidate_namespace == '') ? classname_candidate : class_candidate_namespace.'\'.classname_candidate + endif endif - if classname_candidate != '' - let [classname_candidate, class_candidate_namespace] = phpcomplete#GetCallChainReturnType(classname_candidate, class_candidate_namespace, class_candidate_imports, methodstack) - " return absolute classname, without leading \ - return (class_candidate_namespace == '\' || class_candidate_namespace == '') ? classname_candidate : class_candidate_namespace.'\'.classname_candidate - endif + let i += 1 endwhile elseif a:context =~? '(\s*new\s\+'.class_name_pattern.'\s*)->' let classname_candidate = matchstr(a:context, '\cnew\s\+\zs'.class_name_pattern.'\ze') @@ -1602,26 +1657,26 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor endif endif - " in-file lookup for typehinted function arguments - " - the function can have a name or be anonymous (e.g., function qux() { ... } vs. function () { ... }) - " - the type-hinted argument can be anywhere in the arguments list. - if line =~? 'function\(\s\+'.function_name_pattern.'\)\?\s*(.\{-}'.class_name_pattern.'\s\+'.object && !object_is_array - let f_args = matchstr(line, '\cfunction\(\s\+'.function_name_pattern.'\)\?\s*(\zs.\{-}\ze)') - let args = split(f_args, '\s*\zs,\ze\s*') - for arg in args - if arg =~# object.'\(,\|$\)' - let classname_candidate = matchstr(arg, '\s*\zs'.class_name_pattern.'\ze\s\+'.object) - let [classname_candidate, class_candidate_namespace] = phpcomplete#ExpandClassName(classname_candidate, a:current_namespace, a:imports) + " function declaration line + if line =~? 'function\(\s\+'.function_name_pattern.'\)\?\s*(' + let function_lines = join(reverse(copy(lines)), " ") + " search for type hinted arguments + if function_lines =~? 'function\(\s\+'.function_name_pattern.'\)\?\s*(.\{-}'.class_name_pattern.'\s\+'.object && !object_is_array + let f_args = matchstr(function_lines, '\cfunction\(\s\+'.function_name_pattern.'\)\?\s*(\zs.\{-}\ze)') + let args = split(f_args, '\s*\zs,\ze\s*') + for arg in args + if arg =~# object.'\(,\|$\)' + let classname_candidate = matchstr(arg, '\s*\zs'.class_name_pattern.'\ze\s\+'.object) + let [classname_candidate, class_candidate_namespace] = phpcomplete#ExpandClassName(classname_candidate, a:current_namespace, a:imports) + break + endif + endfor + if classname_candidate != '' break endif - endfor - if classname_candidate != '' - break endif - endif - " if we see a function declaration, try loading the docblock for it and look for matching @params - if line =~? 'function\(\s\+'.function_name_pattern.'\)\?\s*(.\{-}'.object + " search for docblock for the function let match_line = substitute(line, '\\', '\\\\', 'g') let sccontent = getline(0, a:start_line - i) let doc_str = phpcomplete#GetDocBlock(sccontent, match_line) @@ -1641,13 +1696,16 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor endif " assignment for the variable in question with a variable on the right hand side - if line =~# '^\s*'.object.'\s*=&\?\s*'.variable_name_pattern + if line =~# '^\s*'.object.'\s*=&\?\s\+\(clone\)\?\s*'.variable_name_pattern " try to find the next non-comment or string ";" char - let start_col = match(line, '^\s*'.object.'\C\s*=\zs&\?\s*'.variable_name_pattern) - let filelines = reverse(lines) - let [pos, char] = s:getNextCharWithPos(filelines, [a:start_line - i - 1, start_col]) + let start_col = match(line, '^\s*'.object.'\C\s*=\zs&\?\s\+\(clone\)\?\s*'.variable_name_pattern) + let filelines = reverse(copy(lines)) + let [pos, char] = s:getNextCharWithPos(filelines, [len(filelines) - i, start_col]) let chars_read = 1 + let last_pos = pos + " function_boundary == 0 if we are not in a function + let real_lines_offset = len(function_boundary) == 1 ? 1 : function_boundary[0][0] " read while end of the file while char != 'EOF' && chars_read < 1000 let last_pos = pos @@ -1655,7 +1713,11 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor let chars_read += 1 " we got a candidate if char == ';' - let synIDName = synIDattr(synID(pos[0] + 1, pos[1] + 1, 0), 'name') + " pos values is relative to the function's lines, + " line 0 need to be offsetted with the line number + " where te function was started to get the line number + " in real buffer terms + let synIDName = synIDattr(synID(real_lines_offset + pos[0], pos[1] + 1, 0), 'name') " it's not a comment or string, end search if synIDName !~? 'comment\|string' break @@ -1663,7 +1725,7 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor endif endwhile - let prev_context = phpcomplete#GetCurrentInstruction(last_pos[0] + 1, last_pos[1], b:phpbegin) + let prev_context = phpcomplete#GetCurrentInstruction(real_lines_offset + last_pos[0], last_pos[1], b:phpbegin) if prev_context == '' " cannot get previous context give up return @@ -1683,12 +1745,14 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor " assignment for the variable in question with a function on the right hand side if line =~# '^\s*'.object.'\s*=&\?\s*'.function_invocation_pattern - " try to find the next non-comment or string ";" char let start_col = match(line, '\C^\s*'.object.'\s*=\zs&\?\s*'.function_invocation_pattern) - let filelines = reverse(lines) - let [pos, char] = s:getNextCharWithPos(filelines, [a:start_line - i - 1, start_col]) + let filelines = reverse(copy(lines)) + let [pos, char] = s:getNextCharWithPos(filelines, [len(filelines) - i, start_col]) let chars_read = 1 + let last_pos = pos + " function_boundary == 0 if we are not in a function + let real_lines_offset = len(function_boundary) == 1 ? 1 : function_boundary[0][0] " read while end of the file while char != 'EOF' && chars_read < 1000 let last_pos = pos @@ -1696,7 +1760,11 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor let chars_read += 1 " we got a candidate if char == ';' - let synIDName = synIDattr(synID(pos[0] + 1, pos[1] + 1, 0), 'name') + " pos values is relative to the function's lines, + " line 0 need to be offsetted with the line number + " where te function was started to get the line number + " in real buffer terms + let synIDName = synIDattr(synID(real_lines_offset + pos[0], pos[1] + 1, 0), 'name') " it's not a comment or string, end search if synIDName !~? 'comment\|string' break @@ -1704,7 +1772,7 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor endif endwhile - let prev_context = phpcomplete#GetCurrentInstruction(last_pos[0] + 1, last_pos[1], b:phpbegin) + let prev_context = phpcomplete#GetCurrentInstruction(real_lines_offset + last_pos[0], last_pos[1], b:phpbegin) if prev_context == '' " cannot get previous context give up return @@ -1807,6 +1875,9 @@ function! phpcomplete#GetClassLocation(classname, namespace) " {{{ if has_key(g:php_builtin_classes, tolower(a:classname)) && (a:namespace == '' || a:namespace == '\') return 'VIMPHP_BUILTINOBJECT' endif + if has_key(g:php_builtin_interfaces, tolower(a:classname)) && (a:namespace == '' || a:namespace == '\') + return 'VIMPHP_BUILTINOBJECT' + endif if a:namespace == '' || a:namespace == '\' let search_namespace = '\' @@ -1819,7 +1890,7 @@ function! phpcomplete#GetClassLocation(classname, namespace) " {{{ let i = 1 while i < line('.') let line = getline(line('.')-i) - if line =~? '^\s*\(abstract\s\+\|final\s\+\)*\s*class\s*'.a:classname.'\(\s\+\|$\)' && tolower(current_namespace) == search_namespace + if line =~? '^\s*\(abstract\s\+\|final\s\+\)*\s*\(class\|interface\|trait\)\s*'.a:classname.'\(\s\+\|$\|{\)' && tolower(current_namespace) == search_namespace return expand('%:p') else let i += 1 @@ -1831,7 +1902,9 @@ function! phpcomplete#GetClassLocation(classname, namespace) " {{{ let no_namespace_candidate = '' let tags = phpcomplete#GetTaglist('^'.a:classname.'$') for tag in tags - if tag.kind == 'c' || tag.kind == 'i' + " We'll allow interfaces and traits to be handled classes since you + " can't have colliding names with different kinds anyway + if tag.kind == 'c' || tag.kind == 'i' || tag.kind == 't' if !has_key(tag, 'namespace') let no_namespace_candidate = tag.filename else @@ -1981,7 +2054,7 @@ function! phpcomplete#GetClassContentsStructure(file_path, file_lines, class_nam silent! below 1new silent! 0put =cfile - call search('\(class\|interface\)\_s\+'.a:class_name.'\(\>\|$\)') + call search('\c\(class\|interface\|trait\)\_s\+'.a:class_name.'\(\>\|$\)') let cfline = line('.') call search('{') let endline = line('.') @@ -1989,13 +2062,62 @@ function! phpcomplete#GetClassContentsStructure(file_path, file_lines, class_nam let content = join(getline(cfline, endline), "\n") " Catch extends if content =~? 'extends' - let extends_class = matchstr(content, 'class\_s\+'.a:class_name.'\_s\+extends\_s\+\zs'.class_name_pattern.'\ze') + let extends_string = matchstr(content, '\(class\|interface\)\_s\+'.a:class_name.'\_.\+extends\_s\+\zs\('.class_name_pattern.'\(,\|\_s\)*\)\+\ze\(extends\|{\)') + let extended_classes = map(split(extends_string, '\(,\|\_s\)\+'), 'substitute(v:val, "\\_s\\+", "", "g")') + else + let extended_classes = '' + endif + + " Catch implements + if content =~? 'implements' + let implements_string = matchstr(content, 'class\_s\+'.a:class_name.'\_.\+implements\_s\+\zs\('.class_name_pattern.'\(,\|\_s\)*\)\+\ze') + let implemented_interfaces = map(split(implements_string, '\(,\|\_s\)\+'), 'substitute(v:val, "\\_s\\+", "", "g")') else - let extends_class = '' + let implemented_interfaces = [] endif call searchpair('{', '', '}', 'W') - let classcontent = join(getline(cfline, line('.')), "\n") + let class_closing_bracket_line = line('.') + let classcontent = join(getline(cfline, class_closing_bracket_line), "\n") + + let used_traits = [] + " move back to the line next to the class's definition + call cursor(endline + 1, 1) + let keep_searching = 1 + while keep_searching != 0 + " try to grab "use..." keywords + let [lnum, col] = searchpos('\c^\s\+use\s\+'.class_name_pattern, 'cW', class_closing_bracket_line) + let syn_name = synIDattr(synID(lnum, col, 0), "name") + if syn_name =~? 'string\|comment' + call cursor(lnum + 1, 1) + continue + endif + + let trait_line = getline(lnum) + if trait_line !~? ';' + " try to find the next line containing ';' + let l = lnum + let search_line = trait_line + + " add lines from the file until theres no ';' in them + while search_line !~? ';' && l > 0 + " file lines are reversed so we need to go backwards + let l += 1 + let search_line = getline(l) + let trait_line .= ' '.substitute(search_line, '\(^\s\+\|\s\+$\)', '', 'g') + endwhile + endif + let use_expression = matchstr(trait_line, '^\s*use\s\+\zs.\{-}\ze;') + let use_parts = map(split(use_expression, '\s*,\s*'), 'substitute(v:val, "\\s+", " ", "g")') + let used_traits += map(use_parts, 'substitute(v:val, "\\s", "", "g")') + call cursor(lnum + 1, 1) + + if [lnum, col] == [0, 0] + let keep_searching = 0 + endif + endwhile + silent! bw! % + let [current_namespace, imports] = phpcomplete#GetCurrentNameSpace(a:file_lines[0:cfline]) " go back to original window exe phpcomplete_original_window.'wincmd w' @@ -2008,21 +2130,35 @@ function! phpcomplete#GetClassContentsStructure(file_path, file_lines, class_nam \ 'mtime': getftime(full_file_path), \ }) - if extends_class != '' - let [extends_class, namespace] = phpcomplete#ExpandClassName(extends_class, current_namespace, imports) - if namespace == '' - let namespace = '\' - endif - let classlocation = phpcomplete#GetClassLocation(extends_class, namespace) - if classlocation == "VIMPHP_BUILTINOBJECT" - let result += [phpcomplete#GenerateBuiltinClassStub(g:php_builtin_classes[tolower(extends_class)])] - elseif classlocation != '' && filereadable(classlocation) - let full_file_path = fnamemodify(classlocation, ':p') - let result += phpcomplete#GetClassContentsStructure(full_file_path, readfile(full_file_path), extends_class) - elseif tolower(current_namespace) == tolower(namespace) - " try to find the declaration in the same file. - let result += phpcomplete#GetClassContentsStructure(full_file_path, a:file_lines, extends_class) - endif + let all_extends = used_traits + if len(extended_classes) > 0 + call extend(all_extends, extended_classes) + endif + if len(implemented_interfaces) > 0 + call extend(all_extends, implemented_interfaces) + endif + if len(all_extends) > 0 + for class in all_extends + let [class, namespace] = phpcomplete#ExpandClassName(class, current_namespace, imports) + if namespace == '' + let namespace = '\' + endif + let classlocation = phpcomplete#GetClassLocation(class, namespace) + if classlocation == "VIMPHP_BUILTINOBJECT" + if has_key(g:php_builtin_classes, tolower(class)) + let result += [phpcomplete#GenerateBuiltinClassStub('class', g:php_builtin_classes[tolower(class)])] + endif + if has_key(g:php_builtin_interfaces, tolower(class)) + let result += [phpcomplete#GenerateBuiltinClassStub('interface', g:php_builtin_interfaces[tolower(class)])] + endif + elseif classlocation != '' && filereadable(classlocation) + let full_file_path = fnamemodify(classlocation, ':p') + let result += phpcomplete#GetClassContentsStructure(full_file_path, readfile(full_file_path), class) + elseif tolower(current_namespace) == tolower(namespace) && match(join(a:file_lines, "\n"), '\c\(class\|interface\|trait\)\_s\+'.class.'\(\>\|$\)') != -1 + " try to find the declaration in the same file. + let result += phpcomplete#GetClassContentsStructure(full_file_path, a:file_lines, class) + endif + endfor endif return result @@ -2039,43 +2175,53 @@ function! phpcomplete#GetClassContents(classlocation, class_name) " {{{ endfunction " }}} -function! phpcomplete#GenerateBuiltinClassStub(class_info) " {{{ - let re = 'class '.a:class_info['name']." {" - for [name, initializer] in items(a:class_info.constants) - let re .= "\n\tconst ".name." = ".initializer.";" - endfor - for [name, info] in items(a:class_info.properties) - let re .= "\n\t// @var $".name." ".info.type - let re .= "\n\tpublic $".name.";" - endfor - for [name, info] in items(a:class_info.static_properties) - let re .= "\n\t// @var ".name." ".info.type - let re .= "\n\tpublic static ".name." = ".info.initializer.";" - endfor - for [name, info] in items(a:class_info.methods) - if name =~ '^__' - continue - endif - let re .= "\n\t/**" - let re .= "\n\t * ".name - let re .= "\n\t *" - let re .= "\n\t * @return ".info.return_type - let re .= "\n\t */" - let re .= "\n\tpublic function ".name."(".info.signature."){" - let re .= "\n\t}" - endfor - for [name, info] in items(a:class_info.static_methods) - let re .= "\n\t/**" - let re .= "\n\t * ".name - let re .= "\n\t *" - let re .= "\n\t * @return ".info.return_type - let re .= "\n\t */" - let re .= "\n\tpublic static function ".name."(".info.signature."){" - let re .= "\n\t}" - endfor +function! phpcomplete#GenerateBuiltinClassStub(type, class_info) " {{{ + let re = a:type.' '.a:class_info['name']." {" + if has_key(a:class_info, 'constants') + for [name, initializer] in items(a:class_info.constants) + let re .= "\n\tconst ".name." = ".initializer.";" + endfor + endif + if has_key(a:class_info, 'properties') + for [name, info] in items(a:class_info.properties) + let re .= "\n\t// @var $".name." ".info.type + let re .= "\n\tpublic $".name.";" + endfor + endif + if has_key(a:class_info, 'static_properties') + for [name, info] in items(a:class_info.static_properties) + let re .= "\n\t// @var ".name." ".info.type + let re .= "\n\tpublic static ".name." = ".info.initializer.";" + endfor + endif + if has_key(a:class_info, 'methods') + for [name, info] in items(a:class_info.methods) + if name =~ '^__' + continue + endif + let re .= "\n\t/**" + let re .= "\n\t * ".name + let re .= "\n\t *" + let re .= "\n\t * @return ".info.return_type + let re .= "\n\t */" + let re .= "\n\tpublic function ".name."(".info.signature."){" + let re .= "\n\t}" + endfor + endif + if has_key(a:class_info, 'static_methods') + for [name, info] in items(a:class_info.static_methods) + let re .= "\n\t/**" + let re .= "\n\t * ".name + let re .= "\n\t *" + let re .= "\n\t * @return ".info.return_type + let re .= "\n\t */" + let re .= "\n\tpublic static function ".name."(".info.signature."){" + let re .= "\n\t}" + endfor + endif let re .= "\n}" - return { 'class': a:class_info['name'], + return { a:type : a:class_info['name'], \ 'content': re, \ 'namespace': '', \ 'imports': {}, @@ -2099,8 +2245,11 @@ function! phpcomplete#GetDocBlock(sccontent, search) " {{{ " start backward serch for the comment block while l != 0 let line = a:sccontent[l] - " if comment end found save line position and end search - if line =~? '^\s*\*/' + " if it's a one line docblock like comment and we can just return it right away + if line =~? '^\s*\/\*\*.\+\*\/\s*$' + return substitute(line, '\v^\s*(\/\*\*\s*)|(\s*\*\/)\s*$', '', 'g') + "... or if comment end found save line position and end search + elseif line =~? '^\s*\*/' let comment_end = l break " ... or the line doesn't blank (only whitespace or nothing) end search @@ -2122,6 +2271,7 @@ function! phpcomplete#GetDocBlock(sccontent, search) " {{{ endif let l -= 1 endwhile + " no docblock comment start found if comment_start == -1 return '' @@ -2270,19 +2420,48 @@ endfunction! " }}} function! phpcomplete#GetCurrentNameSpace(file_lines) " {{{ + let original_window = winnr() + + silent! below 1new + silent! 0put =a:file_lines + normal! G + + " clear out classes, functions and other blocks + while 1 + let block_start_pos = searchpos('\c\(class\|trait\|function\|interface\)\s\+\_.\{-}\zs{', 'Web') + if block_start_pos == [0, 0] + break + endif + let block_end_pos = searchpairpos('{', '', '}\|\%$', 'W', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"') + + if block_end_pos != [0, 0] + " end of the block found, just delete it + silent! exec block_start_pos[0].','.block_end_pos[0].'d _' + else + " block pair not found, use block start as beginning and the end + " of the buffer instead + silent! exec block_start_pos[0].',$d _' + endif + endwhile + normal! G + + " grab the remains + let file_lines = reverse(getline(1, line('.') - 1)) + + silent! bw! % + exe original_window.'wincmd w' + let namespace_name_pattern = '[a-zA-Z_\x7f-\xff\\][a-zA-Z_0-9\x7f-\xff\\]*' - let file_lines = reverse(copy(a:file_lines)) let i = 0 let file_length = len(file_lines) let imports = {} - let current_namespace = '\' while i < file_length let line = file_lines[i] - if line =~? '^\s*namespace\s*'.namespace_name_pattern - let current_namespace = matchstr(line, '^\s*namespace\s*\zs'.namespace_name_pattern.'\ze') + if line =~? '^\(<?php\)\?\s*namespace\s*'.namespace_name_pattern + let current_namespace = matchstr(line, '\c^\(<?php\)\?\s*namespace\s*\zs'.namespace_name_pattern.'\ze') break endif @@ -2303,11 +2482,11 @@ function! phpcomplete#GetCurrentNameSpace(file_lines) " {{{ let use_line .= ' '.substitute(search_line, '\(^\s\+\|\s\+$\)', '', 'g') endwhile endif - let use_expression = matchstr(use_line, '^\s*use\s\+\zs.\{-}\ze;') + let use_expression = matchstr(use_line, '^\c\s*use\s\+\zs.\{-}\ze;') let use_parts = map(split(use_expression, '\s*,\s*'), 'substitute(v:val, "\\s+", " ", "g")') for part in use_parts if part =~? '\s\+as\s\+' - let [object, name] = split(part, '\s\+as\s\+') + let [object, name] = split(part, '\s\+as\s\+\c') let object = substitute(object, '^\\', '', '') let name = substitute(name, '^\\', '', '') else @@ -2343,7 +2522,7 @@ function! phpcomplete#GetCurrentNameSpace(file_lines) " {{{ break endif " if the name matches with the extracted classname and namespace - if (tag.kind == 'c' || tag.kind == 'i') && tag.name == classname + if (tag.kind == 'c' || tag.kind == 'i' || tag.kind == 't') && tag.name == classname if has_key(tag, 'namespace') let patched_ctags_detected = 1 if tag.namespace == namespace_for_classes @@ -2386,7 +2565,7 @@ function! phpcomplete#GetCurrentNameSpace(file_lines) " {{{ let tags = phpcomplete#GetTaglist('^'.import['name'].'$') for tag in tags " search for the first matchin namespace, class, interface with no namespace - if !has_key(tag, 'namespace') && (tag.kind == 'n' || tag.kind == 'c' || tag.kind == 'i') + if !has_key(tag, 'namespace') && (tag.kind == 'n' || tag.kind == 'c' || tag.kind == 'i' || tag.kind == 't') call extend(import, tag) let import['builtin'] = 0 break @@ -2445,7 +2624,7 @@ endfunction function! phpcomplete#ExpandClassName(classname, current_namespace, imports) " {{{ " if there's an imported class, just use that class's information - if has_key(a:imports, a:classname) && (a:imports[a:classname].kind == 'c' || a:imports[a:classname].kind == 'i') + if has_key(a:imports, a:classname) && (a:imports[a:classname].kind == 'c' || a:imports[a:classname].kind == 'i' || a:imports[a:classname].kind == 't') let namespace = has_key(a:imports[a:classname], 'namespace') ? a:imports[a:classname].namespace : '' return [a:imports[a:classname].name, namespace] endif diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 5d1ce7896d..c7cb14ded7 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -37,16 +37,26 @@ if executable('pbcopy') let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] let s:cache_enabled = 0 -elseif executable('xclip') - let s:copy['+'] = 'xclip -quiet -i -selection clipboard' - let s:paste['+'] = 'xclip -o -selection clipboard' - let s:copy['*'] = 'xclip -quiet -i -selection primary' - let s:paste['*'] = 'xclip -o -selection primary' -elseif executable('xsel') +elseif exists('$DISPLAY') && executable('xsel') let s:copy['+'] = 'xsel --nodetach -i -b' let s:paste['+'] = 'xsel -o -b' let s:copy['*'] = 'xsel --nodetach -i -p' let s:paste['*'] = 'xsel -o -p' +elseif exists('$DISPLAY') && executable('xclip') + let s:copy['+'] = 'xclip -quiet -i -selection clipboard' + let s:paste['+'] = 'xclip -o -selection clipboard' + let s:copy['*'] = 'xclip -quiet -i -selection primary' + let s:paste['*'] = 'xclip -o -selection primary' +elseif executable('lemonade') + let s:copy['+'] = 'lemonade copy' + let s:paste['+'] = 'lemonade paste' + let s:copy['*'] = 'lemonade copy' + let s:paste['*'] = 'lemonade paste' +elseif executable('doitclient') + let s:copy['+'] = 'doitclient wclip' + let s:paste['+'] = 'doitclient wclip -r' + let s:copy['*'] = s:copy['+'] + let s:paste['*'] = s:paste['+'] else echom 'clipboard: No clipboard tool available. See :help nvim-clipboard' finish @@ -83,6 +93,7 @@ function! s:clipboard.set(lines, regtype, reg) end let selection.data = [a:lines, a:regtype] let argv = split(s:copy[a:reg], " ") + let selection.detach = s:cache_enabled let jobid = jobstart(argv, selection) if jobid <= 0 echohl WarningMsg diff --git a/runtime/autoload/provider/python.vim b/runtime/autoload/provider/python.vim index b769895357..cb9d5c5296 100644 --- a/runtime/autoload/provider/python.vim +++ b/runtime/autoload/provider/python.vim @@ -24,12 +24,10 @@ if s:prog == '' finish endif -let s:plugin_path = expand('<sfile>:p:h').'/script_host.py' - " The Python provider plugin will run in a separate instance of the Python " host. call remote#host#RegisterClone('legacy-python-provider', 'python') -call remote#host#RegisterPlugin('legacy-python-provider', s:plugin_path, []) +call remote#host#RegisterPlugin('legacy-python-provider', 'script_host.py', []) function! provider#python#Call(method, args) if s:err != '' diff --git a/runtime/autoload/provider/python3.vim b/runtime/autoload/provider/python3.vim index 2952f76b40..f4a751e7a2 100644 --- a/runtime/autoload/provider/python3.vim +++ b/runtime/autoload/provider/python3.vim @@ -24,12 +24,10 @@ if s:prog == '' finish endif -let s:plugin_path = expand('<sfile>:p:h').'/script_host.py' - " The Python3 provider plugin will run in a separate instance of the Python3 " host. call remote#host#RegisterClone('legacy-python3-provider', 'python3') -call remote#host#RegisterPlugin('legacy-python3-provider', s:plugin_path, []) +call remote#host#RegisterPlugin('legacy-python3-provider', 'script_host.py', []) function! provider#python3#Call(method, args) if s:err != '' diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 022ef19914..05815a4896 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -5,6 +5,32 @@ endif let s:loaded_pythonx_provider = 1 +function! provider#pythonx#Require(host) abort + let ver = (a:host.orig_name ==# 'python') ? 2 : 3 + + " Python host arguments + let args = ['-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()'] + + " Collect registered Python plugins into args + let python_plugins = remote#host#PluginsForHost(a:host.name) + for plugin in python_plugins + call add(args, plugin.path) + endfor + + try + let channel_id = rpcstart((ver == '2' ? + \ provider#python#Prog() : provider#python3#Prog()), args) + if rpcrequest(channel_id, 'poll') == 'ok' + return channel_id + endif + catch + echomsg v:throwpoint + echomsg v:exception + endtry + throw remote#host#LoadErrorForHost(a:host.orig_name, + \ '$NVIM_PYTHON_LOG_FILE') +endfunction + function! provider#pythonx#Detect(major_ver) abort let host_var = (a:major_ver == 2) ? \ 'g:python_host_prog' : 'g:python3_host_prog' diff --git a/runtime/autoload/provider/ruby.vim b/runtime/autoload/provider/ruby.vim new file mode 100644 index 0000000000..aad8c09d28 --- /dev/null +++ b/runtime/autoload/provider/ruby.vim @@ -0,0 +1,34 @@ +" The Ruby provider helper +if exists('s:loaded_ruby_provider') + finish +endif + +let s:loaded_ruby_provider = 1 + +function! provider#ruby#Require(host) abort + " Collect registered Ruby plugins into args + let args = [] + let ruby_plugins = remote#host#PluginsForHost(a:host.name) + + for plugin in ruby_plugins + call add(args, plugin.path) + endfor + + try + let channel_id = rpcstart(provider#ruby#Prog(), args) + + if rpcrequest(channel_id, 'poll') == 'ok' + return channel_id + endif + catch + echomsg v:throwpoint + echomsg v:exception + endtry + + throw remote#host#LoadErrorForHost(a:host.orig_name, + \ '$NVIM_RUBY_LOG_FILE') +endfunction + +function! provider#ruby#Prog() abort + return 'neovim-ruby-host' +endfunction diff --git a/runtime/autoload/provider/script_host.py b/runtime/autoload/provider/script_host.py deleted file mode 100644 index 96d70e0330..0000000000 --- a/runtime/autoload/provider/script_host.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Legacy python/python3-vim emulation.""" -import imp -import logging -import os -import sys - -import neovim - -__all__ = ('ScriptHost',) - - -logger = logging.getLogger(__name__) -debug, info, warn = (logger.debug, logger.info, logger.warn,) - -IS_PYTHON3 = sys.version_info >= (3, 0) - -if IS_PYTHON3: - basestring = str - - if sys.version_info >= (3, 4): - from importlib.machinery import PathFinder - - -@neovim.plugin -class ScriptHost(object): - - """Provides an environment for running python plugins created for Vim.""" - - def __init__(self, nvim): - """Initialize the legacy python-vim environment.""" - self.setup(nvim) - # context where all code will run - self.module = imp.new_module('__main__') - nvim.script_context = self.module - # it seems some plugins assume 'sys' is already imported, so do it now - exec('import sys', self.module.__dict__) - self.legacy_vim = nvim.with_hook(LegacyEvalHook()) - sys.modules['vim'] = self.legacy_vim - - def setup(self, nvim): - """Setup import hooks and global streams. - - This will add import hooks for importing modules from runtime - directories and patch the sys module so 'print' calls will be - forwarded to Nvim. - """ - self.nvim = nvim - info('install import hook/path') - self.hook = path_hook(nvim) - sys.path_hooks.append(self.hook) - nvim.VIM_SPECIAL_PATH = '_vim_path_' - sys.path.append(nvim.VIM_SPECIAL_PATH) - info('redirect sys.stdout and sys.stderr') - self.saved_stdout = sys.stdout - self.saved_stderr = sys.stderr - sys.stdout = RedirectStream(lambda data: nvim.out_write(data)) - sys.stderr = RedirectStream(lambda data: nvim.err_write(data)) - - def teardown(self): - """Restore state modified from the `setup` call.""" - for plugin in self.installed_plugins: - if hasattr(plugin, 'on_teardown'): - plugin.teardown() - nvim = self.nvim - info('uninstall import hook/path') - sys.path.remove(nvim.VIM_SPECIAL_PATH) - sys.path_hooks.remove(self.hook) - info('restore sys.stdout and sys.stderr') - sys.stdout = self.saved_stdout - sys.stderr = self.saved_stderr - - @neovim.rpc_export('python_execute', sync=True) - def python_execute(self, script, range_start, range_stop): - """Handle the `python` ex command.""" - self._set_current_range(range_start, range_stop) - exec(script, self.module.__dict__) - - @neovim.rpc_export('python_execute_file', sync=True) - def python_execute_file(self, file_path, range_start, range_stop): - """Handle the `pyfile` ex command.""" - self._set_current_range(range_start, range_stop) - with open(file_path) as f: - script = compile(f.read(), file_path, 'exec') - exec(script, self.module.__dict__) - - @neovim.rpc_export('python_do_range', sync=True) - def python_do_range(self, start, stop, code): - """Handle the `pydo` ex command.""" - self._set_current_range(start, stop) - nvim = self.nvim - start -= 1 - stop -= 1 - fname = '_vim_pydo' - - # define the function - function_def = 'def %s(line, linenr):\n %s' % (fname, code,) - exec(function_def, self.module.__dict__) - # get the function - function = self.module.__dict__[fname] - while start <= stop: - # Process batches of 5000 to avoid the overhead of making multiple - # API calls for every line. Assuming an average line length of 100 - # bytes, approximately 488 kilobytes will be transferred per batch, - # which can be done very quickly in a single API call. - sstart = start - sstop = min(start + 5000, stop) - lines = nvim.current.buffer.get_line_slice(sstart, sstop, True, - True) - - exception = None - newlines = [] - linenr = sstart + 1 - for i, line in enumerate(lines): - result = function(line, linenr) - if result is None: - # Update earlier lines, and skip to the next - if newlines: - end = sstart + len(newlines) - 1 - nvim.current.buffer.set_line_slice(sstart, end, - True, True, - newlines) - sstart += len(newlines) + 1 - newlines = [] - pass - elif isinstance(result, basestring): - newlines.append(result) - else: - exception = TypeError('pydo should return a string ' + - 'or None, found %s instead' - % result.__class__.__name__) - break - linenr += 1 - - start = sstop + 1 - if newlines: - end = sstart + len(newlines) - 1 - nvim.current.buffer.set_line_slice(sstart, end, True, True, - newlines) - if exception: - raise exception - # delete the function - del self.module.__dict__[fname] - - @neovim.rpc_export('python_eval', sync=True) - def python_eval(self, expr): - """Handle the `pyeval` vim function.""" - return eval(expr, self.module.__dict__) - - def _set_current_range(self, start, stop): - current = self.legacy_vim.current - current.range = current.buffer.range(start, stop) - - -class RedirectStream(object): - def __init__(self, redirect_handler): - self.redirect_handler = redirect_handler - - def write(self, data): - self.redirect_handler(data) - - def writelines(self, seq): - self.redirect_handler('\n'.join(seq)) - - def flush(self): - pass - - -class LegacyEvalHook(neovim.SessionHook): - - """Injects legacy `vim.eval` behavior to a Nvim instance.""" - - def __init__(self): - super(LegacyEvalHook, self).__init__(from_nvim=self._string_eval) - - def _string_eval(self, obj, session, method, kind): - if method == 'vim_eval': - if IS_PYTHON3: - if isinstance(obj, (int, float)): - return str(obj) - elif isinstance(obj, (int, long, float)): - return str(obj) - return obj - - -# This was copied/adapted from nvim-python help -def path_hook(nvim): - def _get_paths(): - return discover_runtime_directories(nvim) - - def _find_module(fullname, oldtail, path): - idx = oldtail.find('.') - if idx > 0: - name = oldtail[:idx] - tail = oldtail[idx+1:] - fmr = imp.find_module(name, path) - module = imp.find_module(fullname[:-len(oldtail)] + name, *fmr) - return _find_module(fullname, tail, module.__path__) - else: - return imp.find_module(fullname, path) - - class VimModuleLoader(object): - def __init__(self, module): - self.module = module - - def load_module(self, fullname, path=None): - # Check sys.modules, required for reload (see PEP302). - if fullname in sys.modules: - return sys.modules[fullname] - return imp.load_module(fullname, *self.module) - - class VimPathFinder(object): - @staticmethod - def find_module(fullname, path=None): - "Method for Python 2.7 and 3.3." - try: - return VimModuleLoader( - _find_module(fullname, fullname, path or _get_paths())) - except ImportError: - return None - - @staticmethod - def find_spec(fullname, path=None, target=None): - "Method for Python 3.4+." - return PathFinder.find_spec(fullname, path or _get_paths(), target) - - def hook(path): - if path == nvim.VIM_SPECIAL_PATH: - return VimPathFinder - else: - raise ImportError - - return hook - - -def discover_runtime_directories(nvim): - rv = [] - for path in nvim.list_runtime_paths(): - if not os.path.exists(path): - continue - path1 = os.path.join(path, 'pythonx') - if IS_PYTHON3: - path2 = os.path.join(path, 'python3') - else: - path2 = os.path.join(path, 'python2') - if os.path.exists(path1): - rv.append(path1) - if os.path.exists(path2): - rv.append(path2) - return rv diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim index b02200be7f..f0f3aaddb3 100644 --- a/runtime/autoload/python3complete.vim +++ b/runtime/autoload/python3complete.vim @@ -1,7 +1,7 @@ "python3complete.vim - Omni Completion for python " Maintainer: Aaron Griffin <aaronmgriffin@gmail.com> " Version: 0.9 -" Last Updated: 18 Jun 2009 +" Last Updated: 18 Jun 2009 (small fix 2015 Sep 14 from Debian) " " Roland Puntaier: this file contains adaptations for python3 and is parallel to pythoncomplete.vim " @@ -359,6 +359,7 @@ class PyParser: def __init__(self): self.top = Scope('global',0) self.scope = self.top + self.parserline = 0 def _parsedotname(self,pre=None): #returns (dottedname, nexttoken) diff --git a/runtime/autoload/pythoncomplete.vim b/runtime/autoload/pythoncomplete.vim index 57add71cbd..ecc36646d9 100644 --- a/runtime/autoload/pythoncomplete.vim +++ b/runtime/autoload/pythoncomplete.vim @@ -377,6 +377,7 @@ class PyParser: def __init__(self): self.top = Scope('global',0) self.scope = self.top + self.parserline = 0 def _parsedotname(self,pre=None): #returns (dottedname, nexttoken) diff --git a/runtime/autoload/remote/define.vim b/runtime/autoload/remote/define.vim index dd2482998d..b04a5d2280 100644 --- a/runtime/autoload/remote/define.vim +++ b/runtime/autoload/remote/define.vim @@ -1,7 +1,7 @@ function! remote#define#CommandOnHost(host, method, sync, name, opts) let prefix = '' - if has_key(a:opts, 'range') + if has_key(a:opts, 'range') if a:opts.range == '' || a:opts.range == '%' " -range or -range=%, pass the line range in a list let prefix = '<line1>,<line2>' @@ -30,7 +30,7 @@ function! remote#define#CommandOnHost(host, method, sync, name, opts) exe s:GetCommandPrefix(a:name, a:opts) \ .' call remote#define#CommandBootstrap("'.a:host.'"' \ . ', "'.a:method.'"' - \ . ', "'.a:sync.'"' + \ . ', '.string(a:sync) \ . ', "'.a:name.'"' \ . ', '.string(a:opts).'' \ . ', "'.join(forward_args, '').'"' @@ -94,7 +94,7 @@ function! remote#define#AutocmdOnHost(host, method, sync, name, opts) let bootstrap_def = s:GetAutocmdPrefix(a:name, a:opts) \ .' call remote#define#AutocmdBootstrap("'.a:host.'"' \ . ', "'.a:method.'"' - \ . ', "'.a:sync.'"' + \ . ', '.string(a:sync) \ . ', "'.a:name.'"' \ . ', '.string(a:opts).'' \ . ', "'.escape(forward, '"').'"' @@ -133,7 +133,7 @@ function! remote#define#FunctionOnHost(host, method, sync, name, opts) exe 'autocmd! '.group.' FuncUndefined '.a:name \ .' call remote#define#FunctionBootstrap("'.a:host.'"' \ . ', "'.a:method.'"' - \ . ', "'.a:sync.'"' + \ . ', '.string(a:sync) \ . ', "'.a:name.'"' \ . ', '.string(a:opts) \ . ', "'.group.'"' @@ -157,6 +157,9 @@ endfunction function! remote#define#FunctionOnChannel(channel, method, sync, name, opts) let rpcargs = [a:channel, '"'.a:method.'"', 'a:000'] + if has_key(a:opts, 'range') + call add(rpcargs, '[a:firstline, a:lastline]') + endif call s:AddEval(rpcargs, a:opts) let function_def = s:GetFunctionPrefix(a:name, a:opts) @@ -187,7 +190,7 @@ let s:next_gid = 1 function! s:GetNextAutocmdGroup() let gid = s:next_gid let s:next_gid += 1 - + let group_name = 'RPC_DEFINE_AUTOCMD_GROUP_'.gid " Ensure the group is defined exe 'augroup '.group_name.' | augroup END' @@ -218,7 +221,11 @@ endfunction function! s:GetFunctionPrefix(name, opts) - return "function! ".a:name."(...)\n" + let res = "function! ".a:name."(...)" + if has_key(a:opts, 'range') + let res = res." range" + endif + return res."\n" endfunction diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim index d04dea180c..8faeaed2ea 100644 --- a/runtime/autoload/remote/host.vim +++ b/runtime/autoload/remote/host.vim @@ -2,10 +2,11 @@ let s:hosts = {} let s:plugin_patterns = {} let s:remote_plugins_manifest = fnamemodify(expand($MYVIMRC, 1), ':h') \.'/.'.fnamemodify($MYVIMRC, ':t').'-rplugin~' +let s:plugins_for_host = {} " Register a host by associating it with a factory(funcref) -function! remote#host#Register(name, pattern, factory) +function! remote#host#Register(name, pattern, factory) abort let s:hosts[a:name] = {'factory': a:factory, 'channel': 0, 'initialized': 0} let s:plugin_patterns[a:name] = a:pattern if type(a:factory) == type(1) && a:factory @@ -19,7 +20,7 @@ endfunction " as `source`, but it will run as a different process. This can be used by " plugins that should run isolated from other plugins created for the same host " type -function! remote#host#RegisterClone(name, orig_name) +function! remote#host#RegisterClone(name, orig_name) abort if !has_key(s:hosts, a:orig_name) throw 'No host named "'.a:orig_name.'" is registered' endif @@ -34,7 +35,10 @@ endfunction " Get a host channel, bootstrapping it if necessary -function! remote#host#Require(name) +function! remote#host#Require(name) abort + if empty(s:plugins_for_host) + call remote#host#LoadRemotePlugins() + endif if !has_key(s:hosts, a:name) throw 'No host named "'.a:name.'" is registered' endif @@ -51,7 +55,7 @@ function! remote#host#Require(name) endfunction -function! remote#host#IsRunning(name) +function! remote#host#IsRunning(name) abort if !has_key(s:hosts, a:name) throw 'No host named "'.a:name.'" is registered' endif @@ -72,7 +76,7 @@ endfunction " " The third item in a declaration is a boolean: non zero means the command, " autocommand or function will be executed synchronously with rpcrequest. -function! remote#host#RegisterPlugin(host, path, specs) +function! remote#host#RegisterPlugin(host, path, specs) abort let plugins = remote#host#PluginsForHost(a:host) for plugin in plugins @@ -116,14 +120,21 @@ function! remote#host#RegisterPlugin(host, path, specs) endfunction -function! remote#host#LoadRemotePlugins() +function! remote#host#LoadRemotePlugins() abort if filereadable(s:remote_plugins_manifest) exe 'source '.s:remote_plugins_manifest endif endfunction -function! s:RegistrationCommands(host) +function! remote#host#LoadRemotePluginsEvent(event, pattern) abort + autocmd! nvim-rplugin + call remote#host#LoadRemotePlugins() + execute 'silent doautocmd <nomodeline>' a:event a:pattern +endfunction + + +function! s:RegistrationCommands(host) abort " Register a temporary host clone for discovering specs let host_id = a:host.'-registration-clone' call remote#host#RegisterClone(host_id, a:host) @@ -138,7 +149,9 @@ function! s:RegistrationCommands(host) endfor let channel = remote#host#Require(host_id) let lines = [] + let registered = [] for path in paths + unlet! specs let specs = rpcrequest(channel, 'specs', path) if type(specs) != type([]) " host didn't return a spec list, indicates a failure while loading a @@ -151,9 +164,10 @@ function! s:RegistrationCommands(host) call add(lines, " \\ ".string(spec).",") endfor call add(lines, " \\ ])") + call add(registered, path) endfor echomsg printf("remote/host: %s host registered plugins %s", - \ a:host, string(map(copy(paths), "fnamemodify(v:val, ':t')"))) + \ a:host, string(map(registered, "fnamemodify(v:val, ':t')"))) " Delete the temporary host clone call rpcstop(s:hosts[host_id].channel) @@ -163,7 +177,7 @@ function! s:RegistrationCommands(host) endfunction -function! s:UpdateRemotePlugins() +function! remote#host#UpdateRemotePlugins() abort let commands = [] let hosts = keys(s:hosts) for host in hosts @@ -180,14 +194,12 @@ function! s:UpdateRemotePlugins() endif endfor call writefile(commands, s:remote_plugins_manifest) + echomsg printf('remote/host: generated the manifest file in "%s"', + \ s:remote_plugins_manifest) endfunction -command! UpdateRemotePlugins call s:UpdateRemotePlugins() - - -let s:plugins_for_host = {} -function! remote#host#PluginsForHost(host) +function! remote#host#PluginsForHost(host) abort if !has_key(s:plugins_for_host, a:host) let s:plugins_for_host[a:host] = [] end @@ -195,40 +207,25 @@ function! remote#host#PluginsForHost(host) endfunction -" Registration of standard hosts - -" Python/Python3 {{{ -function! s:RequirePythonHost(host) - let ver = (a:host.orig_name ==# 'python') ? 2 : 3 - - " Python host arguments - let args = ['-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()'] - - " Collect registered Python plugins into args - let python_plugins = remote#host#PluginsForHost(a:host.name) - for plugin in python_plugins - call add(args, plugin.path) - endfor - - try - let channel_id = rpcstart((ver == '2' ? - \ provider#python#Prog() : provider#python3#Prog()), args) - if rpcrequest(channel_id, 'poll') == 'ok' - return channel_id - endif - catch - echomsg v:throwpoint - echomsg v:exception - endtry - throw 'Failed to load '. a:host.orig_name . ' host. '. +function! remote#host#LoadErrorForHost(host, log) abort + return 'Failed to load '. a:host . ' host. '. \ 'You can try to see what happened '. \ 'by starting Neovim with the environment variable '. - \ '$NVIM_PYTHON_LOG_FILE set to a file and opening '. - \ 'the generated log file. Also, the host stderr will be available '. + \ a:log . ' set to a file and opening the generated '. + \ 'log file. Also, the host stderr will be available '. \ 'in Neovim log, so it may contain useful information. '. \ 'See also ~/.nvimlog.' endfunction -call remote#host#Register('python', '*.py', function('s:RequirePythonHost')) -call remote#host#Register('python3', '*.py', function('s:RequirePythonHost')) -" }}} + +" Registration of standard hosts + +" Python/Python3 +call remote#host#Register('python', '*', + \ function('provider#pythonx#Require')) +call remote#host#Register('python3', '*', + \ function('provider#pythonx#Require')) + +" Ruby +call remote#host#Register('ruby', '*.rb', + \ function('provider#ruby#Require')) diff --git a/runtime/autoload/spellfile.vim b/runtime/autoload/spellfile.vim index c32dd5df9b..a5ffa514ea 100644 --- a/runtime/autoload/spellfile.vim +++ b/runtime/autoload/spellfile.vim @@ -1,6 +1,4 @@ " Vim script to download a missing spell file -" Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2012 Jan 08 if !exists('g:spellfile_URL') " Prefer using http:// when netrw should be able to use it, since @@ -43,22 +41,23 @@ function! spellfile#LoadFile(lang) if len(dirlist) == 0 let dir_to_create = spellfile#WritableSpellDir() if &verbose || dir_to_create != '' - echomsg 'spellfile#LoadFile(): There is no writable spell directory.' + echomsg 'spellfile#LoadFile(): No (writable) spell directory found.' endif if dir_to_create != '' - if confirm("Shall I create " . dir_to_create, "&Yes\n&No", 2) == 1 - " After creating the directory it should show up in the list. - call mkdir(dir_to_create, "p") - let [dirlist, dirchoices] = spellfile#GetDirChoices() - endif + call mkdir(dir_to_create, "p") + " Now it should show up in the list. + let [dirlist, dirchoices] = spellfile#GetDirChoices() endif if len(dirlist) == 0 + echomsg 'Failed to create: '.dir_to_create return + else + echomsg 'Created '.dir_to_create endif endif - let msg = 'Cannot find spell file for "' . a:lang . '" in ' . &enc - let msg .= "\nDo you want me to try downloading it?" + let msg = 'No spell file for "' . a:lang . '" in ' . &enc + let msg .= "\nDownload it?" if confirm(msg, "&Yes\n&No", 2) == 1 let enc = &encoding if enc == 'iso-8859-15' @@ -78,78 +77,77 @@ function! spellfile#LoadFile(lang) " Careful: Nread() may have opened a new window for the error message, " we need to go back to our own buffer and window. if newbufnr != winbufnr(0) - let winnr = bufwinnr(newbufnr) - if winnr == -1 - " Our buffer has vanished!? Open a new window. - echomsg "download buffer disappeared, opening a new one" - new - setlocal bin fenc= - else - exe winnr . "wincmd w" - endif + let winnr = bufwinnr(newbufnr) + if winnr == -1 + " Our buffer has vanished!? Open a new window. + echomsg "download buffer disappeared, opening a new one" + new + setlocal bin fenc= + else + exe winnr . "wincmd w" + endif endif if newbufnr == winbufnr(0) - " We are back the old buffer, remove any (half-finished) download. - g/^/d + " We are back the old buffer, remove any (half-finished) download. + g/^/d_ else - let newbufnr = winbufnr(0) + let newbufnr = winbufnr(0) endif let fname = a:lang . '.ascii.spl' echo 'Could not find it, trying ' . fname . '...' call spellfile#Nread(fname) if getline(2) !~ 'VIMspell' - echo 'Sorry, downloading failed' - exe newbufnr . "bwipe!" - return + echo 'Download failed' + exe newbufnr . "bwipe!" + return endif endif " Delete the empty first line and mark the file unmodified. - 1d + 1d_ set nomod - let msg = "In which directory do you want to write the file:" - for i in range(len(dirlist)) - let msg .= "\n" . (i + 1) . '. ' . dirlist[i] - endfor - let dirchoice = confirm(msg, dirchoices) - 2 + if len(dirlist) == 1 + let dirchoice = 0 + else + let msg = "In which directory do you want to write the file:" + for i in range(len(dirlist)) + let msg .= "\n" . (i + 1) . '. ' . dirlist[i] + endfor + let dirchoice = confirm(msg, dirchoices) - 2 + endif if dirchoice >= 0 if exists('*fnameescape') - let dirname = fnameescape(dirlist[dirchoice]) + let dirname = fnameescape(dirlist[dirchoice]) else - let dirname = escape(dirlist[dirchoice], ' ') + let dirname = escape(dirlist[dirchoice], ' ') endif setlocal fenc= exe "write " . dirname . '/' . fname - " Also download the .sug file, if the user wants to. - let msg = "Do you want me to try getting the .sug file?\n" - let msg .= "This will improve making suggestions for spelling mistakes,\n" - let msg .= "but it uses quite a bit of memory." - if confirm(msg, "&No\n&Yes") == 2 - g/^/d - let fname = substitute(fname, '\.spl$', '.sug', '') - echo 'Downloading ' . fname . '...' - call spellfile#Nread(fname) - if getline(2) =~ 'VIMsug' - 1d - exe "write " . dirname . '/' . fname - set nomod - else - echo 'Sorry, downloading failed' - " Go back to our own buffer/window, Nread() may have taken us to - " another window. - if newbufnr != winbufnr(0) - let winnr = bufwinnr(newbufnr) - if winnr != -1 - exe winnr . "wincmd w" - endif - endif - if newbufnr == winbufnr(0) - set nomod - endif - endif + " Also download the .sug file. + g/^/d_ + let fname = substitute(fname, '\.spl$', '.sug', '') + echo 'Downloading ' . fname . '...' + call spellfile#Nread(fname) + if getline(2) =~ 'VIMsug' + 1d_ + exe "write " . dirname . '/' . fname + set nomod + else + echo 'Download failed' + " Go back to our own buffer/window, Nread() may have taken us to + " another window. + if newbufnr != winbufnr(0) + let winnr = bufwinnr(newbufnr) + if winnr != -1 + exe winnr . "wincmd w" + endif + endif + if newbufnr == winbufnr(0) + set nomod + endif endif endif diff --git a/runtime/autoload/tohtml.vim b/runtime/autoload/tohtml.vim index 5cb23a6146..d972ad63fe 100644 --- a/runtime/autoload/tohtml.vim +++ b/runtime/autoload/tohtml.vim @@ -1,6 +1,6 @@ " Vim autoload file for the tohtml plugin. " Maintainer: Ben Fritz <fritzophrenic@gmail.com> -" Last Change: 2013 Jun 19 +" Last Change: 2013 Sep 03 " " Additional contributors: " @@ -302,7 +302,7 @@ func! tohtml#Convert2HTML(line1, line2) "{{{ else "{{{ let win_list = [] let buf_list = [] - windo | if &diff | call add(win_list, winbufnr(0)) | endif + windo if &diff | call add(win_list, winbufnr(0)) | endif let s:settings.whole_filler = 1 let g:html_diff_win_num = 0 for window in win_list diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim index 4d5a10a97c..43d8a87886 100644 --- a/runtime/autoload/tutor.vim +++ b/runtime/autoload/tutor.vim @@ -251,13 +251,14 @@ endfunction " Tutor Cmd: {{{1 function! s:Locale() - let l:lang = "" if exists('v:lang') && v:lang =~ '\a\a' let l:lang = v:lang elseif $LC_ALL =~ '\a\a' let l:lang = $LC_ALL elseif $LANG =~ '\a\a' let l:lang = $LANG + else + let l:lang = 'en_US' endif return split(l:lang, '_') endfunction diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt new file mode 100644 index 0000000000..ca79465e0d --- /dev/null +++ b/runtime/doc/api.txt @@ -0,0 +1,99 @@ +*api.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + +The C API of Nvim *nvim-api* + +1. Introduction |nvim-api-intro| +2. API Types |nvim-api-types| +3. API metadata |nvim-api-metadata| +4. Buffer highlighting |nvim-api-highlights| + +============================================================================== +1. Introduction *nvim-api-intro* + +Nvim defines a C API as the primary way for external code to interact with +the NVim core. In the present version of Nvim the API is primarily used by +external processes to interact with Nvim using the msgpack-rpc protocol, see +|msgpack-rpc|. The API will also be used from vimscript to access new Nvim core +features, but this is not implemented yet. Later on, Nvim might be embeddable +in C applications as libnvim, and the application will then control the +embedded instance by calling the C API directly. + +============================================================================== +2. API Types *nvim-api-types* + +Nvim's C API uses custom types for all functions. Some are just typedefs +around C99 standard types, and some are Nvim defined data structures. + +Boolean -> bool +Integer (signed 64-bit integer) -> int64_t +Float (IEEE 754 double precision) -> double +String -> {char* data, size_t size} struct + +Additionally, the following data structures are defined: + +Array +Dictionary +Object + +The following handle types are defined as integer typedefs, but are +discriminated as separate types in an Object: + +Buffer -> enum value kObjectTypeBuffer +Window -> enum value kObjectTypeWindow +Tabpage -> enum value kObjectTypeTabpage + +============================================================================== +3. API metadata *nvim-api-metadata* + +Nvim exposes metadata about the API as a Dictionary with the following keys: + +functions calling signature of the API functions +types The custom handle types defined by Nvim +error_types The possible kinds of errors an API function can exit with. + +This metadata is mostly useful for external programs accessing the api over +msgpack-api, see |msgpack-rpc-api|. + +============================================================================== +4. Buffer highlighting *nvim-api-highlights* + +Nvim allows plugins to add position-based highlights to buffers. This is +similar to |matchaddpos()| but with some key differences. The added highlights +are associated with a buffer and adapts to line insertions and deletions, +similar to signs. It is also possible to manage a set of highlights as a group +and delete or replace all at once. + +The intended use case are linter or semantic highlighter plugins that monitor +a buffer for changes, and in the background compute highlights to the buffer. +Another use case are plugins that show output in an append-only buffer, and +want to add highlights to the outputs. Highlight data cannot be preserved +on writing and loading a buffer to file, nor in undo/redo cycles. + +Highlights are registered using the |buffer_add_highlight| function, see the +generated API documentation for details. If an external highlighter plugin is +adding a large number of highlights in a batch, performance can be improved by +calling |buffer_add_highlight| as an asynchronous notification, after first +(synchronously) reqesting a source id. Here is an example using wrapper +functions in the python client: +> + src = vim.new_highlight_source() + + buf = vim.current.buffer + for i in range(5): + buf.add_highlight("String",i,0,-1,src_id=src) + + # some time later + + buf.clear_highlight(src) +< +If the highlights don't need to be deleted or updated, just pass -1 as +src_id (this is the default in python). |buffer_clear_highlight| can be used +to clear highligts from a specific source, in a specific line range or the +entire buffer by passing in the line range 0, -1 (the later is the default +in python as used above). + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 38d53249d1..25ae94f784 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1,4 +1,4 @@ -*autocmd.txt* For Vim version 7.4. Last change: 2014 Sep 23 +*autocmd.txt* For Vim version 7.4. Last change: 2015 Dec 05 VIM REFERENCE MANUAL by Bram Moolenaar @@ -308,6 +308,8 @@ Name triggered by ~ |InsertCharPre| when a character was typed in Insert mode, before inserting it +|TextYankPost| when some text is yanked or deleted + |TextChanged| after a change was made to the text in Normal mode |TextChangedI| after a change was made to the text in Insert mode @@ -722,6 +724,18 @@ InsertCharPre When a character is typed in Insert mode, It is not allowed to change the text |textlock|. The event is not triggered when 'paste' is set. + *TextYankPost* +TextYankPost Just after a |yank| or |deleting| command, but not + if the black hole register |quote_| is used nor + for |setreg()|. Pattern must be * because its + meaning may change in the future. + Sets these |v:event| keys: + operator + regcontents + regname + regtype + Recursion is ignored. + It is not allowed to change the text |textlock|. *InsertEnter* InsertEnter Just before starting Insert mode. Also for Replace mode and Virtual Replace mode. The @@ -756,13 +770,15 @@ OptionSet After setting an option. The pattern is it's global or local scoped and |<amatch>| indicates what option has been set. - Note: It's a bad idea, to reset an option - during this autocommand, since this will - probably break plugins. You can always use - |noa| to prevent triggering this autocommand. - Could be used, to check for existence of the - 'backupdir' and 'undodir' options and create - directories, if they don't exist yet. + Usage example: Check for the existence of the + directory in the 'backupdir' and 'undodir' + options, create the directory if it doesn't + exist yet. + + Note: It's a bad idea to reset an option + during this autocommand, this may break a + plugin. You can always use `:noa` to prevent + triggering this autocommand. *QuickFixCmdPre* QuickFixCmdPre Before a quickfix command is run (|:make|, @@ -973,6 +989,13 @@ WinLeave Before leaving a window. If the window to be ============================================================================== 6. Patterns *autocmd-patterns* *{pat}* +The {pat} argument can be a comma separated list. This works as if the +command was given with each pattern separately. Thus this command: > + :autocmd BufRead *.txt,*.info set et +Is equivalent to: > + :autocmd BufRead *.txt set et + :autocmd BufRead *.info set et + The file pattern {pat} is tested for a match against the file name in one of two ways: 1. When there is no '/' in the pattern, Vim checks for a match against only @@ -1041,7 +1064,7 @@ The pattern is interpreted like mostly used in file names: [^ch] match any character but 'c' and 'h' Note that for all systems the '/' character is used for path separator (even -MS-DOS). This was done because the backslash is difficult to use in a pattern +Windows). This was done because the backslash is difficult to use in a pattern and to make the autocommands portable across different systems. *autocmd-changes* @@ -1079,7 +1102,7 @@ Instead of a pattern buffer-local autocommands use one of these forms: Examples: > :au CursorHold <buffer> echo 'hold' :au CursorHold <buffer=33> echo 'hold' - :au CursorHold <buffer=abuf> echo 'hold' + :au BufNewFile * au CursorHold <buffer=abuf> echo 'hold' All the commands for autocommands also work with buffer-local autocommands, simply use the special string instead of the pattern. Examples: > @@ -1138,6 +1161,9 @@ name! :aug[roup] {name} Define the autocmd group name for the following ":autocmd" commands. The name "end" or "END" selects the default group. + To avoid confusion, the name should be + different from existing {event} names, as this + most likely will not do what you intended. *:augroup-delete* *E367* :aug[roup]! {name} Delete the autocmd group {name}. Don't use diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 075e581bf2..c8eb0705f6 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 7.4. Last change: 2014 Jun 26 +*change.txt* For Vim version 7.4. Last change: 2016 Jan 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -366,13 +366,49 @@ Adding and subtracting ~ CTRL-A Add [count] to the number or alphabetic character at or after the cursor. + *v_CTRL-A* +{Visual}CTRL-A Add [count] to the number or alphabetic character in + the highlighted text. {not in Vi} + + *v_g_CTRL-A* +{Visual}g CTRL-A Add [count] to the number or alphabetic character in + the highlighted text. If several lines are + highlighted, each one will be incremented by an + additional [count] (so effectively creating a + [count] incrementing sequence). {not in Vi} + For Example, if you have this list of numbers: + 1. ~ + 1. ~ + 1. ~ + 1. ~ + Move to the second "1." and Visually select three + lines, pressing g CTRL-A results in: + 1. ~ + 2. ~ + 3. ~ + 4. ~ + *CTRL-X* CTRL-X Subtract [count] from the number or alphabetic character at or after the cursor. + *v_CTRL-X* +{Visual}CTRL-X Subtract [count] from the number or alphabetic + character in the highlighted text. {not in Vi} + + *v_g_CTRL-X* +{Visual}g CTRL-X Subtract [count] from the number or alphabetic + character in the highlighted text. If several lines + are highlighted, each value will be decremented by an + additional [count] (so effectively creating a [count] + decrementing sequence). {not in Vi} + The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned -octal and hexadecimal numbers and alphabetic characters. This depends on the -'nrformats' option. +binary/octal/hexadecimal numbers and alphabetic characters. + +This depends on the 'nrformats' option: +- When 'nrformats' includes "bin", Vim assumes numbers starting with '0b' or + '0B' are binary. - When 'nrformats' includes "octal", Vim considers numbers starting with a '0' to be octal, unless the number includes a '8' or '9'. Other numbers are decimal and may have a preceding minus sign. @@ -386,6 +422,10 @@ octal and hexadecimal numbers and alphabetic characters. This depends on the under or after the cursor. This is useful to make lists with an alphabetic index. +For decimals a leading negative sign is considered for incrementing or +decrementing, for binary, octal and hex values, it won't be considered. To +ignore the sign Visually select the number before using CTRL-A or CTRL-X. + For numbers with leading zeros (including all octal and hexadecimal numbers), Vim preserves the number of characters in the number when possible. CTRL-A on "0077" results in "0100", CTRL-X on "0x100" results in "0x0ff". @@ -397,6 +437,10 @@ octal number. Note that when 'nrformats' includes "octal", decimal numbers with leading zeros cause mistakes, because they can be confused with octal numbers. +Note similarly, when 'nrformats' includes "bin", binary numbers with a leading +'0x' or '0X' can be interpreted as hexadecimal rather than binary since '0b' +are valid hexadecimal digits. + The CTRL-A command is very useful in a macro. Example: Use the following steps to make a numbered list. @@ -578,9 +622,9 @@ For MS-Windows: $TMP, $TEMP, $USERPROFILE, current-dir. may add [flags], see |:s_flags|. Note that after `:substitute` the '&' flag can't be used, it's recognized as a pattern separator. - The space between `:substitute` and the 'c', 'g' and - 'r' flags isn't required, but in scripts it's a good - idea to keep it to avoid confusion. + The space between `:substitute` and the 'c', 'g', + 'i', 'I' and 'r' flags isn't required, but in scripts + it's a good idea to keep it to avoid confusion. :[range]~[&][flags] [count] *:~* Repeat last substitute with same substitute string @@ -789,6 +833,36 @@ either the first or second pattern in parentheses did not match, so either :s/\([ab]\)\|\([cd]\)/\1x/g modifies "a b c d" to "ax bx x x" < + *:sc* *:sce* *:scg* *:sci* *:scI* *:scl* *:scp* *:sg* *:sgc* + *:sge* *:sgi* *:sgI* *:sgl* *:sgn* *:sgp* *:sgr* *:sI* *:si* + *:sic* *:sIc* *:sie* *:sIe* *:sIg* *:sIl* *:sin* *:sIn* *:sIp* + *:sip* *:sIr* *:sir* *:sr* *:src* *:srg* *:sri* *:srI* *:srl* + *:srn* *:srp* +2-letter and 3-letter :substitute commands ~ + + List of :substitute commands + | c e g i I n p l r + | c :sc :sce :scg :sci :scI :scn :scp :scl --- + | e + | g :sgc :sge :sg :sgi :sgI :sgn :sgp :sgl :sgr + | i :sic :sie --- :si :siI :sin :sip --- :sir + | I :sIc :sIe :sIg :sIi :sI :sIn :sIp :sIl :sIr + | n + | p + | l + | r :src --- :srg :sri :srI :srn :srp :srl :sr + +Exceptions: + :scr is `:scriptnames` + :se is `:set` + :sig is `:sign` + :sil is `:silent` + :sn is `:snext` + :sp is `:split` + :sl is `:sleep` + :sre is `:srewind` + + Substitute with an expression *sub-replace-expression* *sub-replace-\=* *s/\=* When the substitute string starts with "\=" the remainder is interpreted as an @@ -832,13 +906,13 @@ This replaces each 'E' character with a euro sign. Read more in |<Char->|. :promptf[ind] [string] Put up a Search dialog. When [string] is given, it is used as the initial search string. - {only for Win32, Motif and GTK GUI} + {only for Win32 GUI} *:promptr* *:promptrepl* :promptr[epl] [string] Put up a Search/Replace dialog. When [string] is given, it is used as the initial search string. - {only for Win32, Motif and GTK GUI} + {only for Win32 GUI} 4.4 Changing tabs *change-tabs* @@ -892,7 +966,7 @@ inside of strings can change! Also see 'softtabstop' option. > :reg[isters] {arg} Display the contents of the numbered and named registers that are mentioned in {arg}. For example: > - :dis 1a + :reg 1a < to display registers '1' and 'a'. Spaces are allowed in {arg}. @@ -1058,16 +1132,17 @@ Rationale: In Vi the "y" command followed by a backwards motion would With a linewise yank command the cursor is put in the first line, but the column is unmodified, thus it may not be on the first yanked character. -There are nine types of registers: *registers* *E354* +There are ten types of registers: *registers* *E354* 1. The unnamed register "" 2. 10 numbered registers "0 to "9 3. The small delete register "- 4. 26 named registers "a to "z or "A to "Z -5. four read-only registers ":, "., "% and "# -6. the expression register "= -7. The selection and drop registers "*, "+ and "~ -8. The black hole register "_ -9. Last search pattern register "/ +5. three read-only registers ":, "., "% +6. alternate buffer register "# +7. the expression register "= +8. The selection and drop registers "*, "+ and "~ +9. The black hole register "_ +10. Last search pattern register "/ 1. Unnamed register "" *quote_quote* *quotequote* Vim fills this register with text deleted with the "d", "c", "s", "x" commands @@ -1109,7 +1184,7 @@ letters to replace their previous contents or as uppercase letters to append to their previous contents. When the '>' flag is present in 'cpoptions' then a line break is inserted before the appended text. -5. Read-only registers ":, "., "% and "# +5. Read-only registers ":, ". and "% These are '%', '#', ':' and '.'. You can use them only with the "p", "P", and ":put" commands and with CTRL-R. *quote_.* *quote.* *E29* @@ -1120,8 +1195,6 @@ and ":put" commands and with CTRL-R. ('textwidth' and other options affect what is inserted). *quote_%* *quote%* "% Contains the name of the current file. - *quote_#* *quote#* - "# Contains the name of the alternate file. *quote_:* *quote:* *E30* ": Contains the most recent executed command-line. Example: Use "@:" to repeat the previous command-line command. @@ -1129,15 +1202,33 @@ and ":put" commands and with CTRL-R. one character of it was typed. Thus it remains unchanged if the command was completely from a mapping. -6. Expression register "= *quote_=* *quote=* *@=* + *quote_#* *quote#* +6. Alternate file register "# +Contains the name of the alternate file for the current window. It will +change how the |CTRL-^| command works. +This register is writable, mainly to allow for restoring it after a plugin has +changed it. It accepts buffer number: > + let altbuf = bufnr(@#) + ... + let @# = altbuf +It will give error |E86| if you pass buffer number and this buffer does not +exist. +It can also accept a match with an existing buffer name: > + let @# = 'buffer_name' +Error |E93| if there is more than one buffer matching the given name or |E94| +if none of buffers matches the given name. + +7. Expression register "= *quote_=* *quote=* *@=* This is not really a register that stores text, but is a way to use an expression in commands which use a register. The expression register is -read-only; you cannot put text into it. After the '=', the cursor moves to -the command-line, where you can enter any expression (see |expression|). All -normal command-line editing commands are available, including a special -history for expressions. When you end the command-line by typing <CR>, Vim -computes the result of the expression. If you end it with <Esc>, Vim abandons -the expression. If you do not enter an expression, Vim uses the previous +read-write. + +When typing the '=' after " or CTRL-R the cursor moves to the command-line, +where you can enter any expression (see |expression|). All normal +command-line editing commands are available, including a special history for +expressions. When you end the command-line by typing <CR>, Vim computes the +result of the expression. If you end it with <Esc>, Vim abandons the +expression. If you do not enter an expression, Vim uses the previous expression (like with the "/" command). The expression must evaluate to a String. A Number is always automatically @@ -1150,35 +1241,23 @@ If the "= register is used for the "p" command, the String is split up at <NL> characters. If the String ends in a <NL>, it is regarded as a linewise register. -7. Selection and drop registers "*, "+ and "~ +8. Selection and drop registers "*, "+ and "~ Use these registers for storing and retrieving the selected text for the GUI. See |quotestar| and |quoteplus|. When the clipboard is not available or not working, the unnamed register is used instead. For Unix systems and Mac OS X, see |nvim-clipboard|. - *quote_~* *quote~* *<Drop>* -The read-only "~ register stores the dropped text from the last drag'n'drop -operation. When something has been dropped onto Vim, the "~ register is -filled in and the <Drop> pseudo key is sent for notification. You can remap -this key if you want; the default action (for all modes) is to insert the -contents of the "~ register at the cursor position. -{only available when compiled with the |+dnd| feature, currently only with the -GTK GUI} - -Note: The "~ register is only used when dropping plain text onto Vim. -Drag'n'drop of URI lists is handled internally. - -8. Black hole register "_ *quote_* +9. Black hole register "_ *quote_* When writing to this register, nothing happens. This can be used to delete text without affecting the normal registers. When reading from this register, nothing is returned. -9. Last search pattern register "/ *quote_/* *quote/* +10. Last search pattern register "/ *quote_/* *quote/* Contains the most recent search-pattern. This is used for "n" and 'hlsearch'. It is writable with `:let`, you can change it to have 'hlsearch' highlight other matches without actually searching. You can't yank or delete into this register. The search direction is available in |v:searchforward|. -Note that the valued is restored when returning from a function +Note that the value is restored when returning from a function |function-search-undo|. *@/* @@ -1390,10 +1469,10 @@ When you hit Return in a C-comment, Vim will insert the middle comment leader for the new line: " * ". To close this comment you just have to type "/" before typing anything else on the new line. This will replace the middle-comment leader with the end-comment leader and apply any specified -alignment, leaving just " */". There is no need to hit BackSpace first. +alignment, leaving just " */". There is no need to hit Backspace first. -When there is a match with a middle part, but there also is a maching end part -which is longer, the end part is used. This makes a C style comment work +When there is a match with a middle part, but there also is a matching end +part which is longer, the end part is used. This makes a C style comment work without requiring the middle part to end with a space. Here is an example of alignment flags at work to make a comment stand out @@ -1597,7 +1676,7 @@ Vim has a sorting function and a sorting command. The sorting function can be found here: |sort()|, |uniq()|. *:sor* *:sort* -:[range]sor[t][!] [i][u][r][n][x][o] [/{pattern}/] +:[range]sor[t][!] [b][f][i][n][o][r][u][x] [/{pattern}/] Sort lines in [range]. When no range is given all lines are sorted. @@ -1605,10 +1684,18 @@ found here: |sort()|, |uniq()|. With [i] case is ignored. + Options [n][f][x][o][b] are mutually exclusive. + With [n] sorting is done on the first decimal number in the line (after or inside a {pattern} match). One leading '-' is included in the number. + With [f] sorting is done on the Float in the line. + The value of Float is determined similar to passing + the text (after or inside a {pattern} match) to + str2float() function. This option is available only + if Vim was compiled with Floating point support. + With [x] sorting is done on the first hexadecimal number in the line (after or inside a {pattern} match). A leading "0x" or "0X" is ignored. @@ -1617,10 +1704,13 @@ found here: |sort()|, |uniq()|. With [o] sorting is done on the first octal number in the line (after or inside a {pattern} match). - With [u] only keep the first of a sequence of - identical lines (ignoring case when [i] is used). - Without this flag, a sequence of identical lines - will be kept in their original order. + With [b] sorting is done on the first binary number in + the line (after or inside a {pattern} match). + + With [u] (u stands for unique) only keep the first of + a sequence of identical lines (ignoring case when [i] + is used). Without this flag, a sequence of identical + lines will be kept in their original order. Note that leading and trailing white space may cause lines to be different. diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index d85d41a295..a123ea711b 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1,4 +1,4 @@ -*cmdline.txt* For Vim version 7.4. Last change: 2014 Sep 06 +*cmdline.txt* For Vim version 7.4. Last change: 2015 Dec 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -97,6 +97,11 @@ CTRL-E or <End> *c_CTRL-E* *c_<End>* *c_End* *c_<LeftMouse>* <LeftMouse> Move the cursor to the position of the mouse click. + *c_<MiddleMouse>* +<MiddleMouse> Paste the contents of the clipboard (for X11 the primary + selection). This is similar to using CTRL-R *, but no CR + characters are inserted between lines. + CTRL-H *c_<BS>* *c_CTRL-H* *c_BS* <BS> Delete the character in front of the cursor. *c_<Del>* *c_Del* @@ -379,7 +384,7 @@ CTRL-N After using 'wildchar' which got multiple matches, go to next <S-Tab> *c_CTRL-P* *c_<S-Tab>* CTRL-P After using 'wildchar' which got multiple matches, go to previous match. Otherwise recall older command-line from - history. <S-Tab> only works with the GUI and with MS-DOS. + history. <S-Tab> only works with the GUI. *c_CTRL-A* CTRL-A All names that match the pattern in front of the cursor are inserted. @@ -482,6 +487,8 @@ followed by another Vim command: :argdo :autocmd :bufdo + :cdo + :cfdo :command :cscope :debug @@ -490,8 +497,9 @@ followed by another Vim command: :function :global :help - :helpfind :lcscope + :ldo + :lfdo :make :normal :promptfind @@ -562,6 +570,7 @@ starts editing the three files "foo bar", "goes to" and "school ". When you want to use the special characters '"' or '|' in a command, or want to use '%' or '#' in a file name, precede them with a backslash. The backslash is not required in a range and in the ":substitute" command. +See also |`=|. *:_!* The '!' (bang) character after an Ex command makes the command behave in a @@ -711,13 +720,13 @@ to insert special things while typing you can use the CTRL-R command. For example, "%" stands for the current file name, while CTRL-R % inserts the current file name right away. See |c_CTRL-R|. -Note: If you want to avoid the special characters in a Vim script you may want -to use |fnameescape()|. +Note: If you want to avoid the effects of special characters in a Vim script +you may want to use |fnameescape()|. Also see |`=|. In Ex commands, at places where a file name can be used, the following characters have a special meaning. These can also be used in the expression -function expand() |expand()|. +function |expand()|. % Is replaced with the current file name. *:_%* *c_%* # Is replaced with the alternate file name. *:_#* *c_#* This is remembered for every window. @@ -752,6 +761,7 @@ it, no matter how many backslashes. # alternate.file \# # \\# \# +Also see |`=|. *:<cword>* *:<cWORD>* *:<cfile>* *<cfile>* *:<sfile>* *<sfile>* *:<afile>* *<afile>* @@ -773,13 +783,13 @@ Note: these are typed literally, they are not special keys! <afile> only when the file name isn't used to match with (for FileType, Syntax and SpellFileMissing events). <sfile> When executing a ":source" command, is replaced with the - file name of the sourced file. *E498* - When executing a function, is replaced with - "function {function-name}"; function call nesting is - indicated like this: - "function {function-name1}..{function-name2}". Note that - filename-modifiers are useless when <sfile> is used inside - a function. + file name of the sourced file. *E498* + When executing a function, is replaced with: + "function {function-name}[{lnum}]" + function call nesting is indicated like this: + "function {function-name1}[{lnum}]..{function-name2}[{lnum}]" + Note that filename-modifiers are useless when <sfile> is + used inside a function. <slnum> When executing a ":source" command, is replaced with the line number. *E842* When executing a function it's the line number relative to @@ -816,7 +826,7 @@ These modifiers can be given, in this order: separator is removed. Thus ":p:h" on a directory name results on the directory name itself (without trailing slash). When the file name is an absolute path (starts with "/" for - Unix; "x:\" for MS-DOS and WIN32), that part is not removed. + Unix; "x:\" for Windows), that part is not removed. When there is no head (path is relative to current directory) the result is empty. :t Tail of the file name (last component of the name). Must @@ -841,7 +851,7 @@ These modifiers can be given, in this order: :gs?pat?sub? Substitute all occurrences of "pat" with "sub". Otherwise this works like ":s". - :S Escape special characters for use with a shell command (see + :S Escape special characters for use with a shell command (see |shellescape()|). Must be the last one. Examples: > :!dir <cfile>:S :call system('chmod +w -- ' . expand('%:S')) @@ -894,9 +904,8 @@ name). This is included for backwards compatibility with version 3.0, the Note: Where a file name is expected wildcards expansion is done. On Unix the shell is used for this, unless it can be done internally (for speed). -Backticks also work, like in > +Unless in |restricted-mode|, backticks work also, like in > :n `echo *.c` -(backtick expansion is not possible in |restricted-mode|) But expansion is only done if there are any wildcards before expanding the '%', '#', etc.. This avoids expanding wildcards inside a file name. If you want to expand the result of <cfile>, add a wildcard character to it. @@ -907,6 +916,7 @@ Examples: (alternate file name is "?readme?") :e #.* :e {files matching "?readme?.*"} :cd <cfile> :cd {file name under cursor} :cd <cfile>* :cd {file name under cursor plus "*" and then expanded} +Also see |`=|. When the expanded argument contains a "!" and it is used for a shell command (":!cmd", ":r !cmd" or ":w !cmd"), the "!" is escaped with a backslash to @@ -915,8 +925,8 @@ option contains "sh", this is done twice, to avoid the shell trying to expand the "!". *filename-backslash* -For filesystems that use a backslash as directory separator (MS-DOS and -Windows), it's a bit difficult to recognize a backslash that is used +For filesystems that use a backslash as directory separator (Windows +filesystems), it's a bit difficult to recognize a backslash that is used to escape the special meaning of the next character. The general rule is: If the backslash is followed by a normal file name character, it does not have a special meaning. Therefore "\file\foo" is a valid file name, you don't have @@ -933,6 +943,8 @@ for the file "$home" in the root directory. A few examples: /\$home file "$home" in root directory \\$home file "\\", followed by expanded $home +Also see |`=|. + ============================================================================== 7. Command-line window *cmdline-window* *cmdwin* *command-line-window* diff --git a/runtime/doc/debugger.txt b/runtime/doc/debugger.txt deleted file mode 100644 index f1eb5639bd..0000000000 --- a/runtime/doc/debugger.txt +++ /dev/null @@ -1,107 +0,0 @@ -*debugger.txt* For Vim version 7.4. Last change: 2005 Mar 29 - - - VIM REFERENCE MANUAL by Gordon Prieur - - -Debugger Support Features *debugger-support* - -1. Debugger Features |debugger-features| - -============================================================================== -1. Debugger Features *debugger-features* - -The following features are available for an integration with a debugger or -an Integrated Programming Environment (IPE) or Integrated Development -Environment (IDE): - - Alternate Command Input |alt-input| - Debug Signs |debug-signs| - Debug Source Highlight |debug-highlight| - Message Footer |gui-footer| - Balloon Evaluation |balloon-eval| - -These features were added specifically for use in the Motif version of gvim. -However, the |alt-input| and |debug-highlight| were written to be usable in -both vim and gvim. Some of the other features could be used in the non-GUI -vim with slight modifications. However, I did not do this nor did I test the -reliability of building for vim or non Motif GUI versions. - - -1.1 Alternate Command Input *alt-input* - -For Vim to work with a debugger there must be at least an input connection -with a debugger or external tool. In many cases there will also be an output -connection but this isn't absolutely necessary. - -The purpose of the input connection is to let the external debugger send -commands to Vim. The commands sent by the debugger should give the debugger -enough control to display the current debug environment and state. - -The current implementation is based on the X Toolkit dispatch loop and the -XtAddInput() function call. - - -1.2 Debug Signs *debug-signs* - -Many debuggers mark specific lines by placing a small sign or color highlight -on the line. The |:sign| command lets the debugger set this graphic mark. Some -examples where this feature would be used would be a debugger showing an arrow -representing the Program Counter (PC) of the program being debugged. Another -example would be a small stop sign for a line with a breakpoint. These visible -highlights let the user keep track of certain parts of the state of the -debugger. - -This feature can be used with more than debuggers, too. An IPE can use a sign -to highlight build errors, searched text, or other things. The sign feature -can also work together with the |debug-highlight| to ensure the mark is -highly visible. - -Debug signs are defined and placed using the |:sign| command. - - -1.3 Debug Source Highlight *debug-highlight* - -This feature allows a line to have a predominant highlight. The highlight is -intended to make a specific line stand out. The highlight could be made to -work for both vim and gvim, whereas the debug sign is, in most cases, limited -to gvim. The one exception to this is Sun Microsystem's dtterm. The dtterm -from Sun has a "sign gutter" for showing signs. - - -1.4 Message Footer *gui-footer* - -The message footer can be used to display messages from a debugger or IPE. It -can also be used to display menu and toolbar tips. The footer area is at the -bottom of the GUI window, below the line used to display colon commands. - -The display of the footer is controlled by the 'guioptions' letter 'F'. - - -1.5 Balloon Evaluation *balloon-eval* - -This feature allows a debugger, or other external tool, to display dynamic -information based on where the mouse is pointing. The purpose of this feature -was to allow Sun's Visual WorkShop debugger to display expression evaluations. -However, the feature was implemented in as general a manner as possible and -could be used for displaying other information as well. - -The Balloon Evaluation has some settable parameters too. For Motif the font -list and colors can be set via X resources (XmNballoonEvalFontList, -XmNballoonEvalBackground, and XmNballoonEvalForeground). -The 'balloondelay' option sets the delay before an attempt is made to show a -balloon. -The 'ballooneval' option needs to be set to switch it on. - -Balloon evaluation is only available when compiled with the |+balloon_eval| -feature. - -The Balloon evaluation functions are also used to show a tooltip for the -toolbar. The 'ballooneval' option does not need to be set for this. But the -other settings apply. - -Another way to use the balloon is with the 'balloonexpr' option. This is -completely user definable. - -============================================================================== - vim:tw=78:sw=4:ts=8:ft=help:norl: diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt index 54d47eb28a..12bc655edc 100644 --- a/runtime/doc/diff.txt +++ b/runtime/doc/diff.txt @@ -1,4 +1,4 @@ -*diff.txt* For Vim version 7.4. Last change: 2015 Jan 19 +*diff.txt* For Vim version 7.4. Last change: 2015 Nov 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -124,8 +124,9 @@ file for a moment and come back to the same file and be in diff mode again. if the current window does not have 'diff' set then no options in it are changed. -The ":diffoff" command resets the relevant options to the values they had when -using |:diffsplit|, |:diffpatch| , |:diffthis|. or starting Vim in diff mode. +The `:diffoff` command resets the relevant options to the values they had when +using `:diffsplit`, `:diffpatch` , `:diffthis`. or starting Vim in diff mode. +When using `:diffoff` twice the last saved values are restored. Otherwise they are set to their default value: 'diff' off @@ -173,8 +174,8 @@ hidden buffers. You can use ":hide" to close a window without unloading the buffer. If you don't want a buffer to remain used for the diff do ":set nodiff" before hiding it. - *:diffu* *:diffupdate* -:diffu[pdate][!] Update the diff highlighting and folds. + *:dif* *:diffupdate* +:dif[fupdate][!] Update the diff highlighting and folds. Vim attempts to keep the differences updated when you make changes to the text. This mostly takes care of inserted and deleted lines. Changes within a @@ -306,6 +307,19 @@ name or a part of a buffer name. Examples: Also see |'diffopt'| and the "diff" item of |'fillchars'|. + *diff-slow* *diff_translations* +For very long lines, the diff syntax highlighting might be slow, especially +since it tries to match all different kind of localisations. To disable +localisations and speed up the syntax highlighting, set the global variable +g:diff_translations to zero: > + + let g:diff_translations = 0 +< +After setting this variable, Reload the syntax script: > + + set syntax=diff +< + FINDING THE DIFFERENCES *diff-diffexpr* diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index ffa3f1d673..c51286a350 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1,4 +1,4 @@ -*editing.txt* For Vim version 7.4. Last change: 2015 Apr 18 +*editing.txt* For Vim version 7.4. Last change: 2016 Jan 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -77,7 +77,8 @@ g CTRL-G Prints the current position of the cursor in five than one position on the screen (<Tab> or special character), both the "real" column and the screen column are shown, separated with a dash. - See also 'ruler' option. + Also see the 'ruler' option and the |wordcount()| + function. *v_g_CTRL-G* {Visual}g CTRL-G Similar to "g CTRL-G", but Word, Character, Line, and @@ -130,9 +131,7 @@ You can use this file if you discover that you need the original file. See also the 'patchmode' option. The name of the backup file is normally the same as the original file with 'backupext' appended. The default "~" is a bit strange to avoid accidentally overwriting existing files. If you prefer ".bak" -change the 'backupext' option. Extra dots are replaced with '_' on MS-DOS -machines, when Vim has detected that an MS-DOS-like filesystem is being used -(e.g., messydos or crossdos). The backup file can be placed in another +change the 'backupext' option. The backup file can be placed in another directory by setting 'backupdir'. When you started editing without giving a file name, "No File" is displayed in @@ -276,7 +275,8 @@ CTRL-^ Edit the alternate file. Mostly the alternate file is Mnemonic: "goto file". Uses the 'isfname' option to find out which characters are supposed to be in a file name. Trailing - punctuation characters ".,:;!" are ignored. + punctuation characters ".,:;!" are ignored. Escaped + spaces "\ " are reduced to a single space. Uses the 'path' option as a list of directory names to look for the file. See the 'path' option for details about relative directories and wildcards. @@ -379,25 +379,38 @@ Finds files: /usr/include/sys/types.h /usr/inc_old/types.h *backtick-expansion* *`-expansion* -On Unix and a few other systems you can also use backticks in the file name, -for example: > - :e `find . -name ver\\*.c -print` -The backslashes before the star are required to prevent "ver*.c" to be -expanded by the shell before executing the find program. +On Unix and a few other systems you can also use backticks for the file name +argument, for example: > + :next `find . -name ver\\*.c -print` + :view `ls -t *.patch \| head -n1` +The backslashes before the star are required to prevent the shell from +expanding "ver*.c" prior to execution of the find program. The backslash +before the shell pipe symbol "|" prevents Vim from parsing it as command +termination. This also works for most other systems, with the restriction that the backticks must be around the whole item. It is not possible to have text directly before the first or just after the last backtick. *`=* -You can have the backticks expanded as a Vim expression, instead of an -external command, by using the syntax `={expr}` e.g.: > +You can have the backticks expanded as a Vim expression, instead of as an +external command, by putting an equal sign right after the first backtick, +e.g.: > :e `=tempname()` The expression can contain just about anything, thus this can also be used to avoid the special meaning of '"', '|', '%' and '#'. However, 'wildignore' does apply like to other wildcards. + +Environment variables in the expression are expanded when evaluating the +expression, thus this works: > + :e `=$HOME . '/.vimrc'` +This does not work, $HOME is inside a string and used literally: > + :e `='$HOME' . '/.vimrc'` + If the expression returns a string then names are to be separated with line breaks. When the result is a |List| then each item is used as a name. Line breaks also separate names. +Note that such expressions are only supported in places where a filename is +expected as an argument to an Ex-command. *++opt* *[++opt]* The [++opt] argument can be used to force the value of 'fileformat', @@ -470,9 +483,9 @@ The 'fileformat' option sets the <EOL> style for a file: "mac" <CR> Mac format *Mac-format* When reading a file, the mentioned characters are interpreted as the <EOL>. -In DOS format (default for MS-DOS and Win32), <CR><NL> and <NL> are both -interpreted as the <EOL>. Note that when writing the file in DOS format, -<CR> characters will be added for each single <NL>. Also see |file-read|. +In DOS format (default for Windows), <CR><NL> and <NL> are both interpreted as +the <EOL>. Note that when writing the file in DOS format, <CR> characters will +be added for each single <NL>. Also see |file-read|. When writing a file, the mentioned characters are used for <EOL>. For DOS format <CR><NL> is used. Also see |DOS-format-write|. @@ -493,13 +506,13 @@ If you start editing a new file and the 'fileformats' option is not empty (which is the default), Vim will try to detect whether the lines in the file are separated by the specified formats. When set to "unix,dos", Vim will check for lines with a single <NL> (as used on Unix) or by a <CR><NL> pair -(MS-DOS). Only when ALL lines end in <CR><NL>, 'fileformat' is set to "dos", +(Windows). Only when ALL lines end in <CR><NL>, 'fileformat' is set to "dos", otherwise it is set to "unix". When 'fileformats' includes "mac", and no <NL> characters are found in the file, 'fileformat' is set to "mac". -If the 'fileformat' option is set to "dos" on non-MS-DOS systems the message +If the 'fileformat' option is set to "dos" on non-Windows systems the message "[dos format]" is shown to remind you that something unusual is happening. On -MS-DOS systems you get the message "[unix format]" if 'fileformat' is set to +Windows systems you get the message "[unix format]" if 'fileformat' is set to "unix". On all systems but the Macintosh you get the message "[mac format]" if 'fileformat' is set to "mac". @@ -795,7 +808,8 @@ USING THE ARGUMENT LIST autocommand event is disabled by adding it to 'eventignore'. This considerably speeds up editing each file. - Also see |:windo|, |:tabdo| and |:bufdo|. + Also see |:windo|, |:tabdo|, |:bufdo|, |:cdo|, |:ldo|, + |:cfdo| and |:lfdo|. Example: > :args *.c @@ -964,10 +978,10 @@ lost the original file. *DOS-format-write* If the 'fileformat' is "dos", <CR> <NL> is used for <EOL>. This is default -for MS-DOS and Win32. On other systems the message "[dos format]" is shown to +for Windows. On other systems the message "[dos format]" is shown to remind you that an unusual <EOL> was used. *Unix-format-write* -If the 'fileformat' is "unix", <NL> is used for <EOL>. On MS-DOS and Win32 +If the 'fileformat' is "unix", <NL> is used for <EOL>. On Windows the message "[unix format]" is shown. *Mac-format-write* If the 'fileformat' is "mac", <CR> is used for <EOL>. On non-Mac systems the @@ -997,11 +1011,11 @@ When the file name is actually a device name, Vim will not make a backup (that would be impossible). You need to use "!", since the device already exists. Example for Unix: > :w! /dev/lpt0 -and for MS-DOS or MS-Windows: > +and Windows: > :w! lpt0 For Unix a device is detected when the name doesn't refer to a normal file or a directory. A fifo or named pipe also looks like a device to Vim. -For MS-DOS and MS-Windows the device is detected by its name: +For Windows the device is detected by its name: CON CLOCK$ NUL @@ -1028,10 +1042,10 @@ The names can be in upper- or lowercase. the last file in the argument list has not been edited. See |:confirm| and 'confirm'. -:q[uit]! Quit without writing, also when currently visible - buffers have changes. Does not exit when this is the - last window and there is a changed hidden buffer. - In this case, the first changed hidden buffer becomes +:q[uit]! Quit without writing, also when the current buffer has + changes. If this is the last window and there is a + modified hidden buffer, the current buffer is + abandoned and the first changed hidden buffer becomes the current buffer. Use ":qall!" to exit always. @@ -1144,7 +1158,7 @@ If you want to always use ":confirm", set the 'confirm' option. |:diffsplit|, |:diffpatch|, |:pedit|, |:redir|, |:source|, |:update|, |:visual|, |:vsplit|, and |:qall| if 'confirm' is set. - {only in Win32, Athena, Motif, GTK and Mac GUI} + {only in Win32 GUI} When ":browse" is not possible you get an error message. If the |+browse| feature is missing or the {command} doesn't support browsing, the {command} is @@ -1172,16 +1186,13 @@ For versions of Vim where browsing is not supported, the command is executed unmodified. *browsefilter* -For MS Windows and GTK, you can modify the filters that are used in the browse -dialog. By setting the g:browsefilter or b:browsefilter variables, you can -change the filters globally or locally to the buffer. The variable is set to -a string in the format "{filter label}\t{pattern};{pattern}\n" where {filter -label} is the text that appears in the "Files of Type" comboBox, and {pattern} -is the pattern which filters the filenames. Several patterns can be given, -separated by ';'. - -For Motif the same format is used, but only the very first pattern is actually -used (Motif only offers one pattern, but you can edit it). +For Windows you can modify the filters that are used in the browse dialog. By +setting the g:browsefilter or b:browsefilter variables, you can change the +filters globally or locally to the buffer. The variable is set to a string in +the format "{filter label}\t{pattern};{pattern}\n" where {filter label} is the +text that appears in the "Files of Type" comboBox, and {pattern} is the +pattern which filters the filenames. Several patterns can be given, separated +by ';'. For example, to have only Vim files in the dialog, you could use the following command: > @@ -1206,12 +1217,18 @@ use has("browsefilter"): > ============================================================================== 7. The current directory *current-directory* -You may use the |:cd| and |:lcd| commands to change to another directory, so -you will not have to type that directory name in front of the file names. It -also makes a difference for executing external commands, e.g. ":!ls". +You can use |:cd|, |:tcd| and |:lcd| to change to another directory, so you +will not have to type that directory name in front of the file names. It also +makes a difference for executing external commands, e.g. ":!ls" or ":te ls". -Changing directory fails when the current buffer is modified, the '.' flag is -present in 'cpoptions' and "!" is not used in the command. +There are three current-directory "scopes": global, tab and window. The +window-local working directory takes precedence over the tab-local +working directory, which in turn takes precedence over the global +working directory. If a local working directory (tab or window) does not +exist, the next-higher scope in the hierarchy applies. + +Commands for changing the working directory can be suffixed with a bang "!" +(e.g. |:cd!|) which is ignored, for compatibility with Vim. *:cd* *E747* *E472* :cd[!] On non-Unix systems: Print the current directory @@ -1225,7 +1242,7 @@ present in 'cpoptions' and "!" is not used in the command. Does not change the meaning of an already opened file, because its full path name is remembered. Files from the |arglist| may change though! - On MS-DOS this also changes the active drive. + On Windows this also changes the active drive. To change to the directory of the current file: > :cd %:h < @@ -1236,29 +1253,50 @@ present in 'cpoptions' and "!" is not used in the command. *:chd* *:chdir* :chd[ir][!] [path] Same as |:cd|. + *:tc* *:tcd* *E5000* *E5001* *E5002* +:tc[d][!] {path} Like |:cd|, but set the current directory for the + current tab and window. The current directory for + other tabs and windows is not changed. + + *:tcd-* +:tcd[!] - Change to the previous current directory (before the + previous ":tcd {path}" command). + + *:tch* *:tchdir* +:tch[dir][!] Same as |:tcd|. + *:lc* *:lcd* :lc[d][!] {path} Like |:cd|, but only set the current directory for the current window. The current directory for other - windows is not changed. + windows or any tabs is not changed. *:lch* *:lchdir* :lch[dir][!] Same as |:lcd|. + *:lcd-* +:lcd[!] - Change to the previous current directory (before the + previous ":tcd {path}" command). + *:pw* *:pwd* *E187* :pw[d] Print the current directory name. Also see |getcwd()|. -So long as no |:lcd| command has been used, all windows share the same current -directory. Using a command to jump to another window doesn't change anything -for the current directory. -When a |:lcd| command has been used for a window, the specified directory -becomes the current directory for that window. Windows where the |:lcd| -command has not been used stick to the global current directory. When jumping -to another window the current directory will become the last specified local -current directory. If none was specified, the global current directory is -used. -When a |:cd| command is used, the current window will lose his local current -directory and will use the global current directory from now on. +So long as no |:tcd| or |:lcd| command has been used, all windows share the +same "current directory". Using a command to jump to another window doesn't +change anything for the current directory. + +When |:lcd| has been used for a window, the specified directory becomes the +current directory for that window. Windows where the |:lcd| command has not +been used stick to the global or tab-local directory. When jumping to another +window the current directory will become the last specified local current +directory. If none was specified, the global or tab-local directory is used. + +When changing tabs the same behaviour applies. If the current tab has no +local working directory the global working directory is used. When a |:cd| +command is used, the current window and tab will lose their local current +directories and will use the global current directory from now on. When +a |:tcd| command is used, only the current window will lose its local working +directory. After using |:cd| the full path name will be used for reading and writing files. On some networked file systems this may cause problems. The result of @@ -1295,7 +1333,7 @@ There are a few things to remember when editing binary files: and when the file is written the <NL> will be replaced with <CR> <NL>. - <Nul> characters are shown on the screen as ^@. You can enter them with "CTRL-V CTRL-@" or "CTRL-V 000". -- To insert a <NL> character in the file split up a line. When writing the +- To insert a <NL> character in the file split a line. When writing the buffer to a file a <NL> will be written for the <EOL>. - Vim normally appends an <EOL> at the end of the file if there is none. Setting the 'binary' option prevents this. If you want to add the final @@ -1307,9 +1345,7 @@ There are a few things to remember when editing binary files: 9. Encryption *encryption* *:X* *E817* *E818* *E819* *E820* -Support for editing encrypted files has been removed, but may be added back in -the future. See the following discussions for more information: - +Support for editing encrypted files has been removed. https://github.com/neovim/neovim/issues/694 https://github.com/neovim/neovim/issues/701 diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 4ff0636b61..6ae137a6ce 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2014 Nov 27 +*eval.txt* For Vim version 7.4. Last change: 2016 Jan 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -65,14 +65,16 @@ the Number. Examples: Number 0 --> String "0" ~ Number -1 --> String "-1" ~ *octal* -Conversion from a String to a Number is done by converting the first digits -to a number. Hexadecimal "0xf9" and Octal "017" numbers are recognized. If -the String doesn't start with digits, the result is zero. Examples: +Conversion from a String to a Number is done by converting the first digits to +a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are +recognized. If the String doesn't start with digits, the result is zero. +Examples: String "456" --> Number 456 ~ String "6bar" --> Number 6 ~ String "foo" --> Number 0 ~ String "0xf1" --> Number 241 ~ String "0100" --> Number 64 ~ + String "0b101" --> Number 5 ~ String "-8" --> Number -8 ~ String "+8" --> Number 0 ~ @@ -522,7 +524,7 @@ Funcref to a Dictionary, but the "self" variable is not available then. To avoid the extra name for the function it can be defined and directly assigned to a Dictionary in this way: > :let mydict = {'data': [0, 1, 2, 3]} - :function mydict.len() dict + :function mydict.len() : return len(self.data) :endfunction :echo mydict.len() @@ -862,8 +864,8 @@ expr1'th single byte from expr8. expr8 is used as a String, expr1 as a Number. This doesn't recognize multi-byte encodings, see |byteidx()| for an alternative. -Index zero gives the first character. This is like it works in C. Careful: -text column numbers start with one! Example, to get the character under the +Index zero gives the first byte. This is like it works in C. Careful: +text column numbers start with one! Example, to get the byte under the cursor: > :let c = getline(".")[col(".") - 1] @@ -914,6 +916,11 @@ just above, except that indexes out of range cause an error. Examples: > Using expr8[expr1] or expr8[expr1a : expr1b] on a |Funcref| results in an error. +Watch out for confusion between a namespace and a variable followed by a colon +for a sublist: > + mylist[n:] " uses variable n + mylist[s:] " uses namespace s:, error! + expr8.name entry in a |Dictionary| *expr-entry* @@ -953,7 +960,7 @@ Decimal, Hexadecimal (starting with 0x or 0X), or Octal (starting with 0). Floating point numbers can be written in two forms: [-+]{N}.{M} - [-+]{N}.{M}e[-+]{exp} + [-+]{N}.{M}[eE][-+]{exp} {N} and {M} are numbers. Both {N} and {M} must be present and can only contain digits. @@ -1001,7 +1008,7 @@ function. Example: > -string *string* *expr-string* *E114* +string *string* *String* *expr-string* *E114* ------ "string" string constant *expr-quote* @@ -1017,7 +1024,7 @@ A string constant accepts these special characters: \X. same as \x. \u.... character specified with up to 4 hex numbers, stored according to the current value of 'encoding' (e.g., "\u02a4") -\U.... same as \u.... +\U.... same as \u but allows up to 8 hex numbers. \b backspace <BS> \e escape <Esc> \f formfeed <FF> @@ -1375,6 +1382,31 @@ v:errmsg Last given error message. It's allowed to set this variable. : ... handle error < "errmsg" also works, for backwards compatibility. + *v:errors* *errors-variable* +v:errors Errors found by assert functions, such as |assert_true()|. + This is a list of strings. + The assert functions append an item when an assert fails. + To remove old results make it empty: > + :let v:errors = [] +< If v:errors is set to anything but a list it is made an empty + list by the assert function. + + *v:event* *event-variable* +v:event Dictionary of event data for the current |autocommand|. The + available keys differ per event type and are specified at the + documentation for each |event|. The possible keys are: + operator The operation performed. Unlike + |v:operator|, it is set also for an Ex + mode command. For instance, |:yank| is + translated to "|y|". + regcontents Text stored in the register as a + |readfile()|-style list of lines. + regname Requested register (e.g "x" for "xyy) + or the empty string for an unnamed + operation. + regtype Type of register as returned by + |getregtype()|. + *v:exception* *exception-variable* v:exception The value of the exception most recently caught and not finished. See also |v:throwpoint| and |throw-variables|. @@ -1386,6 +1418,13 @@ v:exception The value of the exception most recently caught and not :endtry < Output: "caught oops". + *v:false* *false-variable* +v:false Special value used to put "false" in JSON and msgpack. See + |json_encode()|. This value is converted to "false" when used + as a String (e.g. in |expr5| with string concatenation + operator) and to zero when used as a Number (e.g. in |expr5| + or |expr7| when used with numeric operators). + *v:fcs_reason* *fcs_reason-variable* v:fcs_reason The reason why the |FileChangedShell| event was triggered. Can be used in an autocommand to decide what to do and/or what @@ -1464,7 +1503,9 @@ v:hlsearch Variable that indicates whether search highlighting is on. this variable to zero acts like the |:nohlsearch| command, setting it to one acts like > let &hlsearch = &hlsearch -< +< Note that the value is restored when returning from a + function. |function-search-undo|. + *v:insertmode* *insertmode-variable* v:insertmode Used for the |InsertEnter| and |InsertChange| autocommand events. Values: @@ -1523,6 +1564,13 @@ v:msgpack_types Dictionary containing msgpack types used by |msgpackparse()| (not editable) empty lists. To check whether some list is one of msgpack types, use |is| operator. + *v:null* *null-variable* +v:null Special value used to put "null" in JSON and NIL in msgpack. + See |json_encode()|. This value is converted to "null" when + used as a String (e.g. in |expr5| with string concatenation + operator) and to zero when used as a Number (e.g. in |expr5| + or |expr7| when used with numeric operators). + *v:oldfiles* *oldfiles-variable* v:oldfiles List of file names that is loaded from the |shada| file on startup. These are the files that Vim remembers marks for. @@ -1688,6 +1736,13 @@ v:throwpoint The point where the exception most recently caught and not :endtry < Output: "Exception from test.vim, line 2" + *v:true* *true-variable* +v:true Special value used to put "true" in JSON and msgpack. See + |json_encode()|. This value is converted to "true" when used + as a String (e.g. in |expr5| with string concatenation + operator) and to one when used as a Number (e.g. in |expr5| or + |expr7| when used with numeric operators). + *v:val* *val-variable* v:val Value of the current item of a |List| or |Dictionary|. Only valid while evaluating the expression used with |map()| and @@ -1708,7 +1763,8 @@ v:version Version number of Vim: Major version number times 100 plus v:warningmsg Last given warning message. It's allowed to set this variable. *v:windowid* *windowid-variable* {Nvim} -v:windowid Is a no-op at the moment; the value is always set to 0. +v:windowid Application-specific window ID ("window handle" in MS-Windows) + which may be set by any attached UI. Defaults to zero. Note: for windows inside Vim use |winnr()|. ============================================================================== @@ -1728,10 +1784,14 @@ append( {lnum}, {string}) Number append {string} below line {lnum} append( {lnum}, {list}) Number append lines {list} below line {lnum} argc() Number number of files in the argument list argidx() Number current index in the argument list -arglistid( [{winnr}, [ {tabnr}]]) +arglistid( [{winnr} [, {tabnr}]]) Number argument list id argv( {nr}) String {nr} entry of the argument list argv( ) List the argument list +assert_equal( {exp}, {act} [, {msg}]) none assert {exp} equals {act} +assert_exception({error} [, {msg}]) none assert {error} is in v:exception +assert_false( {actual} [, {msg}]) none assert {actual} is false +assert_true( {actual} [, {msg}]) none assert {actual} is true asin( {expr}) Float arc sine of {expr} atan( {expr}) Float arc tangent of {expr} atan2( {expr}, {expr}) Float arc tangent of {expr1} / {expr2} @@ -1742,7 +1802,7 @@ bufexists( {expr}) Number TRUE if buffer {expr} exists buflisted( {expr}) Number TRUE if buffer {expr} is listed bufloaded( {expr}) Number TRUE if buffer {expr} is loaded bufname( {expr}) String Name of the buffer {expr} -bufnr( {expr}) Number Number of the buffer {expr} +bufnr( {expr} [, {create}]) Number Number of the buffer {expr} bufwinnr( {expr}) Number window number of buffer {expr} byte2line( {byte}) Number line number at byte count {byte} byteidx( {expr}, {nr}) Number byte index of {nr}'th char in {expr} @@ -1771,9 +1831,11 @@ cursor( {lnum}, {col} [, {off}]) Number move cursor to {lnum}, {col}, {off} cursor( {list}) Number move cursor to position in {list} deepcopy( {expr} [, {noref}]) any make a full copy of {expr} -delete( {fname}) Number delete file {fname} -dictwatcheradd({dict}, {pattern}, {callback}) Start watching a dictionary -dictwatcherdel({dict}, {pattern}, {callback}) Stop watching a dictionary +delete( {fname} [, {flags}]) Number delete the file or directory {fname} +dictwatcheradd( {dict}, {pattern}, {callback}) + Start watching a dictionary +dictwatcherdel( {dict}, {pattern}, {callback}) + Stop watching a dictionary did_filetype() Number TRUE if FileType autocommand event used diff_filler( {lnum}) Number diff filler lines about {lnum} diff_hlID( {lnum}, {col}) Number diff highlighting at {lnum}/{col} @@ -1793,7 +1855,7 @@ feedkeys( {string} [, {mode}]) Number add key sequence to typeahead buffer filereadable( {file}) Number TRUE if {file} is a readable file filewritable( {file}) Number TRUE if {file} is a writable file filter( {expr}, {string}) List/Dict remove items from {expr} where - {string} is 0 + {string} is 0 finddir( {name}[, {path}[, {count}]]) String find directory {name} in {path} findfile( {name}[, {path}[, {count}]]) @@ -1819,12 +1881,13 @@ getbufvar( {expr}, {varname} [, {def}]) any variable {varname} in buffer {expr} getchar( [expr]) Number get one character from the user getcharmod( ) Number modifiers for the last typed character +getcharsearch() Dict last character search getcmdline() String return the current command-line getcmdpos() Number return cursor position in command-line getcmdtype() String return current command-line type getcmdwintype() String return current command-line window type getcurpos() List position of the cursor -getcwd() String the current working directory +getcwd( [{scope}]) String the current working directory getfontname( [{name}]) String name of font being used getfperm( {fname}) String file permissions of file {fname} getfsize( {fname}) Number size in bytes of file {fname} @@ -1848,10 +1911,10 @@ getwinposx() Number X coord in pixels of GUI Vim window getwinposy() Number Y coord in pixels of GUI Vim window getwinvar( {nr}, {varname} [, {def}]) any variable {varname} in window {nr} -glob( {expr} [, {nosuf} [, {list}]]) +glob( {expr} [, {nosuf} [, {list} [, {alllinks}]]]) any expand file wildcards in {expr} glob2regpat( {expr}) String convert a glob pat into a search pat -globpath( {path}, {expr} [, {nosuf} [, {list}]]) +globpath( {path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]]) String do glob({expr}) for all dirs in {path} has( {feature}) Number TRUE if feature {feature} supported has_key( {dict}, {key}) Number TRUE if {dict} has entry {key} @@ -1875,20 +1938,25 @@ inputdialog( {p} [, {t} [, {c}]]) String like input() but in a GUI dialog inputlist( {textlist}) Number let the user pick from a choice list inputrestore() Number restore typeahead inputsave() Number save and clear typeahead -inputsecret( {prompt} [, {text}]) String like input() but hiding the text -insert( {list}, {item} [, {idx}]) List insert {item} in {list} [before {idx}] +inputsecret( {prompt} [, {text}]) + String like input() but hiding the text +insert( {list}, {item} [, {idx}]) + List insert {item} in {list} [before {idx}] invert( {expr}) Number bitwise invert isdirectory( {directory}) Number TRUE if {directory} is a directory islocked( {expr}) Number TRUE if {expr} is locked items( {dict}) List key-value pairs in {dict} -jobclose({job}[, {stream}]) Number Closes a job stream(s) -jobresize({job}, {width}, {height}) +jobclose( {job}[, {stream}]) Number Closes a job stream(s) +jobpid( {job}) Number Returns pid of a job. +jobresize( {job}, {width}, {height}) Number Resize {job}'s pseudo terminal window -jobsend({job}, {data}) Number Writes {data} to {job}'s stdin -jobstart({cmd}[, {opts}]) Number Spawns {cmd} as a job -jobstop({job}) Number Stops a job -jobwait({ids}[, {timeout}]) Number Wait for a set of jobs +jobsend( {job}, {data}) Number Writes {data} to {job}'s stdin +jobstart( {cmd}[, {opts}]) Number Spawns {cmd} as a job +jobstop( {job}) Number Stops a job +jobwait( {ids}[, {timeout}]) Number Wait for a set of jobs join( {list} [, {sep}]) String join {list} items into one String +json_decode( {expr}) any Convert {expr} from JSON +json_encode( {expr}) String Convert {expr} to JSON keys( {dict}) List keys in {dict} len( {expr}) Number the length of {expr} libcall( {lib}, {func}, {arg}) String call {func} in library {lib} with {arg} @@ -1941,6 +2009,7 @@ range( {expr} [, {max} [, {stride}]]) readfile( {fname} [, {binary} [, {max}]]) List get list of lines from file {fname} reltime( [{start} [, {end}]]) List get time value +reltimefloat( {time}) Float turn the time value into a Float reltimestr( {time}) String turn time value into a String remote_expr( {server}, {string} [, {idvar}]) String send expression @@ -1957,12 +2026,12 @@ repeat( {expr}, {count}) String repeat {expr} {count} times resolve( {filename}) String get filename a shortcut points to reverse( {list}) List reverse {list} in-place round( {expr}) Float round off {expr} -rpcnotify({channel}, {event}[, {args}...]) +rpcnotify( {channel}, {event}[, {args}...]) Sends a |msgpack-rpc| notification to {channel} -rpcrequest({channel}, {method}[, {args}...]) +rpcrequest( {channel}, {method}[, {args}...]) Sends a |msgpack-rpc| request to {channel} -rpcstart({prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel -rpcstop({channel}) Closes a |msgpack-rpc| {channel} +rpcstart( {prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel +rpcstop( {channel}) Closes a |msgpack-rpc| {channel} screenattr( {row}, {col}) Number attribute at screen position screenchar( {row}, {col}) Number character at screen position screencol() Number current cursor column @@ -1984,11 +2053,12 @@ setbufvar( {expr}, {varname}, {val}) set {varname} in buffer {expr} to {val} setcharsearch( {dict}) Dict set character search from {dict} setcmdpos( {pos}) Number set cursor position in command-line setline( {lnum}, {line}) Number set line {lnum} to {line} -setloclist( {nr}, {list}[, {action}]) +setloclist( {nr}, {list}[, {action}[, {title}]]) Number modify location list using {list} setmatches( {list}) Number restore a list of matches setpos( {expr}, {list}) Number set the {expr} position to {list} -setqflist( {list}[, {action}]) Number modify quickfix list using {list} +setqflist( {list}[, {action}[, {title}]] + Number modify quickfix list using {list} setreg( {n}, {v}[, {opt}]) Number set register to value and type settabvar( {nr}, {varname}, {val}) set {varname} in tab page {nr} to {val} settabwinvar( {tabnr}, {winnr}, {varname}, {val}) set {varname} in window @@ -2013,7 +2083,7 @@ split( {expr} [, {pat} [, {keepempty}]]) sqrt( {expr}) Float square root of {expr} str2float( {expr}) Float convert String to Float str2nr( {expr} [, {base}]) Number convert String to Number -strchars( {expr}) Number character length of the String {expr} +strchars( {expr} [, {skipcc}]) Number character length of the String {expr} strdisplaywidth( {expr} [, {col}]) Number display length of the String {expr} strftime( {format}[, {time}]) String time in specified format stridx( {haystack}, {needle}[, {start}]) @@ -2070,6 +2140,7 @@ winrestcmd() String returns command to restore window sizes winrestview( {dict}) none restore view of current window winsaveview() Dict save view of current window winwidth( {nr}) Number width of window {nr} +wordcount() Dict get byte/char/word statistics writefile( {list}, {fname} [, {flags}]) Number write list of lines to file {fname} xor( {expr}, {expr}) Number bitwise XOR @@ -2161,6 +2232,50 @@ argv([{nr}]) The result is the {nr}th file in the argument list of the < Without the {nr} argument a |List| with the whole |arglist| is returned. + *assert_equal()* +assert_equal({expected}, {actual}, [, {msg}]) + When {expected} and {actual} are not equal an error message is + added to |v:errors|. + There is no automatic conversion, the String "4" is different + from the Number 4. And the number 4 is different from the + Float 4.0. The value of 'ignorecase' is not used here, case + always matters. + When {msg} is omitted an error in the form "Expected + {expected} but got {actual}" is produced. + Example: > + assert_equal('foo', 'bar') +< Will result in a string to be added to |v:errors|: + test.vim line 12: Expected 'foo' but got 'bar' ~ + +assert_exception({error} [, {msg}]) *assert_exception()* + When v:exception does not contain the string {error} an error + message is added to |v:errors|. + This can be used to assert that a command throws an exception. + Using the error number, followed by a colon, avoids problems + with translations: > + try + commandthatfails + call assert_false(1, 'command should have failed') + catch + call assert_exception('E492:') + endtry + +assert_false({actual} [, {msg}]) *assert_false()* + When {actual} is not false an error message is added to + |v:errors|, like with |assert_equal()|. + A value is false when it is zero or |v:false|. When "{actual}" + is not a number or |v:false| the assert fails. + When {msg} is omitted an error in the form "Expected False but + got {actual}" is produced. + +assert_true({actual} [, {msg}]) *assert_true()* + When {actual} is not true an error message is added to + |v:errors|, like with |assert_equal()|. + A value is true when it is a non-zero number or |v:true|. + When {actual} is not a number or |v:true| the assert fails. + When {msg} is omitted an error in the form "Expected True but + got {actual}" is produced. + asin({expr}) *asin()* Return the arc sine of {expr} measured in radians, as a |Float| in the range of [-pi/2, pi/2]. @@ -2415,6 +2530,10 @@ col({expr}) The result is a Number, which is the byte index of the column number of bytes in the cursor line plus one) 'x position of mark x (if the mark is not set, 0 is returned) + v In Visual mode: the start of the Visual area (the + cursor is the end). When not in Visual mode + returns the cursor position. Differs from |'<| in + that it's updated right away. Additionally {expr} can be [lnum, col]: a |List| with the line and column number. Most useful when the column is "$", to get the last column of a specific line. When "lnum" or "col" is @@ -2516,10 +2635,10 @@ confirm({msg} [, {choices} [, {default} [, {type}]]]) {default} is omitted, 1 is used. The optional {type} argument gives the type of dialog. This - is only used for the icon of the GTK, Mac, Motif and Win32 - GUI. It can be one of these values: "Error", "Question", - "Info", "Warning" or "Generic". Only the first character is - relevant. When {type} is omitted, "Generic" is used. + is only used for the icon of the Win32 GUI. It can be one of + these values: "Error", "Question", "Info", "Warning" or + "Generic". Only the first character is relevant. + When {type} is omitted, "Generic" is used. If the user aborts the dialog by pressing <Esc>, CTRL-C, or another valid interrupt key, confirm() returns 0. @@ -2667,13 +2786,19 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698* {noref} set to 1 will fail. Also see |copy()|. -delete({fname}) *delete()* - Deletes the file by the name {fname}. The result is a Number, - which is 0 if the file was deleted successfully, and non-zero - when the deletion failed. - Use |remove()| to delete an item from a |List|. - To delete a line from the buffer use |:delete|. Use |:exe| - when the line number is in a variable. +delete({fname} [, {flags}]) *delete()* + Without {flags} or with {flags} empty: Deletes the file by the + name {fname}. This also works when {fname} is a symbolic link. + A symbolic link itself is deleted, not what it points to. + + When {flags} is "d": Deletes the directory by the name + {fname}. This fails when directory {fname} is not empty. + + When {flags} is "rf": Deletes the directory by the name + {fname} and everything in it, recursively. BE CAREFUL! + + The result is a Number, which is 0 if the delete operation was + successful and -1 when the deletion failed or partly failed. dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()* Adds a watcher to a dictionary. A dictionary watcher is @@ -2747,9 +2872,8 @@ diff_hlID({lnum}, {col}) *diff_hlID()* empty({expr}) *empty()* Return the Number 1 if {expr} is empty, zero otherwise. A |List| or |Dictionary| is empty when it does not have any - items. A Number is empty when its value is zero. - For a long |List| this is much faster than comparing the - length with zero. + items. A Number is empty when its value is zero. Special + variable is empty when it is |v:false| or |v:null|. escape({string}, {chars}) *escape()* Escape the characters in {chars} that occur in {string} with a @@ -2778,7 +2902,7 @@ executable({expr}) *executable()* arguments. executable() uses the value of $PATH and/or the normal searchpath for programs. *PATHEXT* - On MS-DOS and MS-Windows the ".exe", ".bat", etc. can + On Windows the ".exe", ".bat", etc. can optionally be included. Then the extensions in $PATHEXT are tried. Thus if "foo.exe" does not exist, "foo.exe.bat" can be found. If $PATHEXT is not set then ".exe;.com;.bat;.cmd" is @@ -2786,9 +2910,9 @@ executable({expr}) *executable()* the name without an extension. When 'shell' looks like a Unix shell, then the name is also tried without adding an extension. - On MS-DOS and MS-Windows it only checks if the file exists and + On Windows it only checks if the file exists and is not a directory, not if it's really executable. - On MS-Windows an executable in the same directory as Vim is + On Windows an executable in the same directory as Vim is always found. Since this directory is added to $PATH it should also work to execute it |win32-PATH|. The result is a Number: @@ -3232,8 +3356,7 @@ foreground() Move the Vim window to the foreground. Useful when sent from On Win32 systems this might not work, the OS does not always allow a window to bring itself to the foreground. Use |remote_foreground()| instead. - {only in the Win32, Athena, Motif and GTK GUI versions and the - Win32 console version} + {only in the Win32 GUI and console version} function({name}) *function()* *E700* @@ -3364,7 +3487,7 @@ getchar([expr]) *getchar()* : endwhile :endfunction < - You may also receive syntetic characters, such as + You may also receive synthetic characters, such as |<CursorHold>|. Often you will want to ignore this and get another character: > :function GetKey() @@ -3458,9 +3581,18 @@ getcurpos() Get the position of the cursor. This is like getpos('.'), but MoveTheCursorAround call setpos('.', save_cursor) < - *getcwd()* -getcwd() The result is a String, which is the name of the current - working directory. +getcwd([{window}[, {tab}]]) *getcwd()* + With no arguments the result is a String, which is the name of + the current effective working directory. With {window} or + {tab} the working directory of that scope is returned. + Tabs and windows are identified by their respective numbers, + 0 means current tab or window. Missing argument implies 0. + Thus the following are equivalent: > + getcwd() + getcwd(0) + getcwd(0, 0) +< If {window} is -1 it is ignored, only the tab is resolved. + getfsize({fname}) *getfsize()* The result is a Number, which is the size in bytes of the @@ -3481,8 +3613,6 @@ getfontname([{name}]) *getfontname()* Only works when the GUI is running, thus not in your vimrc or gvimrc file. Use the |GUIEnter| autocommand to use this function just after the GUI has started. - Note that the GTK 2 GUI accepts any font name, thus checking - for a valid name does not work. getfperm({fname}) *getfperm()* The result is a String, which is the read, write, and execute @@ -3581,8 +3711,7 @@ getmatches() *getmatches()* < *getpid()* getpid() Return a Number which is the process ID of the Vim process. - On Unix and MS-Windows this is a unique number, until Vim - exits. On MS-DOS it's always zero. + This is a unique number, until Vim exits. *getpos()* getpos({expr}) Get the position for {expr}. For possible values of {expr} @@ -3708,7 +3837,7 @@ getwinvar({winnr}, {varname} [, {def}]) *getwinvar()* :let list_is_on = getwinvar(2, '&list') :echo "myvar = " . getwinvar(1, 'myvar') < -glob({expr} [, {nosuf} [, {list}]]) *glob()* +glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()* Expand the file wildcards in {expr}. See |wildcards| for the use of special characters. @@ -3725,8 +3854,11 @@ glob({expr} [, {nosuf} [, {list}]]) *glob()* matches, they are separated by <NL> characters. If the expansion fails, the result is an empty String or List. + A name for a non-existing file is not included. A symbolic link is only included if it points to an existing file. + However, when the {alllinks} argument is present and it is + non-zero then all symbolic links are included. For most systems backticks can be used to get files names from any external command. Example: > @@ -3745,8 +3877,11 @@ glob2regpat({expr}) *glob2regpat()* if filename =~ glob2regpat('Make*.mak') < This is equivalent to: > if filename =~ '^Make.*\.mak$' -< -globpath({path}, {expr} [, {nosuf} [, {list}]]) *globpath()* +< When {expr} is an empty string the result is "^$", match an + empty string. + + *globpath()* +globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]]) Perform glob() on all directories in {path} and concatenate the results. Example: > :echo globpath(&rtp, "syntax/c.vim") @@ -3772,6 +3907,8 @@ globpath({path}, {expr} [, {nosuf} [, {list}]]) *globpath()* they are separated by <NL> characters. Example: > :echo globpath(&rtp, "syntax/c.vim", 0, 1) < + {allinks} is used as with |glob()|. + The "**" item can be used to search in a directory tree. For example, to find all "README.txt" files in the directories in 'runtimepath' and below: > @@ -3790,9 +3927,18 @@ has_key({dict}, {key}) *has_key()* The result is a Number, which is 1 if |Dictionary| {dict} has an entry with key {key}. Zero otherwise. -haslocaldir() *haslocaldir()* - The result is a Number, which is 1 when the current - window has set a local path via |:lcd|, and 0 otherwise. +haslocaldir([{window}[, {tab}]]) *haslocaldir()* + The result is a Number, which is 1 when the specified tabpage + or window has a local path set via |:lcd| or |:tcd|, and + 0 otherwise. + + Tabs and windows are identified by their respective numbers, + 0 means current tab or window. Missing argument implies 0. + Thus the following are equivalent: > + haslocaldir() + haslocaldir(0) + haslocaldir(0, 0) +< If {window} is -1 it is ignored, only the tab is resolved. hasmapto({what} [, {mode} [, {abbr}]]) *hasmapto()* The result is a Number, which is 1 if there is a mapping that @@ -4114,6 +4260,9 @@ jobclose({job}[, {stream}]) {Nvim} *jobclose()* Close {job}'s {stream}, which can be one "stdin", "stdout" or "stderr". If {stream} is omitted, all streams are closed. +jobpid({job}) {Nvim} *jobpid()* + Return the pid (process id) of {job}. + jobresize({job}, {width}, {height}) {Nvim} *jobresize()* Resize {job}'s pseudo terminal window to {width} and {height}. This function will fail if used on jobs started without the @@ -4133,9 +4282,14 @@ jobsend({job}, {data}) {Nvim} *jobsend()* jobstart({cmd}[, {opts}]) {Nvim} *jobstart()* Spawns {cmd} as a job. If {cmd} is a |List|, it will be run - directly. If {cmd} is a |string|, it will be equivalent to > - :call jobstart([&shell, &shellcmdflag, '{cmd}']) -< If passed, {opts} must be a dictionary with any of the + directly. If {cmd} is a |string|, it will be roughly + equivalent to > + :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}']) +< NOTE: read |shell-unquoting| before constructing any lists + with 'shell' or 'shellcmdflag' options. The above call is + only written to show the idea, one needs to perform unquoting + and do split taking quotes into account. + If passed, {opts} must be a dictionary with any of the following keys: - on_stdout: stdout event handler - on_stderr: stderr event handler @@ -4146,6 +4300,10 @@ jobstart({cmd}[, {opts}]) {Nvim} *jobstart()* - width: Width of the terminal screen(only if pty is set) - height: Height of the terminal screen(only if pty is set) - TERM: $TERM environment variable(only if pty is set) + - detach: Detach the job process from the nvim process. The + process won't get killed when nvim exists. If the process + dies before nvim exits, on_exit will still be invoked. + This option is only allowed for non-pty jobs. Either funcrefs or function names can be passed as event handlers. The {opts} object is also used as the "self" argument for the callback, so the caller may pass arbitrary @@ -4196,6 +4354,46 @@ join({list} [, {sep}]) *join()* converted into a string like with |string()|. The opposite function is |split()|. +json_decode({expr}) *json_decode()* + Convert {expr} from JSON object. Accepts |readfile()|-style + list as the input, as well as regular string. May output any + Vim value. When 'encoding' is not UTF-8 string is converted + from UTF-8 to 'encoding', failing conversion fails + json_decode(). In the following cases it will output + |msgpack-special-dict|: + 1. Dictionary contains duplicate key. + 2. Dictionary contains empty key. + 3. String contains NUL byte. Two special dictionaries: for + dictionary and for string will be emitted in case string + with NUL byte was a dictionary key. + + Note: function treats its input as UTF-8 always regardless of + 'encoding' value. This is needed because JSON source is + supposed to be external (e.g. |readfile()|) and JSON standard + allows only a few encodings, of which UTF-8 is recommended and + the only one required to be supported. Non-UTF-8 characters + are an error. + +json_encode({expr}) *json_encode()* + Convert {expr} into a JSON string. Accepts + |msgpack-special-dict| as the input. Converts from 'encoding' + to UTF-8 when encoding strings. Will not convert |Funcref|s, + mappings with non-string keys (can be created as + |msgpack-special-dict|), values with self-referencing + containers, strings which contain non-UTF-8 characters, + pseudo-UTF-8 strings which contain codepoints reserved for + surrogate pairs (such strings are not valid UTF-8 strings). + When converting 'encoding' is taken into account, if it is not + "utf-8", then conversion is performed before encoding strings. + Non-printable characters are converted into "\u1234" escapes + or special escapes like "\t", other are dumped as-is. + + Note: all characters above U+0079 are considered non-printable + when 'encoding' is not UTF-8. This function always outputs + UTF-8 strings as required by the standard thus when 'encoding' + is not unicode resulting string will look incorrect if + "\u1234" notation is not used. + keys({dict}) *keys()* Return a |List| with all the keys of {dict}. The |List| is in arbitrary order. @@ -4507,7 +4705,7 @@ match({expr}, {pat}[, {start}[, {count}]]) *match()* done like 'magic' is set and 'cpoptions' is empty. *matchadd()* *E798* *E799* *E801* -matchadd({group}, {pattern}[, {priority}[, {id}]]) +matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]]) Defines a pattern to be highlighted in the current window (a "match"). It will be highlighted with {group}. Returns an identification number (ID), which can be used to delete the @@ -4515,6 +4713,8 @@ matchadd({group}, {pattern}[, {priority}[, {id}]]) Matching is case sensitive and magic, unless case sensitivity or magicness are explicitly overridden in {pattern}. The 'magic', 'smartcase' and 'ignorecase' options are not used. + The "Conceal" value is special, it causes the match to be + concealed. The optional {priority} argument assigns a priority to the match. A match with a high priority will have its @@ -4532,9 +4732,18 @@ matchadd({group}, {pattern}[, {priority}[, {id}]]) message will appear and the match will not be added. An ID is specified as a positive integer (zero excluded). IDs 1, 2 and 3 are reserved for |:match|, |:2match| and |:3match|, - respectively. If the {id} argument is not specified, + respectively. If the {id} argument is not specified or -1, |matchadd()| automatically chooses a free ID. + The optional {dict} argmument allows for further custom + values. Currently this is used to specify a match specifc + conceal character that will be shown for |hl-Conceal| + highlighted matches. The dict can have the following members: + + conceal Special character to show instead of the + match (only for |hl-Conceal| highlighed + matches, see |:syn-cchar|) + The number of matches is not limited, as it is the case with the |:match| commands. @@ -4548,7 +4757,7 @@ matchadd({group}, {pattern}[, {priority}[, {id}]]) available from |getmatches()|. All matches can be deleted in one operation by |clearmatches()|. -matchaddpos({group}, {pos}[, {priority}[, {id}]]) *matchaddpos()* +matchaddpos({group}, {pos}[, {priority}[, {id}[, {dict}]]]) *matchaddpos()* Same as |matchadd()|, but requires a list of positions {pos} instead of a pattern. This command is faster than |matchadd()| because it does not require to handle regular expressions and @@ -4712,7 +4921,7 @@ msgpackdump({list}) {Nvim} *msgpackdump()* (dictionary with zero items is represented by 0x80 byte in messagepack). - Limitations: + Limitations: *E951* *E952* *E953* 1. |Funcref|s cannot be dumped. 2. Containers that reference themselves cannot be dumped. 3. Dictionary keys are always dumped as STR strings. @@ -4747,9 +4956,13 @@ msgpackparse({list}) {Nvim} *msgpackparse()* contains name of the key from |v:msgpack_types|): Key Value ~ - nil Zero, ignored when dumping. - boolean One or zero. When dumping it is only checked that - value is a |Number|. + nil Zero, ignored when dumping. This value cannot + possibly appear in |msgpackparse()| output in Neovim + versions which have |v:null|. + boolean One or zero. When dumping it is only checked that + value is a |Number|. This value cannot possibly + appear in |msgpackparse()| output in Neovim versions + which have |v:true| and |v:false|. integer |List| with four numbers: sign (-1 or 1), highest two bits, number with bits from 62nd to 31st, lowest 31 bits. I.e. to get actual number one will need to use @@ -4863,6 +5076,9 @@ printf({fmt}, {expr1} ...) *printf()* %c single byte %d decimal number %5d decimal number padded with spaces to 5 characters + %b binary number + %08b binary number padded with zeros to at least 8 characters + %B binary number using upper case letters %x hex number %04x hex number padded with zeros to at least 4 characters %X hex number using upper case letters @@ -4949,20 +5165,19 @@ printf({fmt}, {expr1} ...) *printf()* The conversion specifiers and their meanings are: - *printf-d* *printf-o* *printf-x* *printf-X* - doxX The Number argument is converted to signed decimal - (d), unsigned octal (o), or unsigned hexadecimal (x - and X) notation. The letters "abcdef" are used for - x conversions; the letters "ABCDEF" are used for X - conversions. - The precision, if any, gives the minimum number of - digits that must appear; if the converted value - requires fewer digits, it is padded on the left with - zeros. - In no case does a non-existent or small field width - cause truncation of a numeric field; if the result of - a conversion is wider than the field width, the field - is expanded to contain the conversion result. + *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X* + dbBoxX The Number argument is converted to signed decimal (d), + unsigned binary (b and B), unsigned octal (o), or + unsigned hexadecimal (x and X) notation. The letters + "abcdef" are used for x conversions; the letters + "ABCDEF" are used for X conversions. The precision, if + any, gives the minimum number of digits that must + appear; if the converted value requires fewer digits, it + is padded on the left with zeros. In no case does a + non-existent or small field width cause truncation of a + numeric field; if the result of a conversion is wider + than the field width, the field is expanded to contain + the conversion result. *printf-c* c The Number argument is converted to a byte, and the @@ -4972,6 +5187,7 @@ printf({fmt}, {expr1} ...) *printf()* s The text of the String argument is used. If a precision is specified, no more bytes than the number specified are used. + *printf-S* S The text of the String argument is used. If a precision is specified, no more display cells than the number specified are used. Without the |+multi_byte| @@ -5033,7 +5249,7 @@ py3eval({expr}) *py3eval()* Evaluate Python expression {expr} and return its result converted to Vim data structures. Numbers and strings are returned as they are (strings are - copied though, unicode strings are additionally converted to + copied though, Unicode strings are additionally converted to 'encoding'). Lists are represented as Vim |List| type. Dictionaries are represented as Vim |Dictionary| type with @@ -5077,7 +5293,7 @@ readfile({fname} [, {binary} [, {max}]]) separated with CR will result in a single long line (unless a NL appears somewhere). All NUL characters are replaced with a NL character. - When {binary/append} contains "b" binary mode is used: + When {binary} contains "b" binary mode is used: - When the last line ends in a NL an extra empty list item is added. - No CR characters are removed. @@ -5105,7 +5321,8 @@ readfile({fname} [, {binary} [, {max}]]) reltime([{start} [, {end}]]) *reltime()* Return an item that represents a time value. The format of the item depends on the system. It can be passed to - |reltimestr()| to convert it to a string. + |reltimestr()| to convert it to a string or |reltimefloat()| + to convert to a float. Without an argument it returns the current time. With one argument is returns the time passed since the time specified in the argument. @@ -5113,7 +5330,16 @@ reltime([{start} [, {end}]]) *reltime()* and {end}. The {start} and {end} arguments must be values returned by reltime(). - {only available when compiled with the |+reltime| feature} + +reltimefloat({time}) *reltimefloat()* + Return a Float that represents the time value of {time}. + Unit of time is seconds. + Example: + let start = reltime() + call MyFunction() + let seconds = reltimefloat(reltime(start)) + See the note of reltimestr() about overhead. + Also see |profiling|. reltimestr({time}) *reltimestr()* Return a String that represents the time value of {time}. @@ -5123,12 +5349,10 @@ reltimestr({time}) *reltimestr()* call MyFunction() echo reltimestr(reltime(start)) < Note that overhead for the commands will be added to the time. - The accuracy depends on the system. Leading spaces are used to make the string align nicely. You can use split() to remove it. > echo split(reltimestr(reltime(start)))[0] < Also see |profiling|. - {only available when compiled with the |+reltime| feature} *remote_expr()* *E449* remote_expr({server}, {string} [, {idvar}]) @@ -5160,8 +5384,7 @@ remote_foreground({server}) *remote_foreground()* Note: This does not restore the window if it was minimized, like foreground() does. This function is not available in the |sandbox|. - {only in the Win32, Athena, Motif and GTK GUI versions and the - Win32 console version} + {only in the Win32 GUI and the Win32 console version} remote_peek({serverid} [, {retvar}]) *remote_peek()* @@ -5349,14 +5572,15 @@ search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* move. No error message is given. {flags} is a String, which can contain these character flags: - 'b' search backward instead of forward - 'c' accept a match at the cursor position + 'b' search Backward instead of forward + 'c' accept a match at the Cursor position 'e' move to the End of the match 'n' do Not move the cursor - 'p' return number of matching sub-pattern (see below) - 's' set the ' mark at the previous location of the cursor - 'w' wrap around the end of the file - 'W' don't wrap around the end of the file + 'p' return number of matching sub-Pattern (see below) + 's' Set the ' mark at the previous location of the cursor + 'w' Wrap around the end of the file + 'W' don't Wrap around the end of the file + 'z' start searching at the cursor column instead of Zero If neither 'w' or 'W' is given, the 'wrapscan' option applies. If the 's' flag is supplied, the ' mark is set, only if the @@ -5364,6 +5588,12 @@ search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* flag. 'ignorecase', 'smartcase' and 'magic' are used. + + When the 'z' flag is not given seaching always starts in + column zero and then matches before the cursor are skipped. + When the 'c' flag is present in 'cpo' the next search starts + after the match. Without the 'c' flag the next search starts + one column further. When the {stopline} argument is given then the search stops after searching this line. This is useful to restrict the @@ -5602,7 +5832,7 @@ setbufvar({expr}, {varname}, {val}) *setbufvar()* :call setbufvar("todo", "myvar", "foobar") < This function is not available in the |sandbox|. -setcharsearch() *setcharsearch()* +setcharsearch({dict}) *setcharsearch()* Set the current character search information to {dict}, which contains one or more of the following entries: @@ -5655,11 +5885,13 @@ setline({lnum}, {text}) *setline()* :endfor < Note: The '[ and '] marks are not set. -setloclist({nr}, {list} [, {action}]) *setloclist()* +setloclist({nr}, {list} [, {action}[, {title}]]) *setloclist()* Create or replace or add to the location list for window {nr}. When {nr} is zero the current window is used. For a location list window, the displayed location list is modified. For an - invalid window number {nr}, -1 is returned. + invalid window number {nr}, -1 is returned. If {title} is + given, it will be used to set |w:quickfix_title| after opening + the location window. Otherwise, same as |setqflist()|. Also see |location-list|. @@ -5716,7 +5948,7 @@ setpos({expr}, {list}) |winrestview()|. -setqflist({list} [, {action}]) *setqflist()* +setqflist({list} [, {action}[, {title}]]) *setqflist()* Create or replace or add to the quickfix list using the items in {list}. Each item in {list} is a dictionary. Non-dictionary items in {list} are ignored. Each dictionary @@ -5755,6 +5987,9 @@ setqflist({list} [, {action}]) *setqflist()* with the items from {list}. If {action} is not present or is set to ' ', then a new list is created. + If {title} is given, it will be used to set |w:quickfix_title| + after opening the quickfix window. + Returns zero for success, -1 for failure. This function can be used to create a quickfix list @@ -5837,12 +6072,12 @@ setwinvar({nr}, {varname}, {val}) *setwinvar()* :call setwinvar(2, "myvar", "foobar") sha256({string}) *sha256()* - Returns a String with 64 hex charactes, which is the SHA256 + Returns a String with 64 hex characters, which is the SHA256 checksum of {string}. shellescape({string} [, {special}]) *shellescape()* Escape {string} for use as a shell command argument. - On MS-Windows and MS-DOS, when 'shellslash' is not set, it + On Windows when 'shellslash' is not set, it will enclose {string} in double quotes and double all double quotes within {string}. For other systems, it will enclose {string} in single quotes @@ -5940,6 +6175,10 @@ sort({list} [, {func} [, {dict}]]) *sort()* *E702* strtod() function to parse numbers, Strings, Lists, Dicts and Funcrefs will be considered as being 0). + When {func} is given and it is 'N' then all items will be + sorted numerical. This is like 'n' but a string containing + digits will be used as the number they represent. + When {func} is a |Funcref| or a function name, this function is called to compare items. The function is invoked with two items as argument and must return zero if they are equal, 1 or @@ -6041,7 +6280,8 @@ split({expr} [, {pattern} [, {keepempty}]]) *split()* :let words = split(getline('.'), '\W\+') < To split a string in individual characters: > :for c in split(mystring, '\zs') -< If you want to keep the separator you can also use '\zs': > +< If you want to keep the separator you can also use '\zs' at + the end of the pattern: > :echo split('abc:def:ghi', ':\zs') < ['abc:', 'def:', 'ghi'] ~ Splitting a table where the first element can be empty: > @@ -6078,24 +6318,46 @@ str2float( {expr}) *str2float()* str2nr( {expr} [, {base}]) *str2nr()* Convert string {expr} to a number. - {base} is the conversion base, it can be 8, 10 or 16. + {base} is the conversion base, it can be 2, 8, 10 or 16. When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as with the default String to Number conversion. When {base} is 16 a leading "0x" or "0X" is ignored. With a - different base the result will be zero. + different base the result will be zero. Similarly, when {base} + is 8 a leading "0" is ignored, and when {base} is 2 a leading + "0b" or "0B" is ignored. Text after the number is silently ignored. -strchars({expr}) *strchars()* +strchars({expr} [, {skipcc}]) *strchars()* The result is a Number, which is the number of characters - String {expr} occupies. Composing characters are counted - separately. + in String {expr}. + When {skipcc} is omitted or zero, composing characters are + counted separately. + When {skipcc} set to 1, Composing characters are ignored. Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. + + {skipcc} is only available after 7.4.755. For backward + compatibility, you can define a wrapper function: > + if has("patch-7.4.755") + function s:strchars(str, skipcc) + return strchars(a:str, a:skipcc) + endfunction + else + function s:strchars(str, skipcc) + if a:skipcc + return strlen(substitute(a:str, ".", "x", "g")) + else + return strchars(a:str) + endif + endfunction + endif +< + strdisplaywidth({expr}[, {col}]) *strdisplaywidth()* The result is a Number, which is the number of display cells - String {expr} occupies on the screen when it starts a {col}. + String {expr} occupies on the screen when it starts at {col}. When {col} is omitted zero is used. Otherwise it is the screen column where to start. This matters for Tab characters. @@ -6151,25 +6413,31 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number, {expr} type result ~ String 'string' Number 123 - Float 123.123456 or 1.123456e8 - Funcref function('name') + Float 123.123456 or 1.123456e8 or + `str2float('inf')` + Funcref `function('name')` List [item, item] Dictionary {key: value, key: value} Note that in String values the ' character is doubled. Also see |strtrans()|. + Note 2: Output format is mostly compatible with YAML, except + for infinite and NaN floating-point values representations + which use |str2float()|. Strings are also dumped literally, + only single quote is escaped, which does not allow using YAML + for parsing back binary strings (including text when + 'encoding' is not UTF-8). |eval()| should always work for + strings and floats though and this is the only official + method, use |msgpackdump()| or |json_encode()| if you need to + share data with other application. *strlen()* strlen({expr}) The result is a Number, which is the length of the String {expr} in bytes. - If you want to count the number of multi-byte characters (not - counting composing characters) use something like this: > - - :let len = strlen(substitute(str, ".", "x", "g")) -< If the argument is a Number it is first converted to a String. For other types an error is given. - Also see |len()|, |strchars()|, |strdisplaywidth()| and - |strwidth()|. + If you want to count the number of multi-byte characters use + |strchars()|. + Also see |len()|, |strdisplaywidth()| and |strwidth()|. strpart({src}, {start}[, {len}]) *strpart()* The result is a String, which is part of {src}, starting from @@ -6283,6 +6551,9 @@ synID({lnum}, {col}, {trans}) *synID()* {col} is 1 for the leftmost column, {lnum} is 1 for the first line. 'synmaxcol' applies, in a longer line zero is returned. + Note that when the position is after the last character, + that's where the cursor can be in Insert mode, synID() returns + zero. When {trans} is non-zero, transparent items are reduced to the item that they reveal. This is useful when wanting to know @@ -6588,12 +6859,14 @@ trunc({expr}) *trunc()* type({expr}) *type()* The result is a Number, depending on the type of {expr}: - Number: 0 - String: 1 + Number: 0 + String: 1 Funcref: 2 - List: 3 + List: 3 Dictionary: 4 - Float: 5 + Float: 5 + Boolean: 6 (|v:true| and |v:false|) + Null: 7 (|v:null|) To avoid the magic numbers it should be used this way: > :if type(myvar) == type(0) :if type(myvar) == type("") @@ -6601,6 +6874,10 @@ type({expr}) *type()* :if type(myvar) == type([]) :if type(myvar) == type({}) :if type(myvar) == type(0.0) + :if type(myvar) == type(v:true) +< In place of checking for |v:null| type it is better to check + for |v:null| directly as it is the only value of this type: > + :if myvar is v:null undofile({name}) *undofile()* Return the name of the undo file that would be used for a file @@ -6694,6 +6971,10 @@ virtcol({expr}) *virtcol()* plus one) 'x position of mark x (if the mark is not set, 0 is returned) + v In Visual mode: the start of the Visual area (the + cursor is the end). When not in Visual mode + returns the cursor position. Differs from |'<| in + that it's updated right away. Note that only marks in the current file can be used. Examples: > virtcol(".") with text "foo^Lbar", with cursor on the "^L", returns 5 @@ -6842,6 +7123,28 @@ winwidth({nr}) *winwidth()* : exe "normal 50\<C-W>|" :endif < +wordcount() *wordcount()* + The result is a dictionary of byte/chars/word statistics for + the current buffer. This is the same info as provided by + |g_CTRL-G| + The return value includes: + bytes Number of bytes in the buffer + chars Number of chars in the buffer + words Number of words in the buffer + cursor_bytes Number of bytes before cursor position + (not in Visual mode) + cursor_chars Number of chars before cursor position + (not in Visual mode) + cursor_words Number of words before cursor position + (not in Visual mode) + visual_bytes Number of bytes visually selected + (only in Visual mode) + visual_chars Number of chars visually selected + (only in Visual mode) + visual_words Number of chars visually selected + (only in Visual mode) + + *writefile()* writefile({list}, {fname} [, {flags}]) Write |List| {list} to file {fname}. Each list item is @@ -6906,8 +7209,6 @@ There are four types of features: acl Compiled with |ACL| support. arabic Compiled with Arabic support |Arabic|. autocmd Compiled with autocommand support. |autocommand| -balloon_eval Compiled with |balloon-eval| support. -balloon_multiline GUI supports multiline balloons. browse Compiled with |:browse| support, and browse() will work. browsefilter Compiled with support for |browsefilter|. @@ -6925,7 +7226,6 @@ debug Compiled with "DEBUG" defined. dialog_con Compiled with console dialog support. dialog_gui Compiled with GUI dialog support. digraphs Compiled with support for digraphs. -dnd Compiled with support for the "~ register |quote_~|. eval Compiled with expression evaluation support. Always true, of course! ex_extra Compiled with extra Ex commands |+ex_extra|. @@ -6938,21 +7238,13 @@ filterpipe When 'shelltemp' is off pipes are used for shell find_in_path Compiled with support for include file searches |+find_in_path|. float Compiled with support for |Float|. -fname_case Case in file names matters (for MS-DOS and Windows - this is not present). +fname_case Case in file names matters (for Windows this is not + present). folding Compiled with |folding| support. -footer Compiled with GUI footer support. |gui-footer| gettext Compiled with message translation |multi-lang| gui Compiled with GUI enabled. -gui_athena Compiled with Athena GUI. -gui_gnome Compiled with Gnome support (gui_gtk is also defined). -gui_gtk Compiled with GTK+ GUI (any version). -gui_gtk2 Compiled with GTK+ 2 GUI (gui_gtk is also defined). -gui_mac Compiled with Macintosh GUI. -gui_motif Compiled with Motif GUI. gui_running Vim is running in the GUI, or it will start soon. gui_win32 Compiled with MS Windows Win32 GUI. -gui_win32s idem, and Win32s system being used (Windows 3.1) iconv Can use iconv() for conversion. insert_expand Compiled with support for CTRL-X expansion commands in Insert mode. @@ -7000,6 +7292,7 @@ statusline Compiled with support for 'statusline', 'rulerformat' syntax Compiled with syntax highlighting support |syntax|. syntax_items There are active syntax highlighting items for the current buffer. +tablineat 'tabline' option accepts %@Func@ items. tag_binary Compiled with binary searching in tags files |tag-binary-search|. tag_old_static Compiled with support for old static tags @@ -7024,10 +7317,9 @@ visualextra Compiled with extra Visual mode commands. vreplace Compiled with |gR| and |gr| commands. wildignore Compiled with 'wildignore' option. wildmenu Compiled with 'wildmenu' option. -win32 Win32 version of Vim (MS-Windows 95 and later, 32 or - 64 bits) -win32unix Win32 version of Vim, using Unix files (Cygwin) -win64 Win64 version of Vim (MS-Windows 64 bit). +win32 Windows version of Vim (32 or 64 bit). +win32unix Windows version of Vim, using Unix files (Cygwin). +win64 Windows version of Vim (64 bit). winaltkeys Compiled with 'winaltkeys' option. windows Compiled with support for more than one window. writebackup Compiled with 'writebackup' default on. @@ -7596,7 +7888,7 @@ This does NOT work: > :unlet v < *E741* If you try to change a locked variable you get an - error message: "E741: Value of {name} is locked" + error message: "E741: Value is locked: {name}" [depth] is relevant when locking a |List| or |Dictionary|. It specifies how deep the locking goes: @@ -8513,7 +8805,7 @@ You can catch all Vim errors by the pattern > *catch-text* NOTE: You should never catch the error message text itself: > :catch /No such variable/ -only works in the english locale, but not when the user has selected +only works in the English locale, but not when the user has selected a different language by the |:language| command. It is however helpful to cite the message text in a comment: > :catch /^Vim(\a\+):E108:/ " No such variable diff --git a/runtime/doc/farsi.txt b/runtime/doc/farsi.txt index d5b371ab40..b85c0a357c 100644 --- a/runtime/doc/farsi.txt +++ b/runtime/doc/farsi.txt @@ -53,60 +53,6 @@ o Toggling between Farsi ISIR-3342 standard encoding and Vim Farsi via F9 right-to-left mode, this function is also supported only in right-to-left mode. -Farsi Fonts *farsi fonts* ------------ - -The following files are found in the subdirectories of the '$VIM/farsi/fonts' -directory: - - + far-a01.pcf X Windows fonts for Unix including Linux systems - + far-a01.bf X Windows fonts for SunOS - + far-a01.f16 a screen fonts for Unix including Linux systems - + far-a01.fon a monospaced fonts for Windows NT/95/98 - + far-a01.com a screen fonts for DOS - - -Font Installation ------------------ - -o Installation of fonts for MS Window systems (NT/95/98) - - From 'Control Panel' folder, start the 'Fonts' program. Then from 'file' - menu item select 'Install New Fonts ...'. Browse and select the - 'far-a01.fon', then follow the installation guide. - NOTE: several people have reported that this does not work. The solution - is unknown. - -o Installation of fonts for X Window systems (Unix/Linux) - - Depending on your system, copy far-a01.pcf.Z or far-a01.pcf.gz into a - directory of your choice. Change to the directory containing the Farsi - fonts and execute the following commands: - - > mkfontdir - > xset +fp path_name_of_farsi_fonts_directory - -o Installation of fonts for X Window systems (SunOS) - - Copy far-a01.bf font into a directory of your choice. - Change to the directory containing the far-a01.fb fonts and - execute the following commands: - - > fldfamily - > xset +fp path_name_of_fonts_directory - -o Installation of ASCII screen fonts (Unix/Linux) - - For Linux system, copy the far-a01.f16 fonts into /usr/lib/kbd/consolefonts - directory and execute the setfont program as "setfont far-a01.f16". For - other systems (e.g. SCO Unix), please refer to the fonts installation - section of your system administration manuals. - -o Installation of ASCII screen fonts (DOS) - - After system power on, prior to the first use of Vim, upload the Farsi - fonts by executing the far-a01.com font uploading program. - Usage ----- @@ -165,7 +111,7 @@ The letter encoding used is the Vim extended ISIR-3342 standard with a built in function to convert between Vim extended ISIR-3342 and ISIR-3342 standard. For document portability reasons, the letter encoding is kept the same across -different platforms (i.e. UNIX's, NT/95/98, MS DOS, ...). +different platforms (i.e. Unix, Windows, ...). o Keyboard @@ -215,7 +161,7 @@ o Keyboard Note: ¡ stands for Farsi PSP (break without space) - ¢ stands for Farsi PCN (for HAMZE attribute ) + ¢ stands for Farsi PCN (for HAMZE attribute) Restrictions ------------ diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index baf7550948..76aa3a50ce 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 7.4. Last change: 2013 Dec 15 +*filetype.txt* For Vim version 7.4. Last change: 2015 Dec 06 VIM REFERENCE MANUAL by Bram Moolenaar @@ -510,7 +510,7 @@ Local mappings: to the end of the file in Normal mode. This means "> " is inserted in each line. -MAN *ft-man-plugin* *:Man* +MAN *ft-man-plugin* *:Man* *man.vim* Displays a manual page in a nice way. Also see the user manual |find-manpage|. @@ -535,6 +535,13 @@ Global mapping: Local mappings: CTRL-] Jump to the manual page for the word under the cursor. CTRL-T Jump back to the previous manual page. +q Same as ":quit" + +To enable folding use this: > + let g:ft_man_folding_enable = 1 +If you do not like the default folding, use an autocommand to add your desired +folding style instead. For example: > + autocmd FileType man setlocal foldmethod=indent foldenable PDF *ft-pdf-plugin* @@ -595,7 +602,7 @@ Format description: ignored. 2. Each entry starts with line that has format "{type} with timestamp {timestamp}:". {timestamp} is |strftime()|-formatted string representing - actual UNIX timestamp value. First strftime() argument is equal to + actual Unix timestamp value. First strftime() argument is equal to `%Y-%m-%dT%H:%M:%S`. When writing this timestamp is parsed using |msgpack#strptime()|, with caching (it remembers which timestamp produced particular strftime() output and uses this value if you did not change diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt index 03dd6a61ba..680e3270f2 100644 --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -1,4 +1,4 @@ -*fold.txt* For Vim version 7.4. Last change: 2013 Dec 04 +*fold.txt* For Vim version 7.4. Last change: 2016 Jan 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -94,9 +94,9 @@ These are the conditions with which the expression is evaluated: lowest. "=" use fold level from the previous line "a1", "a2", .. add one, two, .. to the fold level of the previous - line + line, use the result for the current line "s1", "s2", .. subtract one, two, .. from the fold level of the - previous line + previous line, use the result for the next line "<1", "<2", .. a fold with this level ends at this line ">1", ">2", .. a fold with this level starts at this line @@ -119,6 +119,18 @@ method can be very slow! Try to avoid the "=", "a" and "s" return values, since Vim often has to search backwards for a line for which the fold level is defined. This can be slow. +An example of using "a1" and "s1": For a multi-line C comment, a line +containing "/*" would return "a1" to start a fold, and a line containing "*/" +would return "s1" to end the fold after that line: > + if match(thisline, '/\*') >= 0 + return 'a1' + elseif match(thisline, '\*/') >= 0 + return 's1' + else + return '=' + endif +However, this won't work for single line comments, strings, etc. + |foldlevel()| can be useful to compute a fold level relative to a previous fold level. But note that foldlevel() may return -1 if the level is not known yet. And it returns the level at the start of the line, while a fold might @@ -570,8 +582,9 @@ what you type! When using an operator, a closed fold is included as a whole. Thus "dl" deletes the whole closed fold under the cursor. -For Ex commands the range is adjusted to always start at the first line of a -closed fold and end at the last line of a closed fold. Thus this command: > +For Ex commands that work on buffer lines the range is adjusted to always +start at the first line of a closed fold and end at the last line of a closed +fold. Thus this command: > :s/foo/bar/g when used with the cursor on a closed fold, will replace "foo" with "bar" in all lines of the fold. diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index c74c31d1fa..8d97678af2 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -208,18 +208,6 @@ takes too much time or you don't like the cursor jumping to another line, include the 'h' flag in 'guioptions'. Then the scrolling is limited by the text of the current cursor line. - *athena-intellimouse* -If you have an Intellimouse and an X server that supports using the wheel, -then you can use the wheel to scroll the text up and down in gvim. This works -with XFree86 4.0 and later, and with some older versions when you add patches. -See |scroll-mouse-wheel|. - -For older versions of XFree86 you must patch your X server. The following -page has a bit of information about using the Intellimouse on Linux as well as -links to the patches and X server binaries (may not have the one you need -though): - http://www.inria.fr/koala/colas/mouse-wheel-scroll/ - ============================================================================== 3. Mouse Control *gui-mouse* @@ -605,10 +593,6 @@ The default menus have these priorities: When no or zero priority is given, 500 is used. The priority for the PopUp menu is not used. -The Help menu will be placed on the far right side of the menu bar on systems -which support this (Motif and GTK+). For GTK+ 2, this is not done anymore -because right-aligning the Help menu is now discouraged UI design. - You can use a priority higher than 9999, to make it go after the Help menu, but that is non-standard and is discouraged. The highest possible priority is about 32000. The lowest is 1. @@ -677,10 +661,8 @@ level. Vim interprets the items in this menu as follows: toolbar button image. Note that the exact filename is OS-specific: For example, under Win32 the command > :amenu ToolBar.Hello :echo "hello"<CR> -< would find the file 'hello.bmp'. Under GTK+/X11 it is 'Hello.xpm'. With - GTK+ 2 the files 'Hello.png', 'Hello.xpm' and 'Hello.bmp' are checked for - existence, and the first one found would be used. - For MS-Windows and GTK+ 2 the bitmap is scaled to fit the button. For +< would find the file 'hello.bmp'. Under X11 it is 'Hello.xpm'. + For MS-Windows and the bitmap is scaled to fit the button. For MS-Windows a size of 18 by 18 pixels works best. For MS-Windows the bitmap should have 16 colors with the standard palette. The light grey pixels will be changed to the Window frame color and the @@ -729,8 +711,8 @@ nr Name Normal action ~ 30 WinMinWidth make current window use few columns *hidden-menus* *win32-hidden-menus* -In the Win32 and GTK+ GUI, starting a menu name with ']' excludes that menu -from the main menu bar. You must then use the |:popup| command to display it. +In the Win32 GUI, starting a menu name with ']' excludes that menu from the +main menu bar. You must then use the |:popup| command to display it. *popup-menu* You can define the special menu "PopUp". This is the menu that is displayed @@ -883,9 +865,8 @@ a menu item - you don't need to do a :tunmenu as well. 5.9 Popup Menus -In the Win32 and GTK+ GUI, you can cause a menu to popup at the cursor. -This behaves similarly to the PopUp menus except that any menu tree can -be popped up. +In the Win32 GUI, you can cause a menu to popup at the cursor. This behaves +similarly to the PopUp menus except that any menu tree can be popped up. This command is for backwards compatibility, using it is discouraged, because it behaves in a strange way. @@ -894,7 +875,7 @@ it behaves in a strange way. :popu[p] {name} Popup the menu {name}. The menu named must have at least one subentry, but need not appear on the menu-bar (see |hidden-menus|). - {only available for Win32 and GTK GUI} + {only available for Win32 GUI} :popu[p]! {name} Like above, but use the position of the mouse pointer instead of the cursor. diff --git a/runtime/doc/gui_w32.txt b/runtime/doc/gui_w32.txt index eef0aaefb6..228be9eab2 100644 --- a/runtime/doc/gui_w32.txt +++ b/runtime/doc/gui_w32.txt @@ -47,10 +47,6 @@ If you want Vim to start with a maximized window, add this command to your vimrc or gvimrc file: > au GUIEnter * simalt ~x < - *gui-w32s* -There is a specific version of gvim.exe that runs under the Win32s subsystem -of Windows 3.1 or 3.11. See |win32s|. - ============================================================================== 2. Vim as default editor *vim-default-editor* @@ -88,7 +84,6 @@ when you have got a new version): You can also install Vim in the "Send To" menu: 1. Start a Windows Explorer 2. Navigate to your sendto directory: - Windows 95: %windir%\sendto (e.g. "c:\windows\sendto") Windows NT: %windir%\profiles\%user%\sendto (e.g. "c:\winnt\profiles\mattha\sendto"). 3. Right-click in the file pane and select New->Shortcut @@ -262,17 +257,6 @@ WARNING: If you close this window with the "X" button, and confirm the question if you really want to kill the application, Vim may be killed too! (This does not apply to commands run asynchronously with ":!start".) - *msdos-mode* -If you get a dialog that says "This program is set to run in MS-DOS mode..." -when you run an external program, you can solve this by changing the -properties of the associated shortcut: -- Use a Windows Explorer to find the command.com that is used. It can be - c:\command.com, c:\dos\command.com, c:\windows\command.com, etc. -- With the right mouse button, select properties of this command.com. -- In the Program tab select "Advanced". -- Unselect "MS-DOS mode". -- Click "OK" twice. - *win32-!start* Normally, Vim waits for a command to complete before continuing (this makes sense for most shell commands which produce output for Vim to use). If you @@ -281,10 +265,6 @@ syntax on W95 & NT: > :!start [/min] {command} The optional "/min" causes the window to be minimized. -On Win32s, you will have to go to another window instead. Don't forget that -you must tell Windows 3.1x to keep executing a DOS command in the background -while you switch back to Vim. - ============================================================================== 5. Special colors *win32-colors* @@ -293,7 +273,7 @@ On Win32, the normal DOS colors can be used. See |dos-colors|. Additionally the system configured colors can also be used. These are known by the names Sys_XXX, where XXX is the appropriate system color name, from the following list (see the Win32 documentation for full descriptions). Case is -ignored. Note: On Win32s not all of these colors are supported. +ignored. Sys_3DDKShadow Sys_3DFace Sys_BTNFace Sys_3DHilight Sys_3DHighlight Sys_BTNHilight @@ -405,7 +385,7 @@ detailed elsewhere: see |'mouse'|, |win32-hidden-menus|. You can drag and drop one or more files into the Vim window, where they will be opened as normal. See |drag-n-drop|. - *:simalt* *:si* + *:simalt* *:sim* :sim[alt] {key} simulate pressing {key} while holding Alt pressed. {only for Win32 versions} diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 766a440cb3..19bcb35da8 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -1,4 +1,4 @@ -*help.txt* For Vim version 7.4. Last change: 2012 Dec 06 +*help.txt* For Vim version 7.4. Last change: 2015 Apr 15 VIM - main help file k @@ -23,6 +23,7 @@ Get specific help: It is possible to go directly to whatever you want help Command-line editing c_ :help c_<Del> Vim command argument - :help -r Option ' :help 'textwidth' + Regular expression / :help /[ Search for help: Type ":help word", then hit CTRL-D to see matching help entries for "word". Or use ":helpgrep word". |:helpgrep| diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index dea7dded95..f3533b8815 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -184,12 +184,6 @@ command: > :!xterm -e vim +help & < - *:helpfind* *:helpf* -:helpf[ind] Like |:help|, but use a dialog to enter the argument. - Only for backwards compatibility. It now executes the - ToolBar.FindHelp menu entry instead of using a builtin - dialog. {only when compiled with |+GUI_GTK|} - *:helpt* *:helptags* *E154* *E150* *E151* *E152* *E153* *E670* :helpt[ags] [++t] {dir} diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 75c986efcd..e6c1ccc0cf 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1,4 +1,4 @@ -*index.txt* For Vim version 7.4. Last change: 2015 Jan 07 +*index.txt* For Vim version 7.4. Last change: 2016 Jan 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -700,12 +700,16 @@ tag char note action in Normal mode ~ |g'| g'{mark} 1 like |'| but without changing the jumplist |g`| g`{mark} 1 like |`| but without changing the jumplist |gstar| g* 1 like "*", but without using "\<" and "\>" +|g+| g+ go to newer text state N times +|g,| g, 1 go to N newer position in change list +|g-| g- go to older text state N times |g0| g0 1 when 'wrap' off go to leftmost character of the current line that is on the screen; when 'wrap' on go to the leftmost character of the current screen line |g8| g8 print hex value of bytes used in UTF-8 character under the cursor +|g;| g; 1 go to N older position in change list |g<| g< display previous command output |g?| g? 2 Rot13 encoding operator |g?g?| g?? 2 Rot13 encode current line @@ -734,6 +738,7 @@ tag char note action in Normal mode ~ the screen; when 'wrap' on go to the leftmost non-white character of the current screen line +|g_| g_ 1 cursor to the last CHAR N - 1 lines lower |ga| ga print ascii value of character under the cursor |gd| gd 1 go to definition of word under the cursor @@ -1128,6 +1133,8 @@ tag command action ~ |:cc| :cc go to specific error |:cclose| :ccl[ose] close quickfix window |:cd| :cd change directory +|:cdo| :cdo execute command in each valid error list entry +|:cfdo| :cfdo execute command in each file in error list |:center| :ce[nter] format lines at the center |:cexpr| :cex[pr] read errors from expr and jump to first |:cfile| :cf[ile] read file with error messages and jump to first @@ -1239,7 +1246,6 @@ tag command action ~ |:hardcopy| :ha[rdcopy] send text to the printer |:help| :h[elp] open a help window |:helpclose| :helpc[lose] close one help window -|:helpfind| :helpf[ind] dialog to open a help window |:helpgrep| :helpg[rep] like ":grep" but searches help files |:helptags| :helpt[ags] generate help tags for a directory |:highlight| :hi[ghlight] specify highlighting methods @@ -1285,6 +1291,8 @@ tag command action ~ |:lchdir| :lch[dir] change directory locally |:lclose| :lcl[ose] close location window |:lcscope| :lcs[cope] like ":cscope" but uses location list +|:ldo| :ld[o] execute command in valid location list entries +|:lfdo| :lfd[o] execute command in each file in location list |:left| :le[ft] left align lines |:leftabove| :lefta[bove] make split window appear left or above |:let| :let assign a value to a variable or option diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 013eb6b97b..f931dfa341 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1,4 +1,4 @@ -*insert.txt* For Vim version 7.4. Last change: 2015 May 22 +*insert.txt* For Vim version 7.4. Last change: 2015 Sep 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -78,8 +78,8 @@ CTRL-W Delete the word before the cursor (see |i_backspacing| about |word-motions|, for the definition of a word. *i_CTRL-U* CTRL-U Delete all entered characters before the cursor in the current - line. If there are no newly entereed characters and - 'backspace'is not empty, delete all characters before the + line. If there are no newly entered characters and + 'backspace' is not empty, delete all characters before the cursor in the current line. See |i_backspacing| about joining lines. *i_CTRL-I* *i_<Tab>* *i_Tab* @@ -148,7 +148,7 @@ CTRL-R CTRL-R {0-9a-z"%#*+/:.-=} *i_CTRL-R_CTRL-R* CTRL-R a results in "ac". CTRL-R CTRL-R a results in "ab^Hc". < Options 'textwidth', 'formatoptions', etc. still apply. If - you also want to avoid these, use "<C-R><C-O>r", see below. + you also want to avoid these, use CTRL-R CTRL-O, see below. The '.' register (last inserted text) is still inserted as typed. @@ -1941,9 +1941,9 @@ If the 'fileformats' option is not empty Vim tries to recognize the type of changed, the detected format is only used while reading the file. A similar thing happens with 'fileencodings'. -On non-MS-DOS and Win32 systems the message "[dos format]" is shown if +On non-Windows systems the message "[dos format]" is shown if a file is read in DOS format, to remind you that something unusual is done. -On Macintosh, MS-DOS, and Win32 the message "[unix format]" is shown if +On Macintosh and Windows the message "[unix format]" is shown if a file is read in Unix format. On non-Macintosh systems, the message "[Mac format]" is shown if a file is read in Mac format. diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 0825ca8848..cbe017e051 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -1,4 +1,4 @@ -*intro.txt* For Vim version 7.4. Last change: 2014 May 24 +*intro.txt* For Vim version 7.4. Last change: 2015 Jan 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -131,7 +131,7 @@ http://www.vim.org/maillist.php Bug reports: *bugs* *bug-reports* *bugreport.vim* -Send bug reports to: Vim Developers <vim_dev@vim.org> +Send bug reports to: Vim Developers <vim-dev@vim.org> This is a maillist, you need to become a member first and many people will see the message. If you don't want that, e.g. because it is a security issue, send it to <bugs@vim.org>, this only goes to the Vim maintainer (that's Bram). @@ -251,7 +251,7 @@ Vim would never have become what it is now, without the help of these people! lots of patches Ingo Wilken Tcl interface Mike Williams PostScript printing - Juergen Weigert Lattice version, AUX improvements, UNIX and + Juergen Weigert Lattice version, AUX improvements, Unix and MS-DOS ports, autoconf Stefan 'Sec' Zehl Maintainer of vim.org @@ -452,7 +452,7 @@ notation meaning equivalent decimal value(s) ~ <C-...> control-key *control* *ctrl* *<C-* <M-...> alt-key or meta-key *meta* *alt* *<M-* <A-...> same as <M-...> *<A-* -<D-...> command-key (Macintosh only) *<D-* +<D-...> command-key or "super" key *<D-* <t_xx> key with "xx" entry in termcap ----------------------------------------------------------------------- diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index ab49b6f889..464c700a4d 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 7.4. Last change: 2014 Oct 03 +*map.txt* For Vim version 7.4. Last change: 2014 Dec 08 VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt index 2e3a0141ac..c87ed317d4 100644 --- a/runtime/doc/mbyte.txt +++ b/runtime/doc/mbyte.txt @@ -43,16 +43,6 @@ features. Unfortunately, every system has its own way to deal with multibyte languages and it is quite complicated. -COMPILING - -If you already have a compiled Vim program, check if the |+multi_byte| feature -is included. The |:version| command can be used for this. - -If +multi_byte is not included, you should compile Vim with "normal", "big" or -"huge" features. You can further tune what features are included. See the -INSTALL files in the source directory. - - LOCALE First of all, you must make sure your current locale is set correctly. If @@ -107,14 +97,6 @@ is the difficult part. It depends on the system you are using, the locale and a few other things. See the chapters on fonts: |mbyte-fonts-X11| for X-Windows and |mbyte-fonts-MSwin| for MS-Windows. -For GTK+ 2, you can skip most of this section. The option 'guifontset' does -no longer exist. You only need to set 'guifont' and everything should "just -work". If your system comes with Xft2 and fontconfig and the current font -does not contain a certain glyph, a different font will be used automatically -if available. The 'guifontwide' option is still supported but usually you do -not need to set it. It is only necessary if the automatic font selection does -not suit your needs. - For X11 you can set the 'guifontset' option to a list of fonts that together cover the characters that are used. Example for Korean: > @@ -494,11 +476,6 @@ For Vim you may need to set 'encoding' to "utf-8". Unfortunately, using fonts in X11 is complicated. The name of a single-byte font is a long string. For multi-byte fonts we need several of these... -Note: Most of this is no longer relevant for GTK+ 2. Selecting a font via -its XLFD is not supported; see 'guifont' for an example of how to -set the font. Do yourself a favor and ignore the |XLFD| and |xfontset| -sections below. - First of all, Vim only accepts fixed-width fonts for displaying text. You cannot use proportionally spaced fonts. This excludes many of the available (and nicer looking) fonts. However, for menus and tooltips any font can be @@ -629,52 +606,6 @@ If you use a wrong "font" argument you will get an error message. Also make sure that you set 'guifontset' before setting fonts for highlight groups. - -USING RESOURCE FILES - -Instead of specifying 'guifontset', you can set X11 resources and Vim will -pick them up. This is only for people who know how X resource files work. - -For Motif and Athena insert these three lines in your $HOME/.Xdefaults file: - - Vim.font: |base_font_name_list| - Vim*fontSet: |base_font_name_list| - Vim*fontList: your_language_font - -Note: Vim.font is for text area. - Vim*fontSet is for menu. - Vim*fontList is for menu (for Motif GUI) - -For example, when you are using Japanese and a 14 dots font, > - - Vim.font: -misc-fixed-medium-r-normal--14-* - Vim*fontSet: -misc-fixed-medium-r-normal--14-* - Vim*fontList: -misc-fixed-medium-r-normal--14-* -< -or: > - - Vim*font: k14,r14 - Vim*fontSet: k14,r14 - Vim*fontList: k14,r14 -< -To have them take effect immediately you will have to do > - - xrdb -merge ~/.Xdefaults - -Otherwise you will have to stop and restart the X server before the changes -take effect. - - -The GTK+ version of GUI Vim does not use .Xdefaults, use ~/.gtkrc instead. -The default mostly works OK. But for the menus you might have to change -it. Example: > - - style "default" - { - fontset="-*-*-medium-r-normal--14-*-*-*-c-*-*-*" - } - widget_class "*" style "default" - ============================================================================== 6. Fonts on MS-Windows *mbyte-fonts-MSwin* @@ -847,20 +778,6 @@ For example, when you are using kinput2 as |IM-server| and sh, > export XMODIFIERS="@im=kinput2" < -FULLY CONTROLLED XIM - -You can fully control XIM, like with IME of MS-Windows (see |multibyte-ime|). -This is currently only available for the GTK GUI. - -Before using fully controlled XIM, one setting is required. Set the -'imactivatekey' option to the key that is used for the activation of the input -method. For example, when you are using kinput2 + canna as IM Server, the -activation key is probably Shift+Space: > - - :set imactivatekey=S-space - -See 'imactivatekey' for the format. - ============================================================================== 8. Input on MS-Windows *mbyte-IME* @@ -893,14 +810,11 @@ WHAT IS IME URL. WHAT IS GLOBAL IME *global-ime* - Global IME makes capability to input Chinese, Japanese, and Korean text - into Vim buffer on any language version of Windows 98, Windows 95, and - Windows NT 4.0. - On Windows 2000 and XP it should work as well (without downloading). On - Windows 2000 Professional, Global IME is built in, and the Input Locales - can be added through Control Panel/Regional Options/Input Locales. - Please see below URL for detail of Global IME. You can also find various - language version of Global IME at same place. + Global IME enables input of Chinese, Japanese, and Korean text into Vim + buffer on any language version of Windows. Global IME is built in, and + the Input Locales can be added through Control Panel/Regional + Options/Input Locales. Please see below URL for detail of Global IME. + You can also find various language version of Global IME at same place. - Global IME detailed information. http://search.microsoft.com/results.aspx?q=global+ime @@ -1201,14 +1115,12 @@ internally. Vim has comprehensive UTF-8 support. It works well in: - xterm with utf-8 support enabled -- Athena, Motif and GTK GUI - MS-Windows GUI - several other platforms Double-width characters are supported. This works best with 'guifontwide' or 'guifontset'. When using only 'guifont' the wide characters are drawn in the -normal width and a space to fill the gap. Note that the 'guifontset' option -is no longer relevant in the GTK+ 2 GUI. +normal width and a space to fill the gap. *bom-bytes* When reading a file a BOM (Byte Order Mark) can be used to recognize the @@ -1278,8 +1190,6 @@ doesn't always work. See the system specific remarks below, and 'langmenu'. USING UTF-8 IN X-Windows *utf-8-in-xwindows* -Note: This section does not apply to the GTK+ 2 GUI. - You need to specify a font to be used. For double-wide characters another font is required, which is exactly twice as wide. There are three ways to do this: diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index 2ef2a67c4a..114bb61d99 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -401,12 +401,6 @@ You have used an ":unabbreviate" command with an argument which is not an existing abbreviation. All variations of this command give the same message: ":cunabbrev", ":iunabbrev", etc. Check for trailing white space. -> - /dev/dsp: No such file or directory - -Only given for GTK GUI with Gnome support. Gnome tries to use the audio -device and it isn't present. You can ignore this error. - *E31* > No such mapping diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index d732e7f818..bafb9dfc2c 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -7,7 +7,7 @@ The Msgpack-RPC Interface to Nvim *msgpack-rpc* 1. Introduction |msgpack-rpc-intro| -2. API |msgpack-rpc-api| +2. API mapping |msgpack-rpc-api| 3. Connecting |msgpack-rpc-connecting| 4. Clients |msgpack-rpc-clients| 5. Types |msgpack-rpc-types| @@ -36,13 +36,13 @@ Nvim's msgpack-rpc interface is like a more powerful version of Vim's `clientserver` feature. ============================================================================== -2. API *msgpack-rpc-api* +2. API mapping *msgpack-rpc-api* -The Nvim C API is automatically exposed to the msgpack-rpc interface by the -build system, which parses headers at src/nvim/api from the project root. A -dispatch function is generated, which matches msgpack-rpc method names with -non-static API functions, converting/validating arguments and return values -back to msgpack. +The Nvim C API, see |nvim-api|, is automatically exposed to the msgpack-rpc +interface by the build system, which parses headers at src/nvim/api from the +project root. A dispatch function is generated, which matches msgpack-rpc method +names with non-static API functions, converting/validating arguments and return +values back to msgpack. Client libraries will normally provide wrappers that hide msgpack-rpc details from programmers. The wrappers can be automatically generated by reading @@ -63,7 +63,7 @@ Here's a simple way to get human-readable description of the API (requires Python and the `pyyaml`/`msgpack-python` pip packages): > nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' > api.yaml - +< ============================================================================== 3. Connecting *msgpack-rpc-connecting* @@ -162,8 +162,8 @@ https://github.com/msgpack-rpc/msgpack-rpc-ruby/blob/master/lib/msgpack/rpc/tran ============================================================================== 5. Types *msgpack-rpc-types* -Nvim's C API uses custom types for all functions (some are just typedefs -around C99 standard types). The types can be split into two groups: +Nvim's C API uses custom types for all functions, se |nvim-api-types|. +For the purpose of mapping to msgpack, he types can be split into two groups: - Basic types that map natively to msgpack (and probably have a default representation in msgpack-supported programming languages) diff --git a/runtime/doc/nvim_clipboard.txt b/runtime/doc/nvim_clipboard.txt index 1183ad7a3c..078382c7a7 100644 --- a/runtime/doc/nvim_clipboard.txt +++ b/runtime/doc/nvim_clipboard.txt @@ -22,6 +22,10 @@ is found in your `$PATH`. - xclip - xsel (newer alternative to xclip) - pbcopy/pbpaste (only for Mac OS X) +- lemonade (useful for SSH machine) + https://github.com/pocke/lemonade +- doitclient (another option for SSH setups from the maintainer of PuTTY) + http://www.chiark.greenend.org.uk/~sgtatham/doit/ The presence of a suitable clipboard tool implicitly enables the '+' and '*' registers. diff --git a/runtime/doc/nvim_provider.txt b/runtime/doc/nvim_provider.txt index a737d51ac4..91cd5fbfc7 100644 --- a/runtime/doc/nvim_provider.txt +++ b/runtime/doc/nvim_provider.txt @@ -26,7 +26,7 @@ are now decoupled from Nvim core as providers: The first example is clipboard integration: in the original Vim source code, clipboard functions account for more than 1k lines of C source code (and that -is just on ui.c), all to peform two tasks that are now accomplished with +is just on ui.c), all to perform two tasks that are now accomplished with simple shell commands such as xclip or pbcopy/pbpaste. The other example is Python scripting support: Vim has three files dedicated to diff --git a/runtime/doc/nvim_python.txt b/runtime/doc/nvim_python.txt index 1c345b4532..a2fc968db4 100644 --- a/runtime/doc/nvim_python.txt +++ b/runtime/doc/nvim_python.txt @@ -49,6 +49,9 @@ To use Vim Python 2/3 plugins with Nvim, do the following: > $ pip3 install --user neovim < +Note: If you previously installed the package, get the latest version by + appending the `--upgrade` flag to the commands above. + ============================================================================== *g:python_host_prog* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 70a585654e..83ae96a651 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 7.4. Last change: 2014 Dec 17 +*options.txt* For Vim version 7.4. Last change: 2016 Jan 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -49,9 +49,12 @@ achieve special effects. These options come in three forms: :se[t] {option}&vi Reset option to its Vi default value. :se[t] {option}&vim Reset option to its Vim default value. -:se[t] all& Set all options, except terminal options, to their - default value. The values of 'term', 'lines' and - 'columns' are not changed. +:se[t] all& Set all options to their default value. The values of + these options are not changed: + 'columns' + 'encoding' + 'lines' + Warning: This may have a lot of side effects. *:set-args* *E487* *E521* :se[t] {option}={value} or @@ -183,7 +186,7 @@ the option value, use '\"' instead. This example sets the 'titlestring' option to 'hi "there"': > :set titlestring=hi\ \"there\" -For MS-DOS and WIN32 backslashes in file names are mostly not removed. More +For Windows backslashes in file names are mostly not removed. More precise: For options that expect a file name (those where environment variables are expanded) a backslash before a normal file name character is not removed. But a backslash before a special character (space, backslash, comma, @@ -488,11 +491,11 @@ number can be specified where "vim:" or "Vim:" is used: vim<{vers}: version before {vers} vim={vers}: version {vers} vim>{vers}: version after {vers} -{vers} is 600 for Vim 6.0 (hundred times the major version plus minor). -For example, to use a modeline only for Vim 6.0 and later: - /* vim600: set foldmethod=marker: */ ~ -To use a modeline for Vim before version 5.7: - /* vim<570: set sw=4: */ ~ +{vers} is 700 for Vim 7.0 (hundred times the major version plus minor). +For example, to use a modeline only for Vim 7.0: + /* vim700: set foldmethod=marker */ ~ +To use a modeline for Vim after version 7.2: + /* vim>702: set cole=2: */ ~ There can be no blanks between "vim" and the ":". @@ -635,9 +638,9 @@ A jump table for the options with a short description can be found at |Q_op|. (or Vim is run inside an xterm invoked with "-cjkwidth" option.), this option should be set to "double" to match the width perceived by Vim with the width of glyphs in the font. Perhaps it also has - to be set to "double" under CJK Windows 9x/ME or Windows 2k/XP - when the system locale is set to one of CJK locales. See Unicode - Standard Annex #11 (http://www.unicode.org/reports/tr11). + to be set to "double" under CJK Windows XP when the system locale is + set to one of CJK locales. + See Unicode Standard Annex #11 (http://www.unicode.org/reports/tr11). Vim may set this option automatically at startup time when Vim is compiled with the |+termresponse| feature and if |t_u7| is set to the @@ -704,7 +707,8 @@ A jump table for the options with a short description can be found at |Q_op|. line. When 'smartindent' or 'cindent' is on the indent is changed in a different way. - The 'autoindent' option is reset when the 'paste' option is set. + The 'autoindent' option is reset when the 'paste' option is set and + restored when 'paste' is reset. {small difference from Vi: After the indent is deleted when typing <Esc> or <CR>, the cursor position when moving up or down is after the deleted indent; Vi puts the cursor somewhere in the deleted indent}. @@ -772,14 +776,13 @@ A jump table for the options with a short description can be found at |Q_op|. putting a ":gui" command in the gvimrc file, before where the value of 'background' is used (e.g., before ":syntax on"). - For MS-DOS and Windows the default is "dark". - For other systems "dark" is used when 'term' is "linux", - "screen.linux", "cygwin" or "putty", or $COLORFGBG suggests a dark - background. Otherwise the default is "light". + For Windows the default is "dark". "dark" should be used if $COLORFGBG + suggests a dark background (not yet implemented). Otherwise the default + is "light". Normally this option would be set in the vimrc file. Possibly depending on the terminal name. Example: > - :if &term == "pcterm" + :if $TERM == "xterm" : set background=dark :endif < When this option is set, the default settings for the highlight groups @@ -900,7 +903,7 @@ A jump table for the options with a short description can be found at |Q_op|. impossible!). Writing may fail because of this. - A directory "." means to put the backup file in the same directory as the edited file. - - A directory starting with "./" (or ".\" for MS-DOS et al.) means to + - A directory starting with "./" (or ".\" for Windows) means to put the backup file relative to where the edited file is. The leading "." is replaced with the path name of the edited file. ("." inside a directory name has no special meaning). @@ -1110,8 +1113,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'browsedir'* *'bsdir'* 'browsedir' 'bsdir' string (default: "last") global - {only for Motif, Athena, GTK, Mac and - Win32 GUI} + {only for Mac and Win32 GUI} Which directory to use for the file browser: last Use same directory as with last file browser, where a file was opened or saved. @@ -1194,6 +1196,9 @@ A jump table for the options with a short description can be found at |Q_op|. nofile only: The buffer name is fixed, it is not handled like a file name. It is not modified in response to a |:cd| command. + both: When using ":e bufname" and already editing "bufname" + the buffer is made empty and autocommands are + triggered as usual for |:edit|. *E676* "acwrite" implies that the buffer name is not related to a file, like "nofile", but it will be written. Thus, in contrast to "nofile" and @@ -1359,7 +1364,7 @@ A jump table for the options with a short description can be found at |Q_op|. option, yank and delete operations (but not put) will additionally copy the text into register '*'. See |nvim-clipboard|. -< + *clipboard-autoselect* autoselect Works like the 'a' flag in 'guioptions': If present, then whenever Visual mode is started, or the Visual @@ -2073,7 +2078,7 @@ A jump table for the options with a short description can be found at |Q_op|. the edited file. On Unix, a dot is prepended to the file name, so it doesn't show in a directory listing. On MS-Windows the "hidden" attribute is set and a dot prepended if possible. - - A directory starting with "./" (or ".\" for MS-DOS et al.) means to + - A directory starting with "./" (or ".\" for Windows) means to put the swap file relative to where the edited file is. The leading "." is replaced with the path name of the edited file. - For Unix and Win32, if a directory ends in two path separators "//" @@ -2129,7 +2134,7 @@ A jump table for the options with a short description can be found at |Q_op|. 'edcompatible' 'ed' Removed. |vim-differences| {Nvim} *'encoding'* *'enc'* *E543* -'encoding' 'enc' string (default: "utf-8" or value from $LANG) +'encoding' 'enc' string (default: "utf-8") global {only available when compiled with the |+multi_byte| feature} @@ -2151,10 +2156,6 @@ A jump table for the options with a short description can be found at |Q_op|. can use: > if has("multi_byte_encoding") < - Normally 'encoding' will be equal to your current locale. This will - be the default if Vim recognizes your environment settings, otherwise - "utf-8" is used. - When you set this option, it fires the |EncodingChanged| autocommand event so that you can set up fonts if necessary. @@ -2171,22 +2172,20 @@ A jump table for the options with a short description can be found at |Q_op|. setting 'encoding' to one of these values instead of utf-8 only has effect for encoding used for files when 'fileencoding' is empty. - When 'encoding' is set to a Unicode encoding, and 'fileencodings' was - not set yet, the default for 'fileencodings' is changed. - *'endofline'* *'eol'* *'noendofline'* *'noeol'* 'endofline' 'eol' boolean (default on) local to buffer When writing a file and this option is off and the 'binary' option - is on, no <EOL> will be written for the last line in the file. This - option is automatically set when starting to edit a new file, unless - the file does not have an <EOL> for the last line in the file, in - which case it is reset. Normally you don't have to set or reset this - option. When 'binary' is off the value is not used when writing the - file. When 'binary' is on it is used to remember the presence of a - <EOL> for the last line in the file, so that when you write the file - the situation from the original file can be kept. But you can change - it if you want to. + is on, or 'fixeol' option is off, no <EOL> will be written for the + last line in the file. This option is automatically set or reset when + starting to edit a new file, depending on whether file has an <EOL> + for the last line in the file. Normally you don't have to set or + reset this option. + When 'binary' is off and 'fixeol' is on the value is not used when + writing the file. When 'binary' is on or 'fixeol' is off it is used + to remember the presence of a <EOL> for the last line in the file, so + that when you write the file the situation from the original file can + be kept. But you can change it if you want to. *'equalalways'* *'ea'* *'noequalalways'* *'noea'* 'equalalways' 'ea' boolean (default on) @@ -2278,6 +2277,8 @@ A jump table for the options with a short description can be found at |Q_op|. <Tab>. Spaces are used in indents with the '>' and '<' commands and when 'autoindent' is on. To insert a real tab when 'expandtab' is on, use CTRL-V<Tab>. See also |:retab| and |ins-expandtab|. + This option is reset when the 'paste' option is set and restored when + the 'paste' option is reset. *'exrc'* *'ex'* *'noexrc'* *'noex'* 'exrc' 'ex' boolean (default off) @@ -2343,9 +2344,7 @@ A jump table for the options with a short description can be found at |Q_op|. old short name was 'fe', which is no longer used. *'fileencodings'* *'fencs'* -'fileencodings' 'fencs' string (default: "ucs-bom", - "ucs-bom,utf-8,default,latin1" when - 'encoding' is set to a Unicode value) +'fileencodings' 'fencs' string (default: "ucs-bom,utf-8,default,latin1") global {only available when compiled with the |+multi_byte| feature} @@ -2385,9 +2384,8 @@ A jump table for the options with a short description can be found at |Q_op|. because Vim cannot detect an error, thus the encoding is always accepted. The special value "default" can be used for the encoding from the - environment. This is the default value for 'encoding'. It is useful - when 'encoding' is set to "utf-8" and your environment uses a - non-latin1 encoding, such as Russian. + environment. It is useful when 'encoding' is set to "utf-8" and + your environment uses a non-latin1 encoding, such as Russian. When 'encoding' is "utf-8" and a file contains an illegal byte sequence it won't be recognized as UTF-8. You can use the |8g8| command to find the illegal byte sequence. @@ -2402,7 +2400,7 @@ A jump table for the options with a short description can be found at |Q_op|. is read. *'fileformat'* *'ff'* -'fileformat' 'ff' string (MS-DOS and MS-Windows default: "dos", +'fileformat' 'ff' string (Windows default: "dos", Unix default: "unix", Macintosh default: "mac") local to buffer @@ -2446,8 +2444,8 @@ A jump table for the options with a short description can be found at |Q_op|. 2. If a <NL> is found and 'fileformats' includes "unix", 'fileformat' is set to "unix". Note that when a <NL> is found without a preceding <CR>, "unix" is preferred over "dos". - 3. If 'fileformat' has not yet been set, and if 'fileformats' - includes "mac", 'fileformat' is set to "mac". + 3. If 'fileformat' has not yet been set, and if a <CR> is found, and + if 'fileformats' includes "mac", 'fileformat' is set to "mac". This means that "mac" is only chosen when: "unix" is not present or no <NL> is found in the file, and "dos" is not present or no <CR><NL> is found in the file. @@ -2541,6 +2539,17 @@ A jump table for the options with a short description can be found at |Q_op|. fold:c Folded |hl-Folded| diff:c DiffDelete |hl-DiffDelete| + *'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'* +'fixendofline' 'fixeol' boolean (default on) + local to buffer + {not in Vi} + When writing a file and this option is on, <EOL> at the end of file + will be restored if missing. Turn this option off if you want to + preserve the situation from the original file. + When the 'binary' option is set the value of this option doesn't + matter. + See the 'endofline' option. + *'fkmap'* *'fk'* *'nofkmap'* *'nofk'* 'fkmap' 'fk' boolean (default off) *E198* global @@ -2808,10 +2817,7 @@ A jump table for the options with a short description can be found at |Q_op|. written even on filesystems which do metadata-only journaling. This will force the harddrive to spin up on Linux systems running in laptop mode, so it may be undesirable in some situations. Be warned that - turning this off increases the chances of data loss after a crash. On - systems without an fsync() implementation, this variable is always - off. - Also see 'swapsync' for controlling fsync() on swap files. + turning this off increases the chances of data loss after a crash. *'gdefault'* *'gd'* *'nogdefault'* *'nogd'* 'gdefault' 'gd' boolean (default off) @@ -2864,14 +2870,14 @@ A jump table for the options with a short description can be found at |Q_op|. r-cr:hor20-Cursor/lCursor, sm:block-Cursor -blinkwait175-blinkoff150-blinkon175", - for MS-DOS and Win32 console: + for Windows console: "n-v-c:block,o:hor50,i-ci:hor15, r-cr:hor30,sm:block") global {only available when compiled with GUI enabled, and - for MS-DOS and Win32 console} + for Windows console} This option tells Vim what the cursor should look like in different - modes. It fully works in the GUI. In an MSDOS or Win32 console, only + modes. It fully works in the GUI. In a Windows console, only the height of the cursor can be changed. This can be done by specifying a block cursor, or a percentage for a vertical or horizontal cursor. @@ -2970,28 +2976,18 @@ A jump table for the options with a short description can be found at |Q_op|. the case of X). The font names given should be "normal" fonts. Vim will try to find the related bold and italic fonts. - For Win32, GTK, Motif, and Mac OS: > + For Win32 and Mac OS: > :set guifont=* < will bring up a font requester, where you can pick the font you want. The font name depends on the GUI used. See |setting-guifont| for a way to set 'guifont' for various systems. - For the GTK+ 2 GUI the font name looks like this: > - :set guifont=Andale\ Mono\ 11 -< That's all. XLFDs are not used. For Chinese this is reported to work - well: > - if has("gui_gtk2") - set guifont=Bitstream\ Vera\ Sans\ Mono\ 12,Fixed\ 12 - set guifontwide=Microsoft\ Yahei\ 12,WenQuanYi\ Zen\ Hei\ 12 - endif -< For Mac OSX you can use something like this: > :set guifont=Monaco:h10 < *E236* Note that the fonts must be mono-spaced (all characters have the same - width). An exception is GTK 2: all fonts are accepted, but - mono-spaced fonts look best. + width). To preview a font on X11, you might be able to use the "xfontsel" program. The "xlsfonts" program gives a list of all available fonts. @@ -3024,7 +3020,6 @@ A jump table for the options with a short description can be found at |Q_op|. global {only available when compiled with GUI enabled and with the |+xfontset| feature} - {not available in the GTK+ 2 GUI} When not empty, specifies two (or more) fonts to be used. The first one for normal English, the second one for your special language. See |xfontset|. @@ -3052,24 +3047,12 @@ A jump table for the options with a short description can be found at |Q_op|. Note: The size of these fonts must be exactly twice as wide as the one specified with 'guifont' and the same height. - All GUI versions but GTK+ 2: - 'guifontwide' is only used when 'encoding' is set to "utf-8" and 'guifontset' is empty or invalid. When 'guifont' is set and a valid font is found in it and 'guifontwide' is empty Vim will attempt to find a matching double-width font and set 'guifontwide' to it. - GTK+ 2 GUI only: *guifontwide_gtk2* - - If set and valid, 'guifontwide' is always used for double width - characters, even if 'encoding' is not set to "utf-8". - Vim does not attempt to find an appropriate value for 'guifontwide' - automatically. If 'guifontwide' is empty Pango/Xft will choose the - font for characters not available in 'guifont'. Thus you do not need - to set 'guifontwide' at all unless you want to override the choice - made by Pango/Xft. - Windows +multibyte only: *guifontwide_win_mbyte* If set and valid, 'guifontwide' is used for IME instead of 'guifont'. @@ -3077,7 +3060,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'guiheadroom'* *'ghr'* 'guiheadroom' 'ghr' number (default 50) global - {only for GTK and X11 GUI} + {only for X11 GUI} The number of pixels subtracted from the screen height when fitting the GUI window on the screen. Set this before the GUI is started, e.g., in your |gvimrc| file. When zero, the whole screen height will @@ -3087,8 +3070,7 @@ A jump table for the options with a short description can be found at |Q_op|. screen. *'guioptions'* *'go'* -'guioptions' 'go' string (default "egmrLT" (MS-Windows), - "aegimrLT" (GTK, Motif and Athena)) +'guioptions' 'go' string (default "egmrLT" (MS-Windows)) global {only available when compiled with GUI enabled} This option only has an effect in the GUI version of Vim. It is a @@ -3134,10 +3116,9 @@ A jump table for the options with a short description can be found at |Q_op|. 'guitablabel' can be used to change the text in the labels. When 'e' is missing a non-GUI tab pages line may be used. The GUI tabs are only supported on some systems, currently - GTK, Motif, Mac OS/X and MS-Windows. + Mac OS/X and MS-Windows. *'go-i'* - 'i' Use a Vim icon. For GTK with KDE it is used in the left-upper - corner of the window. + 'i' Use a Vim icon. *'go-m'* 'm' Menu bar is present. *'go-M'* @@ -3149,10 +3130,8 @@ A jump table for the options with a short description can be found at |Q_op|. *'go-g'* 'g' Grey menu items: Make menu items that are not active grey. If 'g' is not included inactive menu items are not shown at all. - Exception: Athena will always use grey menu items. *'go-T'* - 'T' Include Toolbar. Currently only in Win32, GTK+, Motif, - and Athena GUIs. + 'T' Include Toolbar. Currently only in Win32 GUI. *'go-r'* 'r' Right-hand scrollbar is always present. *'go-R'* @@ -3184,8 +3163,6 @@ A jump table for the options with a short description can be found at |Q_op|. the right moment, try adding this flag. This must be done before starting the GUI. Set it in your |gvimrc|. Adding or removing it after the GUI has started has no effect. - *'go-F'* - 'F' Add a footer. Only for Motif. See |gui-footer|. *'guipty'* *'noguipty'* @@ -3433,59 +3410,15 @@ A jump table for the options with a short description can be found at |Q_op|. global Ignore case in search patterns. Also used when searching in the tags file. - Also see 'smartcase'. + Also see 'smartcase' and 'tagcase'. Can be overruled by using "\c" or "\C" in the pattern, see |/ignorecase|. *'imactivatefunc'* *'imaf'* -'imactivatefunc' 'imaf' string (default "") - global - {only available when compiled with |+xim| and - |+GUI_GTK|} - This option specifies a function that will be called to - activate/inactivate Input Method. +'imactivatefunc' 'imaf' Removed. |vim-differences| {Nvim} - Example: > - function ImActivateFunc(active) - if a:active - ... do something - else - ... do something - endif - " return value is not used - endfunction - set imactivatefunc=ImActivateFunc -< *'imactivatekey'* *'imak'* -'imactivatekey' 'imak' string (default "") - global - {only available when compiled with |+xim| and - |+GUI_GTK|} *E599* - Specifies the key that your Input Method in X-Windows uses for - activation. When this is specified correctly, vim can fully control - IM with 'imcmdline', 'iminsert' and 'imsearch'. - You can't use this option to change the activation key, the option - tells Vim what the key is. - Format: - [MODIFIER_FLAG-]KEY_STRING - - These characters can be used for MODIFIER_FLAG (case is ignored): - S Shift key - L Lock key - C Control key - 1 Mod1 key - 2 Mod2 key - 3 Mod3 key - 4 Mod4 key - 5 Mod5 key - Combinations are allowed, for example "S-C-space" or "SC-space" are - both shift+ctrl+space. - See <X11/keysymdef.h> and XStringToKeysym for KEY_STRING. - - Example: > - :set imactivatekey=S-space -< "S-space" means shift+space. This is the activation key for kinput2 + - canna (Japanese), and ami (Korean). +'imactivatekey' 'imak' Removed. |vim-differences| {Nvim} *'imcmdline'* *'imc'* *'noimcmdline'* *'noimc'* 'imcmdline' 'imc' boolean (default off) @@ -3527,8 +3460,6 @@ A jump table for the options with a short description can be found at |Q_op|. |i_CTRL-^|. The value is set to 1 when setting 'keymap' to a valid keymap name. It is also used for the argument of commands like "r" and "f". - The value 0 may not work correctly with Athena and Motif with some XIM - methods. Use 'imdisable' to disable XIM then. *'imsearch'* *'ims'* 'imsearch' 'ims' number (default 0, 2 when an input method is supported) @@ -3544,25 +3475,9 @@ A jump table for the options with a short description can be found at |Q_op|. |c_CTRL-^|. The value is set to 1 when it is not -1 and setting the 'keymap' option to a valid keymap name. - The value 0 may not work correctly with Athena and Motif with some XIM - methods. Use 'imdisable' to disable XIM then. *'imstatusfunc'* *'imsf'* -'imstatusfunc' 'imsf' string (default "") - global - {only available when compiled with |+xim| and - |+GUI_GTK|} - This option specifies a function that is called to obtain the status - of Input Method. It must return a positive number when IME is active. - - Example: > - function ImStatusFunc() - let is_active = ...do something - return is_active ? 1 : 0 - endfunction - set imstatusfunc=ImStatusFunc -< - NOTE: This function is invoked very often. Keep it fast. +'imstatusfunc' 'imsf' Removed. |vim-differences| {Nvim} *'include'* *'inc'* 'include' 'inc' string (default "^\s*#\s*include") @@ -3704,7 +3619,7 @@ A jump table for the options with a short description can be found at |Q_op|. When executing commands with |:normal| 'insertmode' is not used. *'isfname'* *'isf'* -'isfname' 'isf' string (default for MS-DOS and Win32: +'isfname' 'isf' string (default for Windows: "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,=") global @@ -3755,7 +3670,7 @@ A jump table for the options with a short description can be found at |Q_op|. See |option-backslash| about including spaces and backslashes. *'isident'* *'isi'* -'isident' 'isi' string (default for MS-DOS and Win32: +'isident' 'isi' string (default for Windows: "@,48-57,_,128-167,224-235" otherwise: "@,48-57,_,192-255") global @@ -3770,10 +3685,8 @@ A jump table for the options with a short description can be found at |Q_op|. change 'iskeyword' instead. *'iskeyword'* *'isk'* -'iskeyword' 'isk' string (Vim default for - Win32: @,48-57,_,128-167,224-235 - otherwise: @,48-57,_,192-255 - Vi default: @,48-57,_) +'iskeyword' 'isk' string (default: @,48-57,_,192-255 + Vi default: @,48-57,_) local to buffer Keywords are used in searching and recognizing with many commands: "w", "*", "[i", etc. It is also used for "\k" in a |pattern|. See @@ -3785,8 +3698,7 @@ A jump table for the options with a short description can be found at |Q_op|. When the 'lisp' option is on the '-' character is always included. *'isprint'* *'isp'* -'isprint' 'isp' string (default for MS-DOS, Win32, and Macintosh: - "@,~-255"; otherwise: "@,161-255") +'isprint' 'isp' string (default: "@,161-255") global The characters given by this option are displayed directly on the screen. It is also used for "\p" in a |pattern|. The characters from @@ -3873,7 +3785,7 @@ A jump table for the options with a short description can be found at |Q_op|. feature} This option allows switching your keyboard into a special language mode. When you are typing text in Insert mode the characters are - inserted directly. When in command mode the 'langmap' option takes + inserted directly. When in Normal mode the 'langmap' option takes care of translating these special characters to the original meaning of the key. This means you don't have to change the keyboard mode to be able to execute Normal mode commands. @@ -4077,8 +3989,9 @@ A jump table for the options with a short description can be found at |Q_op|. conceal:c Character to show in place of concealed text, when 'conceallevel' is set to 1. A space when omitted. *lcs-nbsp* - nbsp:c Character to show for a non-breakable space (character - 0xA0, 160). Left blank when omitted. + nbsp:c Character to show for a non-breakable space character + (0xA0 (160 decimal) and U+202F). Left blank when + omitted. The characters ':' and ',' should not be used. UTF-8 characters can be used when 'encoding' is "utf-8", otherwise only printable @@ -4239,8 +4152,11 @@ A jump table for the options with a short description can be found at |Q_op|. global Maximum amount of memory in Kbyte to use for all buffers together. The maximum usable value is about 2000000 (2 Gbyte). Use this to work - without a limit. On 64 bit machines higher values might work. But - hey, do you really need more than 2 Gbyte for text editing? + without a limit. + On 64 bit machines higher values might work. But hey, do you really + need more than 2 Gbyte for text editing? Keep in mind that text is + stored in the swap file, one can edit files > 2 Gbyte anyway. We do + need the memory to store undo info. Also see 'maxmem'. *'menuitems'* *'mis'* @@ -4381,7 +4297,7 @@ A jump table for the options with a short description can be found at |Q_op|. The mouse pointer is restored when the mouse is moved. *'mousemodel'* *'mousem'* -'mousemodel' 'mousem' string (default "extend", "popup" for MS-DOS and Win32) +'mousemodel' 'mousem' string (default "extend", "popup" for Windows) global Sets the model to use for the mouse. The name mostly specifies what the right mouse button is used for: @@ -4483,12 +4399,12 @@ A jump table for the options with a short description can be found at |Q_op|. *'mousetime'* *'mouset'* 'mousetime' 'mouset' number (default 500) global - Only for GUI, MS-DOS, Win32 and Unix with xterm. Defines the maximum + Only for GUI, Windows and Unix with xterm. Defines the maximum time in msec between two mouse clicks for the second click to be recognized as a multi click. *'nrformats'* *'nf'* -'nrformats' 'nf' string (default "hex") +'nrformats' 'nf' string (default "bin,hex") local to buffer This defines what bases Vim will consider for numbers when using the CTRL-A and CTRL-X commands for adding to and subtracting from a number @@ -4501,6 +4417,9 @@ A jump table for the options with a short description can be found at |Q_op|. hex If included, numbers starting with "0x" or "0X" will be considered to be hexadecimal. Example: Using CTRL-X on "0x100" results in "0x0ff". + bin If included, numbers starting with "0b" or "0B" will be + considered to be binary. Example: Using CTRL-X on + "0b1000" subtracts one, resulting in "0b0111". Numbers which simply begin with a digit in the range 1-9 are always considered decimal. This also happens for numbers that are not recognized as octal or hex. @@ -4564,11 +4483,11 @@ A jump table for the options with a short description can be found at |Q_op|. *'opendevice'* *'odev'* *'noopendevice'* *'noodev'* 'opendevice' 'odev' boolean (default off) global - {only for MS-DOS and MS-Windows} + {only for Windows} Enable reading and writing from devices. This may get Vim stuck on a device that can be opened but doesn't actually do the I/O. Therefore it is off by default. - Note that on MS-Windows editing "aux.h", "lpt1.txt" and the like also + Note that on Windows editing "aux.h", "lpt1.txt" and the like also result in editing a device. @@ -4607,19 +4526,21 @@ A jump table for the options with a short description can be found at |Q_op|. When the 'paste' option is switched on (also when it was already on): - mapping in Insert mode and Command-line mode is disabled - abbreviations are disabled - - 'textwidth' is set to 0 - - 'wrapmargin' is set to 0 - 'autoindent' is reset - - 'smartindent' is reset - - 'softtabstop' is set to 0 + - 'expandtab' is reset + - 'formatoptions' is used like it is empty - 'revins' is reset - 'ruler' is reset - 'showmatch' is reset - - 'formatoptions' is used like it is empty + - 'smartindent' is reset + - 'smarttab' is reset + - 'softtabstop' is set to 0 + - 'textwidth' is set to 0 + - 'wrapmargin' is set to 0 These options keep their value, but their effect is disabled: - - 'lisp' - - 'indentexpr' - 'cindent' + - 'indentexpr' + - 'lisp' NOTE: When you start editing another file while the 'paste' option is on, settings from the modelines or autocommands may change the settings again, causing trouble when pasting text. You might want to @@ -4934,18 +4855,7 @@ A jump table for the options with a short description can be found at |Q_op|. instead of the number of lines. *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'* -'restorescreen' 'rs' boolean (default on) - global - {only in Windows 95/NT console version} - When set, the screen contents is restored when exiting Vim. This also - happens when executing external commands. - - For non-Windows Vim: You can set or reset the 't_ti' and 't_te' - options in your vimrc. To disable restoring: - set t_ti= t_te= - To enable restoring (for an xterm): - set t_ti=^[7^[[r^[[?47h t_te=^[[?47l^[8 - (Where ^[ is an <Esc>, type CTRL-V <Esc> to insert it) +'restorescreen' 'rs' Removed. |vim-differences| {Nvim} *'revins'* *'ri'* *'norevins'* *'nori'* 'revins' 'ri' boolean (default off) @@ -4953,7 +4863,8 @@ A jump table for the options with a short description can be found at |Q_op|. Inserting characters in Insert mode will work backwards. See "typing backwards" |ins-reverse|. This option can be toggled with the CTRL-_ command in Insert mode, when 'allowrevins' is set. - NOTE: This option is reset when 'paste' is set. + This option is reset when 'paste' is set and restored when 'paste' is + reset. *'rightleft'* *'rl'* *'norightleft'* *'norl'* 'rightleft' 'rl' boolean (default off) @@ -5002,7 +4913,8 @@ A jump table for the options with a short description can be found at |Q_op|. separated with a dash. For an empty line "0-1" is shown. For an empty buffer the line number will also be zero: "0,0-1". - This option is reset when the 'paste' option is set. + This option is reset when 'paste' is set and restored when 'paste' is + reset. If you don't want to see the ruler all the time but want to know where you are, use "g CTRL-G" |g_CTRL-G|. @@ -5353,7 +5265,7 @@ A jump table for the options with a short description can be found at |Q_op|. r Removable media. The argument is a string (up to the next ','). This parameter can be given several times. Each specifies the start of a path for which no marks will be - stored. This is to avoid removable media. For MS-DOS you + stored. This is to avoid removable media. For Windows you could use "ra:,rb:". You can also use it for temp files, e.g., for Unix: "r/tmp". Case is ignored. *shada-s* @@ -5391,8 +5303,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'shell'* *'sh'* *E91* 'shell' 'sh' string (default $SHELL or "sh", - MS-DOS and Win32: "command.com" or - "cmd.exe") + Windows: "cmd.exe") global Name of the shell to use for ! and :! commands. When changing the value also check these options: 'shellpipe', 'shellslash' @@ -5403,27 +5314,50 @@ A jump table for the options with a short description can be found at |Q_op|. If the name of the shell contains a space, you might need to enclose it in quotes. Example: > :set shell=\"c:\program\ files\unix\sh.exe\"\ -f -< Note the backslash before each quote (to avoid starting a comment) and - each space (to avoid ending the option value). Also note that the - "-f" is not inside the quotes, because it is not part of the command - name. And Vim automagically recognizes the backslashes that are path - separators. +< Note the backslash before each quote (to avoid starting a comment) and + each space (to avoid ending the option value), so better use |:let-&| + like this: > + :let &shell='"C:\Program Files\unix\sh.exe" -f' +< Also note that the "-f" is not inside the quotes, because it is not + part of the command name. + *shell-unquoting* + Rules regarding quotes: + 1. Option is split on space and tab characters that are not inside + quotes: "abc def" runs shell named "abc" with additional argument + "def", '"abc def"' runs shell named "abc def" with no additional + arguments (here and below: additional means “additional to + 'shellcmdflag'â€). + 2. Quotes in option may be present in any position and any number: + '"abc"', '"a"bc', 'a"b"c', 'ab"c"' and '"a"b"c"' are all equivalent + to just "abc". + 3. Inside quotes backslash preceding backslash means one backslash. + Backslash preceding quote means one quote. Backslash preceding + anything else means backslash and next character literally: + '"a\\b"' is the same as "a\b", '"a\\"b"' runs shell named literally + 'a"b', '"a\b"' is the same as "a\b" again. + 4. Outside of quotes backslash always means itself, it cannot be used + to escape quote: 'a\"b"' is the same as "a\b". + Note that such processing is done after |:set| did its own round of + unescaping, so to keep yourself sane use |:let-&| like shown above. + This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. *'shellcmdflag'* *'shcf'* 'shellcmdflag' 'shcf' string (default: "-c"; - MS-DOS and Win32, when 'shell' does not + Windows, when 'shell' does not contain "sh" somewhere: "/c") global Flag passed to the shell to execute "!" and ":!" commands; e.g., - "bash.exe -c ls" or "command.com /c dir". For the MS-DOS-like + "bash.exe -c ls" or "cmd.exe /c dir". For Windows systems, the default is set according to the value of 'shell', to reduce the need to set this option by the user. On Unix it can have more than one flag. Each white space separated part is passed as an argument to the shell command. See |option-backslash| about including spaces and backslashes. - Also see |dos-shell| for MS-DOS and MS-Windows. + See |shell-unquoting| which talks about separating this option into + multiple arguments. + Also see |dos-shell| for Windows. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -5438,7 +5372,7 @@ A jump table for the options with a short description can be found at |Q_op|. The name of the temporary file can be represented by "%s" if necessary (the file name is appended automatically if no %s appears in the value of this option). - For MS-DOS the default is ">". The output is directly saved in a file + For Windows the default is ">". The output is directly saved in a file and not echoed to the screen. For Unix the default it "| tee". The stdout of the compiler is saved in a file and echoed to the screen. If the 'shell' option is "csh" or @@ -5462,7 +5396,7 @@ A jump table for the options with a short description can be found at |Q_op|. security reasons. *'shellquote'* *'shq'* -'shellquote' 'shq' string (default: ""; MS-DOS and Win32, when 'shell' +'shellquote' 'shq' string (default: ""; Windows, when 'shell' contains "sh" somewhere: "\"") global Quoting character(s), put around the command passed to the shell, for @@ -5470,7 +5404,7 @@ A jump table for the options with a short description can be found at |Q_op|. quoting. See 'shellxquote' to include the redirection. It's probably not useful to set both options. This is an empty string by default. Only known to be useful for - third-party shells on MS-DOS-like systems, such as the MKS Korn Shell + third-party shells on Windows systems, such as the MKS Korn Shell or bash, where it should be "\"". The default is adjusted according the value of 'shell', to reduce the need to set this option by the user. See |dos-shell|. @@ -5535,7 +5469,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'shellxescape'* *'sxe'* 'shellxescape' 'sxe' string (default: ""; - for MS-DOS and MS-Windows: "\"&|<>()@^") + for Windows: "\"&|<>()@^") global When 'shellxquote' is set to "(" then the characters listed in this option will be escaped with a '^' character. This makes it possible @@ -5617,6 +5551,9 @@ A jump table for the options with a short description can be found at |Q_op|. c don't give |ins-completion-menu| messages. For example, "-- XXX completion (YYY)", "match 1 of 2", "The only match", "Pattern not found", "Back at original", etc. + q use "recording" instead of "recording @a" + F don't give the file info when editing a file, like `:silent` + was used for the command This gives you the opportunity to avoid that a change between buffers requires you to hit <Enter>, but still gives as useful a message as @@ -5685,7 +5622,9 @@ A jump table for the options with a short description can be found at |Q_op|. jump is only done if the match can be seen on the screen. The time to show the match can be set with 'matchtime'. A Beep is given if there is no match (no matter if the match can be - seen or not). This option is reset when the 'paste' option is set. + seen or not). + This option is reset when 'paste' is set and restored when 'paste' is + reset. When the 'm' flag is not included in 'cpoptions', typing a character will immediately move the cursor back to where it belongs. See the "sm" field in 'guicursor' for setting the cursor shape and @@ -5704,8 +5643,7 @@ A jump table for the options with a short description can be found at |Q_op|. Use the 'M' flag in 'highlight' to set the type of highlighting for this message. When |XIM| may be used the message will include "XIM". But this - doesn't mean XIM is really active, especially when 'imactivatekey' is - not set. + doesn't mean XIM is really active. *'showtabline'* *'stal'* 'showtabline' 'stal' number (default 1) @@ -5785,7 +5723,8 @@ A jump table for the options with a short description can be found at |Q_op|. mapping: ":inoremap # X^H#", where ^H is entered with CTRL-V CTRL-H. When using the ">>" command, lines starting with '#' are not shifted right. - NOTE: When 'paste' is set smart indenting is disabled. + This option is reset when 'paste' is set and restored when 'paste' is + reset. *'smarttab'* *'sta'* *'nosmarttab'* *'nosta'* 'smarttab' 'sta' boolean (default on) @@ -5800,6 +5739,8 @@ A jump table for the options with a short description can be found at |Q_op|. What gets inserted (a <Tab> or spaces) depends on the 'expandtab' option. Also see |ins-expandtab|. When 'expandtab' is not set, the number of spaces is minimized by using <Tab>s. + This option is reset when 'paste' is set and restored when 'paste' is + reset. *'softtabstop'* *'sts'* 'softtabstop' 'sts' number (default 0) @@ -5812,7 +5753,8 @@ A jump table for the options with a short description can be found at |Q_op|. commands like "x" still work on the actual characters. When 'sts' is zero, this feature is off. When 'sts' is negative, the value of 'shiftwidth' is used. - 'softtabstop' is set to 0 when the 'paste' option is set. + 'softtabstop' is set to 0 when the 'paste' option is set and restored + when 'paste' is reset. See also |ins-expandtab|. When 'expandtab' is not set, the number of spaces is minimized by using <Tab>s. The 'L' flag in 'cpoptions' changes how tabs are used when 'list' is @@ -5884,7 +5826,8 @@ A jump table for the options with a short description can be found at |Q_op|. the two-letter, lower case region name. You can use more than one region by listing them: "en_us,en_ca" supports both US and Canadian English, but not words specific for Australia, New Zealand or Great - Britain. + Britain. (Note: currently en_au and en_nz dictionaries are older than + en_ca, en_gb and en_us). If the name "cjk" is included East Asian characters are excluded from spell checking. This is useful when editing text that also has Asian words. @@ -6090,11 +6033,39 @@ A jump table for the options with a short description can be found at |Q_op|. ( - Start of item group. Can be used for setting the width and alignment of a section. Must be followed by %) somewhere. ) - End of item group. No width fields allowed. - T N For 'tabline': start of tab page N label. Use %T after the last - label. This information is used for mouse clicks. - X N For 'tabline': start of close tab N label. Use %X after the - label, e.g.: %3Xclose%X. Use %999X for a "close current tab" - mark. This information is used for mouse clicks. + T N For 'tabline': start of tab page N label. Use %T or %X to end + the label. Clicking this label with left mouse button switches + to the specified tab page. + X N For 'tabline': start of close tab N label. Use %X or %T to end + the label, e.g.: %3Xclose%X. Use %999X for a "close current + tab" label. Clicking this label with left mouse button closes + specified tab page. + @ N For 'tabline': start of execute function label. Use %X or %T to + end the label, e.g.: %10@SwitchBuffer@foo.c%X. Clicking this + label runs specified function: in the example when clicking once + using left mouse button on "foo.c" "SwitchBuffer(10, 1, 'l', + ' ')" expression will be run. Function receives the + following arguments in order: + 1. minwid field value or zero if no N was specified + 2. number of mouse clicks to detect multiple clicks + 3. mouse button used: "l", "r" or "m" for left, right or middle + button respectively; one should not rely on third argument + being only "l", "r" or "m": any other non-empty string value + that contains only ASCII lower case letters may be expected + for other mouse buttons + 4. modifiers pressed: string which contains "s" if shift + modifier was pressed, "c" for control, "a" for alt and "m" + for meta; currently if modifier is not pressed string + contains space instead, but one should not rely on presence + of spaces or specific order of modifiers: use |stridx()| to + test whether some modifier is present; string is guaranteed + to contain only ASCII letters and spaces, one letter per + modifier; "?" modifier may also be present, but its presence + is a bug that denotes that new mouse button recognition was + added without modifying code that reacts on mouse clicks on + this label. + Note: to test whether your version of Neovim contains this + feature use `has('tablineat')`. < - Where to truncate line if too long. Default is at the start. No width fields allowed. = - Separation point between left and right aligned items. @@ -6118,7 +6089,7 @@ A jump table for the options with a short description can be found at |Q_op|. become empty. This will make a group like the following disappear completely from the statusline when none of the flags are set. > :set statusline=...%(\ [%M%R%H]%)... -< +< *g:actual_curbuf* Beware that an expression is evaluated each and every time the status line is displayed. The current buffer and current window will be set temporarily to that of the window (and buffer) whose statusline is @@ -6201,7 +6172,7 @@ A jump table for the options with a short description can be found at |Q_op|. When 'swapfile' is reset, the swap file for the current buffer is immediately deleted. When 'swapfile' is set, and 'updatecount' is non-zero, a swap file is immediately created. - Also see |swap-file| and |'swapsync'|. + Also see |swap-file|. If you want to open a new buffer without creating a swap file for it, use the |:noswapfile| modifier. @@ -6209,18 +6180,7 @@ A jump table for the options with a short description can be found at |Q_op|. specify special kinds of buffers. See |special-buffers|. *'swapsync'* *'sws'* -'swapsync' 'sws' string (default "fsync") - global - When this option is not empty a swap file is synced to disk after - writing to it. This takes some time, especially on busy unix systems. - When this option is empty parts of the swap file may be in memory and - not written to disk. When the system crashes you may lose more work. - On Unix the system does a sync now and then without Vim asking for it, - so the disadvantage of setting this option off is small. On some - systems the swap file will not be written at all. For a unix system - setting it to "sync" will use the sync() call instead of the default - fsync(), which may work better on some systems. - The 'fsync' option is used for the actual file. +'swapsync' 'sws' Removed. |vim-differences| {Nvim} *'switchbuf'* *'swb'* 'switchbuf' 'swb' string (default "") @@ -6239,6 +6199,7 @@ A jump table for the options with a short description can be found at |Q_op|. split If included, split the current window before loading a buffer for a |quickfix| command that display errors. Otherwise: do not split, use current window. + vsplit Just like "split" but split vertically. newtab Like "split", but open a new tab page. Overrules "split" when both are present. @@ -6364,19 +6325,22 @@ A jump table for the options with a short description can be found at |Q_op|. < [The whitespace before and after the '0' must be a single <Tab>] When a binary search was done and no match was found in any of the - files listed in 'tags', and 'ignorecase' is set or a pattern is used + files listed in 'tags', and case is ignored or a pattern is used instead of a normal tag name, a retry is done with a linear search. Tags in unsorted tags files, and matches with different case will only be found in the retry. If a tag file indicates that it is case-fold sorted, the second, - linear search can be avoided for the 'ignorecase' case. Use a value - of '2' in the "!_TAG_FILE_SORTED" line for this. A tag file can be - case-fold sorted with the -f switch to "sort" in most unices, as in - the command: "sort -f -o tags tags". For "Exuberant ctags" version - 5.x or higher (at least 5.5) the --sort=foldcase switch can be used - for this as well. Note that case must be folded to uppercase for this - to work. + linear search can be avoided when case is ignored. Use a value of '2' + in the "!_TAG_FILE_SORTED" line for this. A tag file can be case-fold + sorted with the -f switch to "sort" in most unices, as in the command: + "sort -f -o tags tags". For "Exuberant ctags" version 5.x or higher + (at least 5.5) the --sort=foldcase switch can be used for this as + well. Note that case must be folded to uppercase for this to work. + + By default, tag searches are case-sensitive. Case is ignored when + 'ignorecase' is set and 'tagcase' is "followic", or when 'tagcase' is + "ignore". When 'tagbsearch' is off, tags searching is slower when a full match exists, but faster when no full match exists. Tags in unsorted tags @@ -6387,6 +6351,16 @@ A jump table for the options with a short description can be found at |Q_op|. This option doesn't affect commands that find all matching tags (e.g., command-line completion and ":help"). + *'tagcase'* *'tc'* +'tagcase' 'tc' string (default "followic") + global or local to buffer |global-local| + {not in Vi} + This option specifies how case is handled when searching the tags + file: + followic Follow the 'ignorecase' option + ignore Ignore case + match Match case + *'taglength'* *'tl'* 'taglength' 'tl' number (default 0) global @@ -6432,18 +6406,7 @@ A jump table for the options with a short description can be found at |Q_op|. mapping which should not change the tagstack. *'term'* *E529* *E530* *E531* -'term' string (default is $TERM, if that fails: - in the GUI: "builtin_gui" - on Mac: "mac-ansi" - on MS-DOS: "pcterm" - on Unix: "ansi" - on Win 32: "win32") - global - Name of the terminal. Used for choosing the terminal control - characters. Environment variables are expanded |:set_env|. - For example: > - :set term=$TERM -< See |termcap|. +'term' Removed. |vim-differences| {Nvim} *'termbidi'* *'tbidi'* *'notermbidi'* *'notbidi'* @@ -6461,6 +6424,14 @@ A jump table for the options with a short description can be found at |Q_op|. *'termencoding'* *'tenc'* 'termencoding' 'tenc' Removed. |vim-differences| {Nvim} + *'termguicolors'* *'tgc'* +'termguicolors' 'tgc' boolean (default off) + global + When on, uses |highlight-guifg| and |highlight-guibg| attributes in + the terminal (thus using 24-bit color). Requires a ISO-8613-3 + compatible terminal. + Must be set at startup (in your |init.vim| or |--cmd|). + *'terse'* *'noterse'* 'terse' boolean (default off) global @@ -6474,8 +6445,10 @@ A jump table for the options with a short description can be found at |Q_op|. local to buffer Maximum width of text that is being inserted. A longer line will be broken after white space to get this width. A zero value disables - this. 'textwidth' is set to 0 when the 'paste' option is set. When - 'textwidth' is zero, 'wrapmargin' may be used. See also + this. + 'textwidth' is set to 0 when the 'paste' option is set and restored + when 'paste' is reset. + When 'textwidth' is zero, 'wrapmargin' may be used. See also 'formatoptions' and |ins-textwidth|. When 'formatexpr' is set it will be used to break the line. @@ -6623,9 +6596,7 @@ A jump table for the options with a short description can be found at |Q_op|. 'ttyscroll' 'tsl' Removed. |vim-differences| {Nvim} *'ttytype'* *'tty'* -'ttytype' 'tty' string (default from $TERM) - global - Alias for 'term', see above. +'ttytype' 'tty' Alias for 'term'. Removed. |vim-differences| {Nvim} *'undodir'* *'udir'* *E926* 'undodir' 'udir' string (default "$XDG_DATA_HOME/nvim/undo") @@ -6707,7 +6678,6 @@ A jump table for the options with a short description can be found at |Q_op|. When 'updatecount' is set from zero to non-zero, swap files are created for all buffers that have 'swapfile' set. When 'updatecount' is set to zero, existing swap files are not deleted. - Also see |'swapsync'|. This option has no meaning in buffers where |'buftype'| is "nofile" or "nowrite". @@ -7011,7 +6981,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'winaltkeys'* *'wak'* 'winaltkeys' 'wak' string (default "menu") global - {only used in Win32, Motif, and GTK} + {only used in Win32} Some GUI versions allow the access to menu entries by using the ALT key in combination with a character that appears underlined in the menu. This conflicts with the use of the ALT key for mappings and @@ -7026,8 +6996,7 @@ A jump table for the options with a short description can be found at |Q_op|. keys can be mapped. If the menu is disabled by excluding 'm' from 'guioptions', the ALT key is never used for the menu. - This option is not used for <F10>; on Win32 and with GTK <F10> will - select the menu, unless it has been mapped. + This option is not used for <F10>; on Win32. *'window'* *'wi'* 'window' 'wi' number (default screen height - 1) @@ -7195,6 +7164,6 @@ A jump table for the options with a short description can be found at |Q_op|. global The number of microseconds to wait for each character sent to the screen. When non-zero, characters are sent to the terminal one by - one. For MS-DOS pcterm this does not work. For debugging purposes. + one. For debugging purposes. vim:tw=78:ts=8:ft=help:noet:norl: diff --git a/runtime/doc/os_dos.txt b/runtime/doc/os_dos.txt deleted file mode 100644 index 1c80f4d7a5..0000000000 --- a/runtime/doc/os_dos.txt +++ /dev/null @@ -1,292 +0,0 @@ -*os_dos.txt* For Vim version 7.4. Last change: 2006 Mar 30 - - - VIM REFERENCE MANUAL by Bram Moolenaar - - - *dos* *DOS* -This file documents some particularities of the Win32 -version of Vim. Also see |os_win32.txt|. - -1. File locations |dos-locations| -2. Using backslashes |dos-backslash| -3. Standard mappings |dos-standard-mappings| -4. Screen output and colors |dos-colors| -5. File formats |dos-file-formats| -6. :cd command |dos-:cd| -7. Interrupting |dos-CTRL-Break| -8. Temp files |dos-temp-files| -9. Shell option default |dos-shell| - -============================================================================== -1. File locations *dos-locations* - -If you keep the Vim executable in the directory that contains the help and -syntax subdirectories, there is no need to do anything special for Vim to -work. No registry entries or environment variables need to be set. Just make -sure that the directory is in your search path, or use a shortcut on the -desktop. - -Your vimrc files ("_vimrc" and "_gvimrc") are normally located one directory -up from the runtime files. If you want to put them somewhere else, set the -environment variable $VIM to the directory where you keep them. Example: > - set VIM=C:\user\piet -Will find "c:\user\piet\_vimrc". -Note: This would only be needed when the computer is used by several people. -Otherwise it's simpler to keep your _vimrc file in the default place. - -If you move the executable to another location, you also need to set the $VIM -environment variable. The runtime files will be found in "$VIM/vim{version}". -Example: > - set VIM=E:\vim -Will find the version 5.4 runtime files in "e:\vim\vim54". -Note: This is _not_ recommended. The preferred way is to keep the executable -in the runtime directory. - -If you move your executable AND want to put your "_vimrc" and "_gvimrc" files -somewhere else, you must set $VIM to where you vimrc files are, and set -$VIMRUNTIME to the runtime files. Example: > - set VIM=C:\usr\piet - set VIMRUNTIME=E:\vim\vim54 -Will find "c:\user\piet\_vimrc" and the runtime files in "e:\vim\vim54". - -See |$VIM| and |$VIMRUNTIME| for more information. - -Under Windows 95, you can set $VIM in your C:\autoexec.bat file. For -example: > - set VIM=D:\vim -Under Windows NT, you can set environment variables for each user separately -under "Start/Settings/Control Panel->System", or through the properties in the -menu of "My Computer", under the Environment Tab. - -============================================================================== -2. Using backslashes *dos-backslash* - -Using backslashes in file names can be a problem. Vi halves the number of -backslashes for some commands. Vim is a bit more tolerant and does not remove -backslashes from a file name, so ":e c:\foo\bar" works as expected. But when -a backslash occurs before a special character (space, comma, backslash, etc.), -Vim removes the backslash. Use slashes to avoid problems: ":e c:/foo/bar" -works fine. Vim replaces the slashes with backslashes internally to avoid -problems with some MS-DOS programs and Win32 programs. - -When you prefer to use forward slashes, set the 'shellslash' option. Vim will -then replace backslashes with forward slashes when expanding file names. This -is especially useful when using a Unix-like 'shell'. - -============================================================================== -3. Standard mappings *dos-standard-mappings* - -The mappings for CTRL-PageUp and CTRL-PageDown have been removed, they now -jump to the next or previous tab page |<C-PageUp>| |<C-PageDown>| - -If you want them to move to the first and last screen line you can use these -mappings: - -key key code Normal/Visual mode Insert mode ~ -CTRL-PageUp <M-N><M-C-D> H <C-O>H -CTRL-PageDown <M-N>v L$ <C-O>L<C-O>$ - -Additionally, these keys are available for copy/cut/paste. -In the Win32 version, they also use the clipboard. - -Shift-Insert paste text (from clipboard) *<S-Insert>* -CTRL-Insert copy Visual text (to clipboard) *<C-Insert>* -CTRL-Del cut Visual text (to clipboard) *<C-Del>* -Shift-Del cut Visual text (to clipboard) *<S-Del>* - -These mappings accomplish this (Win32 version of Vim): - -key key code Normal Visual Insert ~ -Shift-Insert <M-N><M-T> "*P "-d"*P <C-R><C-O>* -CTRL-Insert <M-N><M-U> "*y -Shift-Del <M-N><M-W> "*d -CTRL-Del <M-N><M-X> "*d - -Or these mappings (non-Win32 version of Vim): - -key key code Normal Visual Insert ~ -Shift-Insert <M-N><M-T> P "-dP <C-R><C-O>" -CTRL-Insert <M-N><M-U> y -Shift-Del <M-N><M-W> d -CTRL-Del <M-N><M-X> d - -When the clipboard is supported, the "* register is used. - -============================================================================== -4. Screen output and colors *dos-colors* - -The default output method for the screen is to use bios calls. This works -right away on most systems. You do not need ansi.sys. You can use ":mode" to -set the current screen mode. See |:mode|. - -To change the screen colors that Vim uses, you can use the |:highlight| -command. The Normal highlight group specifies the colors Vim uses for normal -text. For example, to get grey text on a blue background: > - :hi Normal ctermbg=Blue ctermfg=grey -See |highlight-groups| for other groups that are available. - -A DOS console does not support attributes like bold and underlining. You can -set the color used in five modes with nine terminal options. Note that this -is not necessary since you can set the color directly with the ":highlight" -command; these options are for backward compatibility with older Vim versions. -The |'highlight'| option specifies which of the five modes is used for which -action. > - - :set t_mr=^V^[\|xxm start of invert mode - :set t_md=^V^[\|xxm start of bold mode - :set t_me=^V^[\|xxm back to normal text - - :set t_so=^V^[\|xxm start of standout mode - :set t_se=^V^[\|xxm back to normal text - - :set t_us=^V^[\|xxm start of underline mode - :set t_ue=^V^[\|xxm back to normal text - - :set t_ZH=^V^[\|xxm start of italics mode - :set t_ZR=^V^[\|xxm back to normal text - -^V is CTRL-V -^[ is <Esc> -You must replace xx with a decimal code, which is the foreground color number -and background color number added together: - -COLOR FOREGROUND BACKGROUND ~ -Black 0 0 -DarkBlue 1 16 -DarkGreen 2 32 -DarkCyan 3 48 -DarkRed 4 64 -DarkMagenta 5 80 -Brown, DarkYellow 6 96 -LightGray 7 112 -DarkGray 8 128 * -Blue, LightBlue 9 144 * -Green, LightGreen 10 160 * -Cyan, LightCyan 11 176 * -Red, LightRed 12 192 * -Magenta, LightMagenta 13 208 * -Yellow, LightYellow 14 224 * -White 15 240 * - -* Depending on the display mode, the color codes above 128 may not be - available, and code 128 will make the text blink. - -When you use 0, the color is reset to the one used when you started Vim -(usually 7, lightgray on black, but you can override this. If you have -overridden the default colors in a command prompt, you may need to adjust -some of the highlight colors in your vimrc---see below). -This is the default for t_me. - -The defaults for the various highlight modes are: - t_mr 112 reverse mode: Black text (0) on LightGray (112) - t_md 15 bold mode: White text (15) on Black (0) - t_me 0 normal mode (revert to default) - - t_so 31 standout mode: White (15) text on DarkBlue (16) - t_se 0 standout mode end (revert to default) - - t_czh 225 italic mode: DarkBlue text (1) on Yellow (224) - t_czr 0 italic mode end (revert to default) - - t_us 67 underline mode: DarkCyan text (3) on DarkRed (64) - t_ue 0 underline mode end (revert to default) - -These colors were chosen because they also look good when using an inverted -display, but you can change them to your liking. - -Example: > - :set t_mr=^V^[\|97m " start of invert mode: DarkBlue (1) on Brown (96) - :set t_md=^V^[\|67m " start of bold mode: DarkCyan (3) on DarkRed (64) - :set t_me=^V^[\|112m " back to normal mode: Black (0) on LightGray (112) - - :set t_so=^V^[\|37m " start of standout mode: DarkMagenta (5) on DarkGreen - (32) - :set t_se=^V^[\|112m " back to normal mode: Black (0) on LightGray (112) - -============================================================================== -5. File formats *dos-file-formats* - -If the 'fileformat' option is set to "dos" (which is the default), Vim accepts -a single <NL> or a <CR><NL> pair for end-of-line (<EOL>). When writing a -file, Vim uses <CR><NL>. Thus, if you edit a file and write it, Vim replaces -<NL> with <CR><NL>. - -If the 'fileformat' option is set to "unix", Vim uses a single <NL> for <EOL> -and shows <CR> as ^M. - -You can use Vim to replace <NL> with <CR><NL> by reading in any mode and -writing in Dos mode (":se ff=dos"). -You can use Vim to replace <CR><NL> with <NL> by reading in Dos mode and -writing in Unix mode (":se ff=unix"). - -Vim sets 'fileformat' automatically when 'fileformats' is not empty (which is -the default), so you don't really have to worry about what you are doing. - |'fileformat'| |'fileformats'| - -If you want to edit a script file or a binary file, you should set the -'binary' option before loading the file. Script files and binary files may -contain single <NL> characters which Vim would replace with <CR><NL>. You can -set 'binary' automatically by starting Vim with the "-b" (binary) option. - -============================================================================== -6. :cd command *dos-:cd* - -The ":cd" command recognizes the drive specifier and changes the current -drive. Use ":cd c:" to make drive C the active drive. Use ":cd d:\foo" to go -to the directory "foo" in the root of drive D. Vim also recognizes UNC names -if the system supports them; e.g., ":cd \\server\share\dir". |:cd| - -============================================================================== -7. Interrupting *dos-CTRL-Break* - -Use CTRL-Break instead of CTRL-C to interrupt searches. Vim does not detect -the CTRL-C until it tries to read a key. - -============================================================================== -8. Temp files *dos-temp-files* - -Only for the 16 bit and 32 bit DOS version: -Vim puts temporary files (for filtering) in the first of these directories -that exists and in which Vim can create a file: - $TMP - $TEMP - C:\TMP - C:\TEMP - current directory - -For the Win32 version (both console and GUI): -Vim uses standard Windows functions to obtain a temporary file name (for -filtering). The first of these directories that exists and in which Vim can -create a file is used: - $TMP - $TEMP - current directory - -============================================================================== -9. Shell option default *dos-shell* - -The default for the 'sh' ('shell') option is "command.com" on Windows 95 and -"cmd.exe" on Windows NT. If SHELL is defined, Vim uses SHELL instead, and if -SHELL is not defined but COMSPEC is, Vim uses COMSPEC. Vim starts external -commands with "<shell> /c <command_name>". Typing CTRL-Z starts a new command -subshell. Return to Vim with "exit". |'shell'| |CTRL-Z| - -If you are running a third-party shell, you may need to set the -|'shellcmdflag'| ('shcf') and |'shellquote'| ('shq') or |'shellxquote'| -('sxq') options. Unfortunately, this also depends on the version of Vim used. -For example, with the MKS Korn shell or with bash, the values of the options -on Win32 should be: - -'shellcmdflag' -c -'shellquote' (empty) -'shellxquote' " - -For Win32, this starts the shell as: - <shell> -c "command name >file" - -When starting up, Vim checks for the presence of "sh" anywhere in the 'shell' -option. If it is present, Vim sets the 'shellcmdflag' and 'shellquote' or -'shellxquote' options will be set as described above. - - vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/os_win32.txt b/runtime/doc/os_win32.txt index 603dbcddce..3c7ca4e36a 100644 --- a/runtime/doc/os_win32.txt +++ b/runtime/doc/os_win32.txt @@ -7,17 +7,15 @@ *win32* *Win32* *MS-Windows* This file documents the idiosyncrasies of the Win32 version of Vim. -The Win32 version of Vim works on Windows NT, XP, Vista and Windows 7. +The Win32 version of Vim works on Windows XP, Vista and Windows 7. There are both console and GUI versions. The 32 bit version also runs on 64 bit MS-Windows systems. 1. Known problems |win32-problems| 2. Startup |win32-startup| -3. Restore screen contents |win32-restore| -4. Using the mouse |win32-mouse| -5. Running under Windows 3.1 |win32-win3.1| -6. Win32 mini FAQ |win32-faq| +3. Using the mouse |win32-mouse| +4. Win32 mini FAQ |win32-faq| Additionally, there are a number of common Win32 and DOS items: File locations |dos-locations| @@ -34,7 +32,6 @@ Win32 GUI |gui-w32| Credits: The Win32 version was written by George V. Reilly <george@reilly.org>. -The original Windows NT port was done by Roger Knobbe <RogerK@wonderware.com>. The GUI version was made by George V. Reilly and Robert Webb. ============================================================================== @@ -79,14 +76,7 @@ make "!xxd" work, as it is in the Tools menu. And it also means that when executable() returns 1 the executable can actually be executed. ============================================================================== -3. Restore screen contents *win32-restore* - -When 'restorescreen' is set (which is the default), Vim will restore the -original contents of the console when exiting or when executing external -commands. If you don't want this, use ":set nors". |'restorescreen'| - -============================================================================== -4. Using the mouse *win32-mouse* +3. Using the mouse *win32-mouse* The Win32 version of Vim supports using the mouse. If you have a two-button mouse, the middle button can be emulated by pressing both left and right @@ -98,35 +88,7 @@ When the mouse doesn't work, try disabling the "Quick Edit Mode" feature of the console. ============================================================================== -5. Running under Windows 3.1 *win32-win3.1* - - *win32s* *windows-3.1* -There is a special version of Gvim that runs under Windows 3.1 and 3.11. You -need the gvim.exe that was compiled with Visual C++ 4.1. - -To run the Win32 version under Windows 3.1, you need to install Win32s. You -might have it already from another Win32 application which you have installed. -If Vim doesn't seem to be running properly, get the latest version: 1.30c. -You can find it at: - - http://support.microsoft.com/download/support/mslfiles/pw1118.exe - -(Microsoft moved it again, we don't know where it is now :-( ). - -The reason for having two versions of gvim.exe is that the Win32s version was -compiled with VC++ 4.1. This is the last version of VC++ that supports Win32s -programs. VC++ 5.0 is better, so that one was used for the Win32 version. -Apart from that, there is no difference between the programs. If you are in a -mixed environment, you can use the gvim.exe for Win32s on both. - -The Win32s version works the same way as the Win32 version under 95/NT. When -running under Win32s the following differences apply: -- You cannot use long file names, because Windows 3.1 doesn't support them! -- When executing an external command, it doesn't return an exit code. After - doing ":make" you have to do ":cn" yourself. - -============================================================================== -6. Win32 mini FAQ *win32-faq* +4. Win32 mini FAQ *win32-faq* Q. How do I change the font? A. In the GUI version, you can use the 'guifont' option. Example: > @@ -134,14 +96,6 @@ A. In the GUI version, you can use the 'guifont' option. Example: > < In the console version, you need to set the font of the console itself. You cannot do this from within Vim. -Q. How do I type dead keys on Windows NT? -A. Dead keys work on NT 3.51. Just type them as you would in any other - application. - On NT 4.0, you need to make sure that the default locale (set in the - Keyboard part of the Control Panel) is the same as the currently active - locale. Otherwise the NT code will get confused and crash! This is a NT - 4.0 problem, not really a Vim problem. - Q. I'm using Vim to edit a symbolically linked file on a Unix NFS file server. When I write the file, Vim does not "write through" the symlink. Instead, it deletes the symbolic link and creates a new file in its place. Why? @@ -176,28 +130,6 @@ A. Basically what you need is to put a tee program that will copy its input :set shellpipe=\|\ tee < to your _vimrc. -Q. I'm storing files on a remote machine that works with VisionFS, and files - disappear! -A. VisionFS can't handle certain dot (.) three letter extension file names. - SCO declares this behavior required for backwards compatibility with 16bit - DOS/Windows environments. The two commands below demonstrate the behavior: -> - echo Hello > file.bat~ - dir > file.bat -< - The result is that the "dir" command updates the "file.bat~" file, instead - of creating a new "file.bat" file. This same behavior is exhibited in Vim - when editing an existing file named "foo.bat" because the default behavior - of Vim is to create a temporary file with a '~' character appended to the - name. When the file is written, it winds up being deleted. - - Solution: Add this command to your _vimrc file: > - :set backupext=.temporary - -Q. How do I change the blink rate of the cursor? -A. You can't! This is a limitation of the NT console. NT 5.0 is reported to - be able to set the blink rate for all console windows at the same time. - *:!start* Q. How can I run an external command or program asynchronously? A. When using :! to run an external command, you can run it with "start": > @@ -240,28 +172,6 @@ A. You have two possible solutions depending on what you want: < The first command runs notepad minimized and the second one runs it normally. -Q. I'm using Win32s, and when I try to run an external command like "make", - Vim doesn't wait for it to finish! Help! -A. The problem is that a 32-bit application (Vim) can't get notification from - Windows that a 16-bit application (your DOS session) has finished. Vim - includes a work-around for this, but you must set up your DOS commands to - run in a window, not full-screen. Unfortunately the default when you - install Windows is full-screen. To change this: - 1) Start PIF editor (in the Main program group). - 2) Open the file "_DEFAULT.PIF" in your Windows directory. - 3) Changes the display option from "Full Screen" to "Windowed". - 4) Save and exit. - - To test, start Vim and type > - :!dir C:\<CR>". -< You should see a DOS box window appear briefly with the directory listing. - -Q. I use Vim under Win32s and NT. In NT, I can define the console to default to - 50 lines, so that I get a 80x50 shell when I ':sh'. Can I do the same in - W3.1x, or am I stuck with 80x25? -A. Edit SYSTEM.INI and add 'ScreenLines=50' to the [NonWindowsApp] section. DOS - prompts and external DOS commands will now run in a 50-line window. - *windows-icon* Q. I don't like the Vim icon, can I change it? A. Yes, place your favorite icon in bitmaps/vim.ico in a directory of diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index 1a76a4749a..5897f756d8 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1,4 +1,4 @@ -*pattern.txt* For Vim version 7.4. Last change: 2014 Sep 06 +*pattern.txt* For Vim version 7.4. Last change: 2016 Jan 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -59,6 +59,8 @@ explanations are in chapter 27 |usr_27.txt|. *n* n Repeat the latest "/" or "?" [count] times. + If the cursor doesn't move the search is repeated with + count + 1. |last-pattern| *N* @@ -129,7 +131,7 @@ gD Goto global Declaration. When the cursor is on a *CTRL-C* CTRL-C Interrupt current (search) command. Use CTRL-Break on - MS-DOS |dos-CTRL-Break|. + Windows |dos-CTRL-Break|. In Normal mode, any pending command is aborted. *:noh* *:nohlsearch* @@ -390,8 +392,8 @@ Use of "\M" makes the pattern after it be interpreted as if 'nomagic' is used. Use of "\v" means that in the pattern after it all ASCII characters except '0'-'9', 'a'-'z', 'A'-'Z' and '_' have a special meaning. "very magic" -Use of "\V" means that in the pattern after it only the backslash has a -special meaning. "very nomagic" +Use of "\V" means that in the pattern after it only the backslash and the +terminating character (/ or ?) has a special meaning. "very nomagic" Examples: after: \v \m \M \V matches ~ @@ -399,6 +401,7 @@ after: \v \m \M \V matches ~ $ $ $ \$ matches end-of-line . . \. \. matches any character * * \* \* any number of the previous atom + ~ ~ \~ \~ latest substitute string () \(\) \(\) \(\) grouping into an atom | \| \| \| separating alternatives \a \a \a \a alphabetic character @@ -475,6 +478,7 @@ More explanation and examples below, follow the links. |/\%v| \%23v \%23v in virtual column 23 |/zero-width| Character classes: */character-classes* + magic nomagic matches ~ |/\i| \i \i identifier character (see 'isident' option) |/\I| \I \I like "\i", but excluding digits |/\k| \k \k keyword character (see 'iskeyword' option) @@ -505,6 +509,7 @@ Character classes: */character-classes* class with end-of-line included (end of character classes) + magic nomagic matches ~ |/\e| \e \e <Esc> |/\t| \t \t <Tab> |/\r| \r \r <CR> @@ -530,6 +535,7 @@ Character classes: */character-classes* |/\Z| \Z \Z ignore differences in Unicode "combining characters". Useful when searching voweled Hebrew or Arabic text. + magic nomagic matches ~ |/\m| \m \m 'magic' on for the following chars in the pattern |/\M| \M \M 'magic' off for the following chars in the pattern |/\v| \v \v the following chars in the pattern are "very magic" @@ -1050,7 +1056,10 @@ x A single character, with no special meaning, matches itself *E769* When the ']' is not there Vim will not give an error message but assume no collection is used. Useful to search for '['. However, you - do get E769 for internal searching. + do get E769 for internal searching. And be aware that in a + `:substitute` command the whole command becomes the pattern. E.g. + ":s/[/x/" searches for "[/x" and replaces it with nothing. It does + not search for "[" and replaces it with "x"! If the sequence begins with "^", it matches any single character NOT in the collection: "[^xyz]" matches anything but 'x', 'y' and 'z'. @@ -1082,10 +1091,13 @@ x A single character, with no special meaning, matches itself *[:backspace:]* [:backspace:] the <BS> character The brackets in character class expressions are additional to the brackets delimiting a collection. For example, the following is a - plausible pattern for a UNIX filename: "[-./[:alnum:]_~]\+" That is, + plausible pattern for a Unix filename: "[-./[:alnum:]_~]\+" That is, a list of at least one character, each of which is either '-', '.', '/', alphabetic, numeric, '_' or '~'. - These items only work for 8-bit characters. + These items only work for 8-bit characters, except [:lower:] and + [:upper:] also work for multi-byte characters when using the new + regexp engine. In the future these items may work for multi-byte + characters. */[[=* *[==]* - An equivalence class. This means that characters are matched that have almost the same meaning, e.g., when ignoring accents. This diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 7f5825ba25..1705010ff2 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1,4 +1,4 @@ -*pi_netrw.txt* For Vim version 7.4. Last change: 2015 Jan 05 +*pi_netrw.txt* For Vim version 7.4. Last change: 2015 Oct 31 ------------------------------------------------ NETRW REFERENCE MANUAL by Charles E. Campbell @@ -365,7 +365,12 @@ settings are described below, in |netrw-browser-options|, and in fun! MyFuncRef() endfun let g:Netrw_funcref= function("MyFuncRef") + < + *g:Netrw_UserMaps* specifies a function or |List| of functions which can + be used to set up user-specified maps and functionality. + See |netrw-usermaps| + *g:netrw_ftp* if it doesn't exist, use default ftp =0 use default ftp (uid password) =1 use alternate ftp method (user uid password) @@ -1062,9 +1067,10 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 < <F1> Causes Netrw to issue help <cr> Netrw will enter the directory or read the file |netrw-cr| <del> Netrw will attempt to remove the file/directory |netrw-del| - <c-h> Edit file hiding list |netrw-ctrl-h| - <c-l> Causes Netrw to refresh the directory listing |netrw-ctrl-l| - <c-r> Browse using a gvim server |netrw-ctrl-r| + <c-h> Edit file hiding list |netrw-ctrl-h| + <c-l> Causes Netrw to refresh the directory listing |netrw-ctrl-l| + <c-r> Browse using a gvim server |netrw-ctrl-r| + <c-tab> Shrink/expand a netrw/explore window |netrw-c-tab| - Makes Netrw go up one directory |netrw--| a Toggles between normal display, |netrw-a| hiding (suppress display of files matching g:netrw_list_hide) @@ -1077,6 +1083,7 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 gd Force treatment as directory |netrw-gd| gf Force treatment as file |netrw-gf| gh Quick hide/unhide of dot-files |netrw-gh| + gn Make top of tree the directory below the cursor |netrw-gn| i Cycle between thin, long, wide, and tree listings |netrw-i| mb Bookmark current directory |netrw-mb| mc Copy marked files to marked-file target directory |netrw-mc| @@ -1105,7 +1112,7 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 qf Display information on file |netrw-qf| qF Mark files using a quickfix list |netrw-qF| r Reverse sorting order |netrw-r| - R Rename the designed file(s)/directory(ies) |netrw-R| + R Rename the designated file(s)/directory(ies) |netrw-R| s Select sorting style: by name, time, or file size |netrw-s| S Specify suffix priority for name-sorting |netrw-S| t Enter the file/directory under the cursor in a new tab|netrw-t| @@ -1174,10 +1181,10 @@ Addtionally, one may use :NetrwMB to bookmark files or directories. > < No bang: enters files/directories into Netrw's bookmark system No argument and in netrw buffer: - if there are marked files: bookmark marked files - otherwise : bookmark file/directory under cursor + if there are marked files : bookmark marked files + otherwise : bookmark file/directory under cursor No argument and not in netrw buffer: bookmarks current open file - Has arguments: globs them individually and bookmarks them + Has arguments : |glob()|s each arg and bookmarks them With bang: deletes files/directories from Netrw's bookmark system @@ -1394,8 +1401,8 @@ list (unless |g:netrw_dirhistmax| is zero; by default, it's ten). With the the opposite, see |netrw-U|. The "u" map also accepts counts to go back in the history several slots. -For your convenience, |netrw-qb| lists the history number which can be -re-used in that count. +For your convenience, qb (see |netrw-qb|) lists the history number which may +be used in that count. *.netrwhist* See |g:netrw_dirhistmax| for how to control the quantity of history stack @@ -1412,7 +1419,7 @@ CHANGING TO A SUCCESSOR DIRECTORY *netrw-U* *netrw-downdir* {{{2 With the "U" map, one can change to a later directory (successor). This map is the opposite of the "u" map. (see |netrw-u|) Use the -q map to list both the bookmarks and history. (see |netrw-qb|) +qb map to list both the bookmarks and history. (see |netrw-qb|) The "U" map also accepts counts to go forward in the history several slots. @@ -1420,7 +1427,7 @@ See |g:netrw_dirhistmax| for how to control the quantity of history stack slots. -CHANGING TREE TOP *netrw-ntree* *:Ntree* {{{2 +CHANGING TREE TOP *netrw-ntree* *:Ntree* *netrw-gn* {{{2 One may specify a new tree top for tree listings using > @@ -1430,14 +1437,18 @@ Without a "dirname", the current line is used (and any leading depth information is elided). With a "dirname", the specified directory name is used. +The "gn" map will take the word below the cursor and use that for +changing the top of the tree listing. + NETRW CLEAN *netrw-clean* *:NetrwClean* {{{2 -With :NetrwClean one may easily remove netrw from one's home directory; +With NetrwClean one may easily remove netrw from one's home directory; more precisely, from the first directory on your |'runtimepath'|. -With :NetrwClean!, netrw will remove netrw from all directories on your -|'runtimepath'|. +With NetrwClean!, netrw will attempt to remove netrw from all directories on +your |'runtimepath'|. Of course, you have to have write/delete permissions +correct to do this. With either form of the command, netrw will first ask for confirmation that the removal is in fact what you want to do. If netrw doesn't have @@ -1454,6 +1465,7 @@ operating system). Netrw allows one to invoke such special handlers by: > * when Exploring, hit the "x" key * when editing, hit gx with the cursor atop the special filename < (latter not available if the |g:netrw_nogx| variable exists) + Netrw determines which special handler by the following method: * if |g:netrw_browsex_viewer| exists, then it will be used to attempt to @@ -1629,19 +1641,23 @@ DIRECTORY EXPLORATION COMMANDS {{{2 of the current tab. It will open a netrw window on the current directory if [dir] is omitted; a :Lexplore [dir] will show the specified directory in the left-hand side browser display no matter - from which window the command is issued. By default, :Lexplore will - change an uninitialized |g:netrw_chgwin| to 2; edits will thus - preferentially be made in window#2. - The [N] specifies a |g:netrw_winsize| just for the new :Lexplore + from which window the command is issued. + + By default, :Lexplore will change an uninitialized |g:netrw_chgwin| + to 2; edits will thus preferentially be made in window#2. + + The [N] specifies a |g:netrw_winsize| just for the new :Lexplore window. - Those who like this method often also like tree style displays; + + Those who like this method often also often like tree style displays; see |g:netrw_liststyle|. - Also see: |netrw-C| |g:netrw_chgwin| |g:netrw_winsize| - |netrw-p| |netrw-P| |g:netrw_browse_split| + Also see: |netrw-C| |g:netrw_browse_split| |g:netrw_wiw| + |netrw-p| |netrw-P| |g:netrw_chgwin| + |netrw-c-tab| |g:netrw_winsize| :[N]Lexplore! is like :Lexplore, except that the full-height Explorer window - will open on the right hand side, and an uninitialized |g:netrw_chgwin| + will open on the right hand side and an uninitialized |g:netrw_chgwin| will be set to 1. *netrw-:Sexplore* @@ -2125,19 +2141,18 @@ is unlikely to be fixed. UNMARKING FILES *netrw-mF* {{{2 - (also see |netrw-mf|) + (also see |netrw-mf|, |netrw-mu|) -This command will unmark all files in the current buffer. One may also use -mf (|netrw-mf|) on a specific file to unmark just that file. +The "mF" command will unmark all files in the current buffer. One may also use +mf (|netrw-mf|) on a specific, already marked, file to unmark just that file. MARKING FILES BY QUICKFIX LIST *netrw-qF* {{{2 (also see |netrw-mf|) -One may convert the |quickfix-error-lists| into a marked file list using -"qF". You may then proceed with commands such as me (|netrw-me|) to -edit them. Quickfix error lists are generated, for example, by calls -to |:vimgrep|. +One may convert |quickfix-error-lists| into a marked file list using "qF". +You may then proceed with commands such as me (|netrw-me|) to edit them. +Quickfix error lists are generated, for example, by calls to |:vimgrep|. MARKING FILES BY REGULAR EXPRESSION *netrw-mr* {{{2 @@ -2155,14 +2170,17 @@ MARKED FILES, ARBITRARY VIM COMMAND *netrw-mv* {{{2 (See |netrw-mf| and |netrw-mr| for how to mark files) (uses the local marked-file list) -The "mv" map causes netrw execute an arbitrary vim command on each file -on the local marked file list, individually: +The "mv" map causes netrw to execute an arbitrary vim command on each file on +the local marked file list, individually: * 1split * sil! keepalt e file * run vim command * sil! keepalt wq! +A prompt, "Enter vim command: ", will be issued to elicit the vim command +you wish used. + MARKED FILES, ARBITRARY SHELL COMMAND *netrw-mx* {{{2 (See |netrw-mf| and |netrw-mr| for how to mark files) @@ -2194,13 +2212,13 @@ command to be applied to all marked files on the global marked file list. The command files -It is useful, for example, to select files and make a tarball: +This approach is useful, for example, to select files and make a tarball: > (mark files) mX Enter command: tar cf mynewtarball.tar - -The command that will be run in this example: +< +The command that will be run with this example: tar cf mynewtarball.tar 'file1' 'file2' ... @@ -2253,7 +2271,7 @@ MARKED FILES: EDITING *netrw-me* {{{2 (See |netrw-mf| and |netrw-mr| for how to mark files) (uses the global marked file list) -This command will place the marked files on the |arglist| and commence +The "me" command will place the marked files on the |arglist| and commence editing them. One may return the to explorer window with |:Rexplore|. (use |:n| and |:p| to edit next and previous files in the arglist) @@ -2261,26 +2279,33 @@ MARKED FILES: GREP *netrw-mg* {{{2 (See |netrw-mf| and |netrw-mr| for how to mark files) (uses the global marked file list) -This command will apply |:vimgrep| to the marked files. +The "mg" command will apply |:vimgrep| to the marked files. The command will ask for the requested pattern; one may then enter: > /pattern/[g][j] ! /pattern/[g][j] pattern < -In the cases of "j" option usage as shown above, "mg" will winnow the current -marked file list to just those possessing the specified pattern. -Thus, one may use > - mr ...file-pattern - mg ..contents-pattern -to have a marked file list satisfying the file-pattern but containing the -desired contents-pattern. +With /pattern/, editing will start with the first item on the |quickfix| list +that vimgrep sets up (see |:copen|, |:cnext|, |:cprevious|). The |:vimgrep| +command is in use, so without 'g' each line is added to quickfix list only +once; with 'g' every match is included. + +With /pattern/j, "mg" will winnow the current marked file list to just those +marked files also possessing the specified pattern. Thus, one may use > + + mr ...file-pattern... + mg /pattern/j +< +to have a marked file list satisfying the file-pattern but also restricted to +files containing some desired pattern. + MARKED FILES: HIDING AND UNHIDING BY SUFFIX *netrw-mh* {{{2 (See |netrw-mf| and |netrw-mr| for how to mark files) (uses the local marked file list) -This command extracts the suffices of the marked files and toggles their +The "mh" command extracts the suffices of the marked files and toggles their presence on the hiding list. Please note that marking the same suffix this way multiple times will result in the suffix's presence being toggled for each file (so an even quantity of marked files having the same suffix @@ -2309,16 +2334,16 @@ MARKED FILES: PRINTING *netrw-mp* {{{2 (See |netrw-mf| and |netrw-mr| for how to mark files) (uses the local marked file list) -Netrw will apply the |:hardcopy| command to marked files. What it does -is open each file in a one-line window, execute hardcopy, then close the -one-line window. +When "mp" is used, netrw will apply the |:hardcopy| command to marked files. +What netrw does is open each file in a one-line window, execute hardcopy, then +close the one-line window. MARKED FILES: SOURCING *netrw-ms* {{{2 (See |netrw-mf| and |netrw-mr| for how to mark files) (uses the local marked file list) -Netrw will source the marked files (using vim's |:source| command) +With "ms", netrw will source the marked files (using vim's |:source| command) MARKED FILES: SETTING THE TARGET DIRECTORY *netrw-mt* {{{2 @@ -2341,6 +2366,9 @@ Set the marked file copy/move-to target (see |netrw-mc| and |netrw-mm|): This command uses |<q-args>|, so spaces in the directory name are permitted without escaping. + * With mouse-enabled vim or with gvim, one may select a target by using + <c-leftmouse> + There is only one copy/move-to target at a time in a vim session; ie. the target is a script variable (see |s:var|) and is shared between all netrw windows (in an instance of vim). @@ -2417,9 +2445,13 @@ Related topics: MARKED FILES: UNMARKING *netrw-mu* {{{2 - (See |netrw-mf| and |netrw-mr| for how to mark files) + (See |netrw-mf|, |netrw-mF|) + +The "mu" mapping will unmark all currently marked files. This command differs +from "mF" as the latter only unmarks files in the current directory whereas +"mu" will unmark global and all buffer-local marked files. +(see |netrw-mF|) -The "mu" mapping will unmark all currently marked files. *netrw-browser-settings* NETRW BROWSER VARIABLES *netrw-browser-options* *netrw-browser-var* {{{2 @@ -2724,6 +2756,11 @@ your browsing preferences. (see also: |netrw-settings|) evaluation will be suppressed (see |'ballooneval'|) + *g:netrw_usetab* if this variable exists and is non-zero, then + the <tab> map supporting shrinking/expanding a + Lexplore or netrw window will be enabled. + (see |netrw-c-tab|) + *g:netrw_remote_mkdir* command for making a remote directory via ftp (also see |g:netrw_mkdir_cmd|) default: "mkdir" @@ -2760,7 +2797,8 @@ your browsing preferences. (see also: |netrw-settings|) |netrw-ctrl-r| to use for its server. default: "NETRWSERVER" - *g:netrw_sort_by* sort by "name", "time", or "size" + *g:netrw_sort_by* sort by "name", "time", "size", or + "exten". default: "name" *g:netrw_sort_direction* sorting direction: "normal" or "reverse" @@ -2872,6 +2910,10 @@ your browsing preferences. (see also: |netrw-settings|) take effect, for example). default: 50 (for 50%) + *g:netrw_wiw* =1 specifies the minimum window width to use + when shrinking a netrw/Lexplore window + (see |netrw-c-tab|). + *g:netrw_xstrlen* Controls how netrw computes string lengths, including multi-byte characters' string length. (thanks to N Weibull, T Mechelynck) @@ -2917,7 +2959,7 @@ help on what each of the variables do. ============================================================================== -OBTAINING A FILE *netrw-O* {{{2 +OBTAINING A FILE *netrw-obtain* *netrw-O* {{{2 If there are no marked files: @@ -2947,7 +2989,7 @@ Related topics: * To automatically make the currently browsed directory the current directory, see |g:netrw_keepdir|. - *netrw-createfile* + *netrw-newfile* *netrw-createfile* OPEN A NEW FILE IN NETRW'S CURRENT DIRECTORY *netrw-%* {{{2 To open a new file in netrw's current directory, press "%". This map @@ -2979,10 +3021,13 @@ These will: will use only 30% of the columns available; the rest of the window is used for the preview window. -Also see: |g:netrw_chgwin| |netrw-P| + Related: if you like this idea, you may also find :Lexplore + (|netrw-:Lexplore|) or |g:netrw_chgwin| of interest +Also see: |g:netrw_chgwin| |netrw-P| |'previewwindow'| -PREVIOUS WINDOW *netrw-P* *netrw-prvwin* {{{2 + +PREVIOUS WINDOW *netrw-P* *netrw-prvwin* {{{2 To edit a file or directory in the previously used (last accessed) window (see :he |CTRL-W_p|), press a "P". If there's only one window, then the one window @@ -3004,7 +3049,7 @@ Associated setting variables: Also see: |g:netrw_chgwin| |netrw-p| -REFRESHING THE LISTING *netrw-ctrl-l* *netrw-ctrl_l* {{{2 +REFRESHING THE LISTING *netrw-refresh* *netrw-ctrl-l* *netrw-ctrl_l* {{{2 To refresh either a local or remote directory listing, press ctrl-l (<c-l>) or hit the <cr> when atop the ./ directory entry in the listing. One may also @@ -3024,11 +3069,12 @@ RENAMING FILES OR DIRECTORIES *netrw-move* *netrw-rename* *netrw-R* {{{2 If there are no marked files: (see |netrw-mf|) - Renaming/moving files and directories involves moving the cursor to the + Renaming files and directories involves moving the cursor to the file/directory to be moved (renamed) and pressing "R". You will then be - queried for where you want the file/directory to be moved. You may select + queried for what you want the file/directory to be renamed to You may select a range of lines with the "V" command (visual selection), and then - pressing "R". + press "R"; you will be queried for each file as to what you want it + renamed to. If there are marked files: (see |netrw-mf|) @@ -3046,6 +3092,14 @@ If there are marked files: (see |netrw-mf|) This example will mark all *.c files and then rename them to *.cpp files. + The ctrl-X character has special meaning for renaming files: > + + <c-x> : a single ctrl-x tells netrw to ignore the portion of the response + lying between the last '/' and the ctrl-x. + + <c-x><c-x> : a pair of contiguous ctrl-x's tells netrw to ignore any + portion of the string preceding the double ctrl-x's. +< WARNING:~ Note that moving files is a dangerous operation; copies are safer. That's @@ -3053,13 +3107,13 @@ If there are marked files: (see |netrw-mf|) the copy fails and the delete does not, you may lose the file. Use at your own risk. -The g:netrw_rename_cmd variable is used to implement renaming. By default its -value is: +The g:netrw_rename_cmd variable is used to implement remote renaming. By +default its value is: ssh HOSTNAME mv One may rename a block of files and directories by selecting them with -the V (|linewise-visual|). +V (|linewise-visual|) when using thin style SELECTING SORTING STYLE *netrw-s* *netrw-sort* {{{2 @@ -3072,18 +3126,19 @@ Related topics: |netrw-r| |netrw-S| Associated setting variables: |g:netrw_sort_by| |g:netrw_sort_sequence| -SETTING EDITING WINDOW *netrw-C* *netrw-:NetrwC* {{{2 +SETTING EDITING WINDOW *netrw-editwindow* *netrw-C* *netrw-:NetrwC* {{{2 One may select a netrw window for editing with the "C" mapping, using the -:NetrwC [win#] command, or by setting g:netrw_chgwin to the selected window +:NetrwC [win#] command, or by setting |g:netrw_chgwin| to the selected window number. Subsequent selection of a file to edit (|netrw-cr|) will use that window. - * C by itself, will select the current window for editing via - |netrw-cr| + * C : by itself, will select the current window holding a netrw buffer + for editing via |netrw-cr|. The C mapping is only available while in + netrw buffers. - * [count]C the count will be used as the window number to be used - for editing via |netrw-cr|. + * [count]C : the count will be used as the window number to be used + for subsequent editing via |netrw-cr|. * :NetrwC will set |g:netrw_chgwin| to the current window @@ -3092,12 +3147,91 @@ window. Using > let g:netrw_chgwin= -1 -will restore the default editing behavior (ie. use the current window). +will restore the default editing behavior +(ie. editing will use the current window). Related topics: |netrw-cr| |g:netrw_browse_split| Associated setting variables: |g:netrw_chgwin| +SHRINKING OR EXPANDING A NETRW OR LEXPLORE WINDOW *netrw-c-tab* {{{2 + +The <c-tab> key will toggle a netrw or |:Lexplore| window's width, +but only if |g:netrw_usetab| exists and is non-zero (and, of course, +only if your terminal supports differentiating <c-tab> from a plain +<tab>). + + * If the current window is a netrw window, toggle its width + (between |g:netrw_wiw| and its original width) + + * Else if there is a |:Lexplore| window in the current tab, toggle + its width + + * Else bring up a |:Lexplore| window + +If |g:netrw_usetab| exists or is zero, or if there is a pre-existing mapping +for <c-tab>, then the <tab> will not be mapped. One may map something other +than a <c-tab>, too: (but you'll still need to have had g:netrw_usetab set) > + + nmap <unique> (whatever) <Plug>NetrwShrink +< +Related topics: |:Lexplore| +Associated setting variable: |g:netrw_usetab| + + +USER SPECIFIED MAPS *netrw-usermaps* {{{1 + +One may make customized user maps. Specify a variable, |g:Netrw_UserMaps|, +to hold a |List| of lists of keymap strings and function names: > + + [["keymap-sequence","ExampleUserMapFunc"],...] +< +When netrw is setting up maps for a netrw buffer, if |g:Netrw_UserMaps| +exists, then the internal function netrw#UserMaps(islocal) is called. +This function goes through all the entries in the |g:Netrw_UserMaps| list: + + * sets up maps: > + nno <buffer> <silent> KEYMAP-SEQUENCE + :call s:UserMaps(islocal,"ExampleUserMapFunc") +< * refreshes if result from that function call is the string + "refresh" + * if the result string is not "", then that string will be + executed (:exe result) + * if the result is a List, then the above two actions on results + will be taken for every string in the result List + +The user function is passed one argument; it resembles > + + fun! ExampleUserMapFunc(islocal) +< +where a:islocal is 1 if its a local-directory system call or 0 when +remote-directory system call. + +Use netrw#Expose("varname") to access netrw-internal (script-local) + variables. +Use netrw#Modify("varname",newvalue) to change netrw-internal variables. +Use netrw#Call("funcname"[,args]) to call a netrw-internal function with + specified arguments. + +Example: Get a copy of netrw's marked file list: > + + let netrwmarkfilelist= netrw#Expose("netrwmarkfilelist") +< +Example: Modify the value of netrw's marked file list: > + + call netrw#Modify("netrwmarkfilelist",[]) +< +Example: Clear netrw's marked file list via a mapping on gu > + " ExampleUserMap: {{{2 + fun! ExampleUserMap(islocal) + call netrw#Modify("netrwmarkfilelist",[]) + call netrw#Modify('netrwmarkfilemtch_{bufnr("%")}',"") + let retval= ["refresh"] + return retval + endfun + let g:Netrw_UserMaps= [["gu","ExampleUserMap"]] +< + 10. Problems and Fixes *netrw-problems* {{{1 (This section is likely to grow as I get feedback) @@ -3272,6 +3406,7 @@ Associated setting variables: |g:netrw_chgwin| The first one (|g:netrw_ssh_cmd|) is the most important; most of the others will use the string in g:netrw_ssh_cmd by default. + *netrw-p9* *netrw-ml_get* P9. I'm browsing, changing directory, and bang! ml_get errors appear and I have to kill vim. Any way around this? @@ -3298,6 +3433,14 @@ Associated setting variables: |g:netrw_chgwin| P11. I want to have two windows; a thin one on the left and my editing window on the right. How may I accomplish this? + You probably want netrw running as in a side window. If so, you + will likely find that ":[N]Lexplore" does what you want. The + optional "[N]" allows you to select the quantity of columns you + wish the |:Lexplore|r window to start with (see |g:netrw_winsize| + for how this parameter works). + + Previous solution: + * Put the following line in your <.vimrc>: let g:netrw_altv = 1 * Edit the current directory: :e . @@ -3311,6 +3454,7 @@ Associated setting variables: |g:netrw_chgwin| <leftmouse> in the browser window and then press the <middlemouse> to select the file. + *netrw-p12* P12. My directory isn't sorting correctly, or unwanted letters are appearing in the listed filenames, or things aren't lining @@ -3388,7 +3532,7 @@ Associated setting variables: |g:netrw_chgwin| to open a swap file. (romainl) It looks like you are starting Vim from a protected - directory. Start if from your $HOME or another writable + directory. Start netrw from your $HOME or other writable directory. *netrw-p17* @@ -3412,6 +3556,58 @@ Associated setting variables: |g:netrw_chgwin| "Using Vim to Remotely Edit A File on ServerB Only Accessible From ServerA" + *netrw-P19* + P19. How do I get numbering on in directory listings? + With |g:netrw_bufsettings|, you can control netrw's buffer + settings; try putting > + let g:netrw_bufsettings="noma nomod nu nobl nowrap ro nornu" +< in your .vimrc. If you'd like to have relative numbering + instead, try > + let g:netrw_bufsettings="noma nomod nonu nobl nowrap ro rnu" +< + *netrw-P20* + P20. How may I have gvim start up showing a directory listing? + Try putting the following code snippet into your .vimrc: > + augroup VimStartup + au! + au VimEnter * if expand("%") == "" && argc() == 0 && + \ (v:servername =~ 'GVIM\d*' || v:servername == "") + \ | e . | endif + augroup END +< You may use Lexplore instead of "e" if you're so inclined. + This snippet assumes that you have client-server enabled + (ie. a "huge" vim version). + + *netrw-P21* + P21. I've made a directory (or file) with an accented character, but + netrw isn't letting me enter that directory/read that file: + + Its likely that the shell or o/s is using a different encoding + than you have vim (netrw) using. A patch to vim supporting + "systemencoding" may address this issue in the future; for + now, just have netrw use the proper encoding. For example: > + + au FileType netrw set enc=latin1 +< + *netrw-P22* + P22. I get an error message when I try to copy or move a file: + + **error** (netrw) tried using g:netrw_localcopycmd<cp>; it doesn't work! + + What's wrong? + + Netrw uses several system level commands to do things (see + + |g:netrw_localcopycmd|, |g:netrw_localmovecmd|, + |g:netrw_localrmdir|, |g:netrw_mkdir_cmd|). + + You may need to adjust the default commands for one or more of + these commands by setting them properly in your .vimrc. Another + source of difficulty is that these commands use vim's local + directory, which may not be the same as the browsing directory + shown by netrw (see |g:netrw_keepdir|). + + ============================================================================== 11. Debugging Netrw Itself *netrw-debug* {{{1 @@ -3502,6 +3698,46 @@ netrw: ============================================================================== 12. History *netrw-history* {{{1 + v154: Feb 26, 2015 * (Yuri Kanivetsky) reported a situation where + a file was not treated properly as a file + due to g:netrw_keepdir == 1 + Mar 25, 2015 * (requested by Ben Friz) one may now sort by + extension + Mar 28, 2015 * (requested by Matt Brooks) netrw has a lot + of buffer-local mappings; however, some + plugins (such as vim-surround) set up + conflicting mappings that cause vim to wait. + The "<nowait>" modifier has been included + with most of netrw's mappings to avoid that + delay. + Jun 26, 2015 * |netrw-gn| mapping implemted + * :Ntree NotADir resulted in having + the tree listing expand in the error messages + window. Fixed. + Jun 29, 2015 * Attempting to delete a file remotely caused + an error with "keepsol" mentioned; fixed. + Jul 08, 2015 * Several changes to keep the |:jumps| table + correct when working with + |g:netrw_fastbrowse| set to 2 + * wide listing with accented characters fixed + (using %-S instead of %-s with a |printf()| + Jul 13, 2015 * (Daniel Hahler) CheckIfKde() could be true + but kfmclient not installed. Changed order + in netrw#BrowseX(): checks if kde and + kfmclient, then will use xdg-open on a unix + system (if xdg-open is executable) + Aug 11, 2015 * (McDonnell) tree listing mode wouldn't + select a file in a open subdirectory. + * (McDonnell) when multiple subdirectories + were concurrently open in tree listing + mode, a ctrl-L wouldn't refresh properly. + * The netrw:target menu showed duplicate + entries + Oct 13, 2015 * (mattn) provided an exception to handle + windows with shellslash set but no shell + Oct 23, 2015 * if g:netrw_usetab and <c-tab> now used + to control whether NetrwShrink is used + (see |netrw-c-tab|) v153: May 13, 2014 * added another |g:netrw_ffkeep| usage {{{2 May 14, 2014 * changed s:PerformListing() so that it always sets ft=netrw for netrw buffers diff --git a/runtime/doc/print.txt b/runtime/doc/print.txt index 086d05613d..7565d1e976 100644 --- a/runtime/doc/print.txt +++ b/runtime/doc/print.txt @@ -677,7 +677,7 @@ It is possible to achieve a poor man's version of duplex printing using the PS utility psselect. This utility has options -e and -o for printing just the even or odd pages of a PS file respectively. -First generate a PS file with the 'hardcopy' command, then generate a new +First generate a PS file with the 'hardcopy' command, then generate new files with all the odd and even numbered pages with: > psselect -o test.ps odd.ps diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 8c428e44ef..ff4fded0d9 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1,4 +1,4 @@ -*quickfix.txt* For Vim version 7.4. Last change: 2014 Mar 27 +*quickfix.txt* For Vim version 7.4. Last change: 2015 Sep 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -291,6 +291,74 @@ use this code: > au QuickfixCmdPost make call QfMakeConv() +EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST: + *:cdo* +:cdo[!] {cmd} Execute {cmd} in each valid entry in the quickfix list. + It works like doing this: > + :cfirst + :{cmd} + :cnext + :{cmd} + etc. +< When the current file can't be |abandon|ed and the [!] + is not present, the command fails. + When an error is detected excecution stops. + The last buffer (or where an error occurred) becomes + the current buffer. + {cmd} can contain '|' to concatenate several commands. + + Only valid entries in the quickfix list are used. + A range can be used to select entries, e.g.: > + :10,$cdo cmd +< To skip entries 1 to 9. + + Note: While this command is executing, the Syntax + autocommand event is disabled by adding it to + 'eventignore'. This considerably speeds up editing + each buffer. + {not in Vi} {not available when compiled without the + |+listcmds| feature} + Also see |:bufdo|, |:tabdo|, |:argdo|, |:windo|, + |:ldo|, |:cfdo| and |:lfdo|. + + *:cfdo* +:cfdo[!] {cmd} Execute {cmd} in each file in the quickfix list. + It works like doing this: > + :cfirst + :{cmd} + :cnfile + :{cmd} + etc. +< Otherwise it works the same as `:cdo`. + {not in Vi} {not available when compiled without the + |+listcmds| feature} + + *:ldo* +:ld[o][!] {cmd} Execute {cmd} in each valid entry in the location list + for the current window. + It works like doing this: > + :lfirst + :{cmd} + :lnext + :{cmd} + etc. +< Only valid entries in the location list are used. + Otherwise it works the same as `:cdo`. + {not in Vi} {not available when compiled without the + |+listcmds| feature} + + *:lfdo* +:lfdo[!] {cmd} Execute {cmd} in each file in the location list for + the current window. + It works like doing this: > + :lfirst + :{cmd} + :lnfile + :{cmd} + etc. +< Otherwise it works the same as `:ldo`. + {not in Vi} {not available when compiled without the + |+listcmds| feature} ============================================================================= 2. The error window *quickfix-window* @@ -493,11 +561,11 @@ or simpler > "$*" can be given multiple times, for example: > :set makeprg=gcc\ -o\ $*\ $* -The 'shellpipe' option defaults to ">" for MS-DOS and Win32. This means that -the output of the compiler is saved in a file and not shown on the screen -directly. For Unix "| tee" is used. The compiler output is shown on the -screen and saved in a file the same time. Depending on the shell used "|& -tee" or "2>&1| tee" is the default, so stderr output will be included. +The 'shellpipe' option defaults to ">" on Windows. This means that the output +of the compiler is saved in a file and not shown on the screen directly. For +Unix "| tee" is used. The compiler output is shown on the screen and saved in +a file the same time. Depending on the shell used "|& tee" or "2>&1| tee" is +the default, so stderr output will be included. If 'shellpipe' is empty, the {errorfile} part will be omitted. This is useful for compilers that write to an errorfile themselves. @@ -905,9 +973,9 @@ normally happens by matching following characters and items. When nothing is following the rest of the line is matched. If "%f" is followed by a '%' or a backslash, it will look for a sequence of 'isfname' characters. -On MS-DOS and MS-Windows a leading "C:" will be included in "%f", even when -using "%f:". This means that a file name which is a single alphabetical -letter will not be detected. +On Windows a leading "C:" will be included in "%f", even when using "%f:". +This means that a file name which is a single alphabetical letter will not be +detected. The "%p" conversion is normally followed by a "^". It's used for compilers that output a line like: > diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index ea73b99ad2..8e40628e25 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -1,4 +1,4 @@ -*quickref.txt* For Vim version 7.4. Last change: 2014 Nov 19 +*quickref.txt* For Vim version 7.4. Last change: 2015 Nov 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -617,6 +617,7 @@ Short explanation of each option: *option-list* 'balloondelay' 'bdlay' delay in mS before a balloon may pop up 'ballooneval' 'beval' switch on balloon evaluation 'balloonexpr' 'bexpr' expression to show in balloon +'belloff' 'bo' do not ring the bell for these reasons 'binary' 'bin' read/write/edit file in binary mode 'bomb' prepend a Byte Order Mark to the file 'breakat' 'brk' characters that may cause a line break @@ -688,6 +689,7 @@ Short explanation of each option: *option-list* 'fileignorecase' 'fic' ignore case when using file names 'filetype' 'ft' type of file, used for autocommands 'fillchars' 'fcs' characters to use for displaying special items +'fixendofline' 'fixeol' make sure last line in file has <EOL> 'fkmap' 'fk' Farsi keyboard mapping 'foldclose' 'fcl' close a fold when the cursor leaves it 'foldcolumn' 'fdc' width of the column used to indicate folds @@ -702,10 +704,10 @@ Short explanation of each option: *option-list* 'foldnestmax' 'fdn' maximum fold depth 'foldopen' 'fdo' for which commands a fold will be opened 'foldtext' 'fdt' expression used to display for a closed fold +'formatexpr' 'fex' expression used with "gq" command 'formatlistpat' 'flp' pattern used to recognize a list header 'formatoptions' 'fo' how automatic formatting is to be done 'formatprg' 'fp' name of external program used with "gq" command -'formatexpr' 'fex' expression used with "gq" command 'fsync' 'fs' whether to invoke fsync() after file write 'gdefault' 'gd' the ":substitute" flag 'g' is default on 'grepformat' 'gfm' format of 'grepprg' output @@ -730,13 +732,10 @@ Short explanation of each option: *option-list* 'icon' let Vim set the text of the window icon 'iconstring' string to use for the Vim icon text 'ignorecase' 'ic' ignore case in search patterns -'imactivatekey' 'imak' key that activates the X input method -'imactivatefunc' 'imaf' function to enable/disable the X input method 'imcmdline' 'imc' use IM when starting to edit a command line 'imdisable' 'imd' do not use the IM in any mode 'iminsert' 'imi' use :lmap or IM in Insert mode 'imsearch' 'ims' use :lmap or IM when typing a search pattern -'imstatusfunc' 'imsf' function to obtain X input method status 'include' 'inc' pattern to be used to find an include file 'includeexpr' 'inex' expression used to process an include line 'incsearch' 'is' highlight match while typing search pattern @@ -801,6 +800,7 @@ Short explanation of each option: *option-list* 'patchexpr' 'pex' expression used to patch a file 'patchmode' 'pm' keep the oldest version of a file 'path' 'pa' list of directories searched with "gf" et.al. +'perldll' name of the Perl dynamic library 'preserveindent' 'pi' preserve the indent structure when reindenting 'previewheight' 'pvh' height of the preview window 'previewwindow' 'pvw' identifies the preview window @@ -813,6 +813,8 @@ Short explanation of each option: *option-list* 'printmbfont' 'pmbfn' font names to be used for CJK output of :hardcopy 'printoptions' 'popt' controls the format of :hardcopy output 'pumheight' 'ph' maximum height of the popup menu +'pythondll' name of the Python 2 dynamic library +'pythonthreedll' name of the Python 3 dynamic library 'quoteescape' 'qe' escape characters used in a string 'readonly' 'ro' disallow writing the buffer 'redrawtime' 'rdt' timeout for 'hlsearch' and |:match| highlighting @@ -820,10 +822,10 @@ Short explanation of each option: *option-list* 'relativenumber' 'rnu' show relative line number in front of each line 'remap' allow mappings to work recursively 'report' threshold for reporting nr. of lines changed -'restorescreen' 'rs' Win32: restore screen when exiting 'revins' 'ri' inserting characters will work backwards 'rightleft' 'rl' window is right-to-left oriented 'rightleftcmd' 'rlc' commands for which editing works right-to-left +'rubydll' name of the Ruby dynamic library 'ruler' 'ru' show cursor line and column in the status line 'rulerformat' 'ruf' custom format for the ruler 'runtimepath' 'rtp' list of directories used for runtime files @@ -874,14 +876,14 @@ Short explanation of each option: *option-list* 'suffixes' 'su' suffixes that are ignored with multiple match 'suffixesadd' 'sua' suffixes added when searching for a file 'swapfile' 'swf' whether to use a swapfile for a buffer -'swapsync' 'sws' how to sync the swap file 'switchbuf' 'swb' sets behavior when switching to another buffer 'synmaxcol' 'smc' maximum column to find syntax items 'syntax' 'syn' syntax to be loaded for current buffer -'tabstop' 'ts' number of spaces that <Tab> in file uses 'tabline' 'tal' custom format for the console tab pages line 'tabpagemax' 'tpm' maximum number of tab pages for |-p| and "tab all" +'tabstop' 'ts' number of spaces that <Tab> in file uses 'tagbsearch' 'tbs' use binary searching in tags files +'tagcase' 'tc' how to handle case when searching in tags files 'taglength' 'tl' number of significant characters for a tag 'tagrelative' 'tr' file names in tag file are relative 'tags' 'tag' list of file names used by the tag command @@ -985,11 +987,9 @@ Short explanation of each option: *option-list* |g_CTRL-G| g CTRL-G show cursor column, line, and character position |CTRL-C| CTRL-C during searches: Interrupt the search -|dos-CTRL-Break| CTRL-Break MS-DOS: during searches: Interrupt the search +|dos-CTRL-Break| CTRL-Break Windows: during searches: Interrupt the search |<Del>| <Del> while entering a count: delete last character |:version| :ve[rsion] show version information -|:mode| :mode N MS-DOS: set screen mode to N (number, C80, - C4350, etc.) |:normal| :norm[al][!] {commands} execute Normal mode commands |Q| Q switch to "Ex" mode diff --git a/runtime/doc/quotes.txt b/runtime/doc/quotes.txt index 28b0e1c623..c35fb2f139 100644 --- a/runtime/doc/quotes.txt +++ b/runtime/doc/quotes.txt @@ -178,14 +178,14 @@ Hurrah for VIM!! It is "at your fingertips" like vi, and has the extensions that vi sorely needs: highlighting for executing commands on blocks, an easily navigable and digestible help screen, and more. (Paul Pax) -The reason WHY I don't have this amazingly useful macro any more, is that I +The reason WHY I don't have this amazingly useful macro anymore, is that I now use VIM - and this is built in!! (Stephen Riehm, Germany) I am a user of VIM and I love it. I use it to do all my programming, C, C++, HTML what ever. (Tim Allwine) I discovered VIM after years of struggling with the original vi, and I just -can't live without it any more. (Emmanuel Mogenet, USA) +can't live without it anymore. (Emmanuel Mogenet, USA) Emacs has not a bit of chance to survive so long as VIM is around. Besides, it also has the most detailed software documentation I have ever seen---much diff --git a/runtime/doc/recover.txt b/runtime/doc/recover.txt index 34a579f499..e09138b2f5 100644 --- a/runtime/doc/recover.txt +++ b/runtime/doc/recover.txt @@ -60,7 +60,7 @@ Disadvantages: If you want to put swap files in a fixed place, put a command resembling the following ones in your vimrc: :set dir=~/tmp (for Unix) - :set dir=c:\\tmp (for MS-DOS and Win32) + :set dir=c:\\tmp (for Windows) This is also very handy when editing files on floppy. Of course you will have to create that "tmp" directory for this to work! @@ -92,10 +92,7 @@ changed, not when you only moved around. The reason why it is not kept up to date all the time is that this would slow down normal work too much. You can change the 200 character count with the 'updatecount' option. You can set the time with the 'updatetime' option. The time is given in milliseconds. -After writing to the swap file Vim syncs the file to disk. This takes some -time, especially on busy Unix systems. If you don't want this you can set the -'swapsync' option to an empty string. The risk of losing work becomes bigger -though. +After writing to the swap file Vim syncs the file to disk. If the writing to the swap file is not wanted, it can be switched off by setting the 'updatecount' option to 0. The same is done when starting Vim diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt index 81ab72a100..933ab3a444 100644 --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -1,4 +1,4 @@ -*remote.txt* For Vim version 7.4. Last change: 2008 May 24 +*remote.txt* For Vim version 7.4. Last change: 2015 Mar 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -34,7 +34,8 @@ The following command line arguments are available: The remote Vim is raised. If you don't want this use > vim --remote-send "<C-\><C-N>:n filename<CR>" -< --remote-silent [+{cmd}] {file} ... *--remote-silent* +< + --remote-silent [+{cmd}] {file} ... *--remote-silent* As above, but don't complain if there is no server and the file is edited locally. --remote-wait [+{cmd}] {file} ... *--remote-wait* diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index d029391c60..b2e935eb3f 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -1,4 +1,4 @@ -*repeat.txt* For Vim version 7.4. Last change: 2015 Jan 07 +*repeat.txt* For Vim version 7.4. Last change: 2015 Apr 13 VIM REFERENCE MANUAL by Bram Moolenaar @@ -109,6 +109,12 @@ q{0-9a-zA-Z"} Record typed characters into register {0-9a-zA-Z"} while executing a register, and it doesn't work inside a mapping and |:normal|. + Note: If the register being used for recording is also + used for |y| and |p| the result is most likely not + what is expected, because the put will paste the + recorded macro and the yank will overwrite the + recorded macro. + q Stops recording. Implementation note: The 'q' that stops recording is not stored in the register, unless it was the result @@ -287,13 +293,13 @@ with CTRL-V followed by the three digit decimal code. This does NOT work for the <t_xx> termcap codes, these can only be used in mappings. *:source_crnl* *W15* -MS-DOS and Win32: Files that are read with ":source" normally have -<CR><NL> <EOL>s. These always work. If you are using a file with <NL> <EOL>s -(for example, a file made on Unix), this will be recognized if 'fileformats' -is not empty and the first line does not end in a <CR>. This fails if the -first line has something like ":map <F1> :help^M", where "^M" is a <CR>. If -the first line ends in a <CR>, but following ones don't, you will get an error -message, because the <CR> from the first lines will be lost. +Windows: Files that are read with ":source" normally have <CR><NL> <EOL>s. +These always work. If you are using a file with <NL> <EOL>s (for example, a +file made on Unix), this will be recognized if 'fileformats' is not empty and +the first line does not end in a <CR>. This fails if the first line has +something like ":map <F1> :help^M", where "^M" is a <CR>. If the first line +ends in a <CR>, but following ones don't, you will get an error message, +because the <CR> from the first lines will be lost. Mac Classic: Files that are read with ":source" normally have <CR> <EOL>s. These always work. If you are using a file with <NL> <EOL>s (for example, a @@ -303,7 +309,7 @@ linebreaks which has a <CR> in first line. On other systems, Vim expects ":source"ed files to end in a <NL>. These always work. If you are using a file with <CR><NL> <EOL>s (for example, a -file made on MS-DOS), all lines will have a trailing <CR>. This may cause +file made on Windows), all lines will have a trailing <CR>. This may cause problems for some commands (e.g., mappings). There is no automatic <EOL> detection, because it's common to start with a line that defines a mapping that ends in a <CR>, which will confuse the automaton. @@ -390,7 +396,7 @@ To enter debugging mode use one of these methods: useful to find out what is happening when Vim is starting up. A side effect is that Vim will switch the terminal mode before initialisations have finished, with unpredictable results. - For a GUI-only version (Windows, Macintosh) the debugging will start as + For a GUI-only version (Windows) the debugging will start as soon as the GUI window has been opened. To make this happen early, add a ":gui" command in the vimrc file. *:debug* @@ -583,6 +589,7 @@ For example, to profile the one_script.vim script file: > :prof[ile] start {fname} *:prof* *:profile* *E750* Start profiling, write the output in {fname} upon exit. + "~/" and environment variables in {fname} will be expanded. If {fname} already exists it will be silently overwritten. The variable |v:profiling| is set to one. @@ -658,10 +665,6 @@ long you take to respond to the input() prompt is irrelevant. Profiling should give a good indication of where time is spent, but keep in mind there are various things that may clobber the results: -- The accuracy of the time measured depends on the gettimeofday() system - function. It may only be as accurate as 1/100 second, even though the times - are displayed in micro seconds. - - Real elapsed time is measured, if other processes are busy they may cause delays at unpredictable moments. You may want to run the profiling several times and use the lowest results. diff --git a/runtime/doc/rileft.txt b/runtime/doc/rileft.txt index b4d9347470..e4e495bb18 100644 --- a/runtime/doc/rileft.txt +++ b/runtime/doc/rileft.txt @@ -106,9 +106,6 @@ o Does not support reverse insert and rightleft modes on the command-line. o Somewhat slower in right-to-left mode, because right-to-left motion is emulated inside Vim, not by the controlling terminal. -o When the Athena GUI is used, the bottom scrollbar works in the wrong - direction. This is difficult to fix. - o When both 'rightleft' and 'revins' are on: 'textwidth' does not work. Lines do not wrap at all; you just get a single, long line. diff --git a/runtime/doc/scroll.txt b/runtime/doc/scroll.txt index 8626a1a7f6..f2a6f713e6 100644 --- a/runtime/doc/scroll.txt +++ b/runtime/doc/scroll.txt @@ -245,26 +245,6 @@ dragging the scrollbar of the current window. How many lines are scrolled depends on your mouse driver. If the scroll action causes input focus problems, see |intellimouse-wheel-problems|. -For the X11 GUIs (Motif, Athena and GTK) scrolling the wheel generates key -presses <ScrollWheelUp>, <ScrollWheelDown>, <ScrollWheelLeft> and -<ScrollWheelRight>. For example, if you push the scroll wheel upwards a -<ScrollWheelUp> key press is generated causing the window to scroll upwards -(while the text is actually moving downwards). The default action for these -keys are: - <ScrollWheelUp> scroll three lines up *<ScrollWheelUp>* - <S-ScrollWheelUp> scroll one page up *<S-ScrollWheelUp>* - <C-ScrollWheelUp> scroll one page up *<C-ScrollWheelUp>* - <ScrollWheelDown> scroll three lines down *<ScrollWheelDown>* - <S-ScrollWheelDown> scroll one page down *<S-ScrollWheelDown>* - <C-ScrollWheelDown> scroll one page down *<C-ScrollWheelDown>* - <ScrollWheelLeft> scroll six columns left *<ScrollWheelLeft>* - <S-ScrollWheelLeft> scroll one page left *<S-ScrollWheelLeft>* - <C-ScrollWheelLeft> scroll one page left *<C-ScrollWheelLeft>* - <ScrollWheelRight> scroll six columns right *<ScrollWheelRight>* - <S-ScrollWheelRight> scroll one page right *<S-ScrollWheelRight>* - <C-ScrollWheelRight> scroll one page right *<C-ScrollWheelRight>* -This should work in all modes, except when editing the command line. - Note that horizontal scrolling only works if 'nowrap' is set. Also, unless the "h" flag in 'guioptions' is set, the cursor moves to the longest visible line if the cursor line is about to be scrolled off the screen (similarly to @@ -281,13 +261,6 @@ You can also use Alt and Ctrl modifiers. This only works when Vim gets the scroll wheel events, of course. You can check if this works with the "xev" program. -When using XFree86, the /etc/XF86Config file should have the correct entry for -your mouse. For FreeBSD, this entry works for a Logitech scrollmouse: > - Protocol "MouseMan" - Device "/dev/psm0" - ZAxisMapping 4 5 -See the XFree86 documentation for information. - *<MouseDown>* *<MouseUp>* The keys <MouseDown> and <MouseUp> have been deprecated. Use <ScrollWheelUp> instead of <MouseDown> and use <ScrollWheelDown> instead of <MouseUp>. diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 8dc229df41..44a5361c5d 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -75,12 +75,8 @@ DEFINING A SIGN. *:sign-define* *E255* *E160* *E612* Define the file name where the bitmap can be found. Should be a full path. The bitmap should fit in the place of two characters. This is not checked. If the bitmap is too big it - will cause redraw problems. Only GTK 2 can scale the bitmap - to fit the space available. + will cause redraw problems. toolkit supports ~ - GTK 1 pixmap (.xpm) - GTK 2 many - Motif pixmap (.xpm) Win32 .bmp, .ico, .cur pixmap (.xpm) |+xpm_w32| diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index a45a97a0fe..752444a3bd 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -35,9 +35,7 @@ Vim only checks words for spelling, there is no grammar check. If the 'mousemodel' option is set to "popup" and the cursor is on a badly spelled word or it is "popup_setpos" and the mouse pointer is on a badly spelled word, then the popup menu will contain a submenu to replace the bad -word. Note: this slows down the appearance of the popup menu. Note for GTK: -don't release the right mouse button until the menu appears, otherwise it -won't work. +word. Note: this slows down the appearance of the popup menu. To search for the next misspelled word: diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index f46a258e2e..46efe1996a 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -217,7 +217,7 @@ argument. :set to display option values. When 'verbose' is non-zero messages are printed (for debugging, to stderr). - 'term' and $TERM are not used. + $TERM is not used. If Vim appears to be stuck try typing "qa!<Enter>". You don't get a prompt thus you can't see Vim is waiting for you to type something. @@ -315,9 +315,10 @@ argument. When {vimrc} is equal to "NONE" (all uppercase), all initializations from files and environment variables are skipped, including reading the |ginit.vim| file when the GUI - starts. Loading plugins is also skipped. + starts. Plugins and syntax highlighting are also skipped. When {vimrc} is equal to "NORC" (all uppercase), this has the - same effect as "NONE", but loading plugins is not skipped. + same effect as "NONE", but plugins and syntax highlighting are + not skipped. *-i* -i {shada} The file {shada} is used instead of the default ShaDa @@ -349,18 +350,15 @@ argument. -W {scriptout} Like -w, but do not append, overwrite an existing file. ============================================================================== -3. Initialization *initialization* *startup* +2. Initialization *initialization* *startup* At startup, Vim checks environment variables and files and sets values accordingly. Vim proceeds in this order: -1. Set the 'shell' and 'term' option *SHELL* *COMSPEC* *TERM* +1. Set the 'shell' option *SHELL* *COMSPEC* *TERM* The environment variable SHELL, if it exists, is used to set the - 'shell' option. On MS-DOS and Win32, the COMSPEC variable is used + 'shell' option. On Windows, the COMSPEC variable is used if SHELL is not set. - The environment variable TERM, if it exists, is used to set the 'term' - option. However, 'term' will change later when starting the GUI (step - 8 below). 2. Process the arguments The options and file names from the command that start Vim are @@ -382,6 +380,8 @@ accordingly. Vim proceeds in this order: Places for your personal initializations: Unix $XDG_CONFIG_HOME/nvim/init.vim (default for $XDG_CONFIG_HOME is ~/.config) + Windows $XDG_CONFIG_HOME/nvim/init.vim + (default for $XDG_CONFIG_HOME is ~/AppData/Local) The files are searched in the order specified above and only the first one that is found is read. @@ -394,7 +394,8 @@ accordingly. Vim proceeds in this order: All following initializations until 4. are skipped. $MYVIMRC is not set. "vim -u NORC" can be used to skip these initializations without - reading a file. "vim -u NONE" also skips loading plugins. |-u| + reading a file. "vim -u NONE" also skips plugins and syntax + highlighting. |-u| If Vim was started in Ex mode with the "-s" argument, all following initializations until 4. are skipped. Only the "-u" option is @@ -427,7 +428,22 @@ accordingly. Vim proceeds in this order: - The file ".exrc" (for Unix) "_exrc" (for Win32) -4. Load the plugin scripts. *load-plugins* +4. Enable filetype and indent plugins. + This does the same as the commands: > + :runtime! filetype.vim + :runtime! ftplugin.vim + :runtime! indent.vim +< This step is skipped if ":filetype ..." was called before now or if + the "-u NONE" command line argument was given. + +5. Enable syntax highlighting. + This does the same as the command: > + :runtime! syntax/syntax.vim +< Note: This enables filetype detection even if ":filetype off" was + called before now. + This step is skipped if the "-u NONE" command line argument was given. + +6. Load the plugin scripts. *load-plugins* This does the same as the command: > :runtime! plugin/**/*.vim < The result is that all directories in the 'runtimepath' option will be @@ -443,31 +459,30 @@ accordingly. Vim proceeds in this order: commands from the command line have not been executed yet. You can use "--cmd 'set noloadplugins'" |--cmd|. -5. Set 'shellpipe' and 'shellredir' +7. Set 'shellpipe' and 'shellredir' The 'shellpipe' and 'shellredir' options are set according to the value of the 'shell' option, unless they have been set before. This means that Vim will figure out the values of 'shellpipe' and 'shellredir' for you, unless you have set them yourself. -6. Set 'updatecount' to zero, if "-n" command argument used +8. Set 'updatecount' to zero, if "-n" command argument used -7. Set binary options +9. Set binary options If the "-b" flag was given to Vim, the options for binary editing will be set now. See |-b|. -8. Perform GUI initializations +10. Perform GUI initializations Only when starting "gvim", the GUI initializations will be done. See |gui-init|. -9. Read the ShaDa file - If the 'shada' option is not empty, the ShaDa file is read. See - |shada-file|. +11. Read the ShaDa file + See |shada-file|. -10. Read the quickfix file +12. Read the quickfix file If the "-q" flag was given to Vim, the quickfix file is read. If this fails, Vim exits. -11. Open all windows +13. Open all windows When the |-o| flag was given, windows will be opened (but not displayed yet). When the |-p| flag was given, tab pages will be created (but not @@ -476,7 +491,7 @@ accordingly. Vim proceeds in this order: If the "-q" flag was given to Vim, the first error is jumped to. Buffers for all windows will be loaded. -12. Execute startup commands +14. Execute startup commands If a "-t" flag was given to Vim, the tag is jumped to. The commands given with the |-c| and |+cmd| arguments are executed. The starting flag is reset, has("vim_starting") will now return zero. @@ -494,7 +509,7 @@ sessions. Put it in a place so that it will be found by 3b: Local setup: Put all commands that you need for editing a specific directory only into a vimrc file and place it in that directory under the name ".nvimrc" ("_nvimrc" -for MS-DOS and Win32). NOTE: To make Vim look for these special files you +for Windows). NOTE: To make Vim look for these special files you have to turn on the option 'exrc'. See |trojan-horse| too. System setup: @@ -516,10 +531,9 @@ interfere with Vi, then use the variable VIMINIT and the file init.vim instead. MS-DOS line separators: -On MS-DOS-like systems (MS-DOS itself and Win32), Vim assumes that all -the vimrc files have <CR> <NL> pairs as line separators. This will give -problems if you have a file with only <NL>s and have a line like -":map xx yy^M". The trailing ^M will be ignored. +On Windows systems Vim assumes that all the vimrc files have <CR> <NL> pairs +as line separators. This will give problems if you have a file with only +<NL>s and have a line like ":map xx yy^M". The trailing ^M will be ignored. The $MYVIMRC or $MYGVIMRC file will be set to the first found vimrc and/or gvimrc file. @@ -587,7 +601,7 @@ This still won't work for systems where gvim does not use stdout at all though. ============================================================================== -4. $VIM and $VIMRUNTIME +3. $VIM and $VIMRUNTIME *$VIM* The environment variable "$VIM" is used to locate various user files for Vim, such as the user startup script |init.vim|. This depends on the system, see @@ -649,7 +663,7 @@ greps in the help files) you might be able to use this: > VIMRUNTIME="$(nvim -e --cmd 'echo $VIMRUNTIME|quit' 2>&1)" ============================================================================== -5. Suspending *suspend* +4. Suspending *suspend* *iconize* *iconise* *CTRL-Z* *v_CTRL-Z* CTRL-Z Suspend Vim, like ":stop". @@ -682,7 +696,7 @@ can't paste it in another application (since Vim is going to sleep an attempt to get the selection would make the program hang). ============================================================================== -6. Saving settings *save-settings* +5. Saving settings *save-settings* Mostly you will edit your vimrc files manually. This gives you the greatest flexibility. There are a few commands to generate a vimrc file automatically. @@ -706,8 +720,8 @@ vimrc file. These commands will write ":map" and ":set" commands to a file, in such a way that when these commands are executed, the current key mappings and options will be set to the same values. The options 'columns', 'endofline', -'fileformat', 'lines', 'modified', 'scroll', and 'term' are not included, -because these are terminal or file dependent. +'fileformat', 'lines', 'modified', and 'scroll' are not included, because +these are terminal or file dependent. Note that the options 'binary', 'paste' and 'readonly' are included, this might not always be what you want. @@ -739,7 +753,7 @@ these steps: You need to escape special characters, esp. spaces. ============================================================================== -7. Views and Sessions *views-sessions* +6. Views and Sessions *views-sessions* This is introduced in sections |21.4| and |21.5| of the user manual. @@ -883,7 +897,7 @@ To automatically save and restore views for *.c files: > au BufWinEnter *.c silent loadview ============================================================================== -8. The ShaDa file *shada* *shada-file* +7. The ShaDa file *shada* *shada-file* If you exit Vim and later start it again, you would normally lose a lot of information. The ShaDa file can be used to remember that information, which @@ -947,7 +961,7 @@ about to abandon with ":bdel", use ":wsh". The '[' and ']' marks are not stored, but the '"' mark is. The '"' mark is very useful for jumping to the cursor position when the file was last exited. No marks are saved for files that start with any string given with the "r" flag in 'shada'. This can be -used to avoid saving marks for files on removable media (for MS-DOS you would +used to avoid saving marks for files on removable media (for Windows you would use "ra:,rb:". The |v:oldfiles| variable is filled with the file names that the ShaDa file has marks for. diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 67550365a3..81ba639dbe 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 7.4. Last change: 2015 Jan 07 +*syntax.txt* For Vim version 7.4. Last change: 2015 Dec 19 VIM REFERENCE MANUAL by Bram Moolenaar @@ -71,10 +71,10 @@ with: > For a color terminal see |:hi-normal-cterm|. For setting up your own colors syntax highlighting see |syncolor|. -NOTE: The syntax files on MS-DOS and Windows have lines that end in <CR><NL>. -The files for Unix end in <NL>. This means you should use the right type of -file for your system. Although on MS-DOS and Windows the right format is -automatically selected if the 'fileformats' option is not empty. +NOTE: The syntax files on Windows have lines that end in <CR><NL>. The files +for Unix end in <NL>. This means you should use the right type of file for +your system. Although on Windows the right format is automatically selected +if the 'fileformats' option is not empty. NOTE: When using reverse video ("gvim -fg white -bg black"), the default value of 'background' will not be set until the GUI window is opened, which is after @@ -206,7 +206,8 @@ thing. These are then linked to a highlight group that specifies the color. A syntax group name doesn't specify any color or attributes itself. The name for a highlight or syntax group must consist of ASCII letters, digits -and the underscore. As a regexp: "[a-zA-Z0-9_]*" +and the underscore. As a regexp: "[a-zA-Z0-9_]*". However, Vim does not give +an error when using other characters. To be able to allow each user to pick his favorite set of colors, there must be preferred names for highlight groups that are common for many languages. @@ -416,18 +417,19 @@ and last line to be converted. Example, using the last set Visual area: > *:TOhtml* :[range]TOhtml The ":TOhtml" command is defined in a standard plugin. This command will source |2html.vim| for you. When a - range is given, set |g:html_start_line| and - |g:html_end_line| to the start and end of the range, - respectively. Default range is the entire buffer. - - If the current window is part of a |diff|, unless - |g:html_diff_one_file| is set, :TOhtml will convert - all windows which are part of the diff in the current - tab and place them side-by-side in a <table> element - in the generated HTML. With |g:html_line_ids| you can - jump to lines in specific windows with (for example) - #W1L42 for line 42 in the first diffed window, or - #W3L87 for line 87 in the third. + range is given, this command sets |g:html_start_line| + and |g:html_end_line| to the start and end of the + range, respectively. Default range is the entire + buffer. + + If the current window is part of a |diff|, unless + |g:html_diff_one_file| is set, :TOhtml will convert + all windows which are part of the diff in the current + tab and place them side-by-side in a <table> element + in the generated HTML. With |g:html_line_ids| you can + jump to lines in specific windows with (for example) + #W1L42 for line 42 in the first diffed window, or + #W3L87 for line 87 in the third. Examples: > @@ -741,6 +743,22 @@ and UTF-32 instead, use: > Note that documents encoded in either UTF-32 or UTF-16 have known compatibility problems with some major browsers. + *g:html_font* +Default: "monospace" +You can specify the font or fonts used in the converted document using +g:html_font. If this option is set to a string, then the value will be +surrounded with single quotes. If this option is set to a list then each list +item is surrounded by single quotes and the list is joined with commas. Either +way, "monospace" is added as the fallback generic family name and the entire +result used as the font family (using CSS) or font face (if not using CSS). +Examples: > + + " font-family: 'Consolas', monospace; + :let g:html_font = "Consolas" + + " font-family: 'DejaVu Sans Mono', 'Consolas', monospace; + :let g:html_font = ["DejaVu Sans Mono", "Consolas"] +< *convert-to-XML* *convert-to-XHTML* *g:html_use_xhtml* Default: 0. When 0, generate standard HTML 4.01 (strict when possible). @@ -1058,7 +1076,8 @@ CPP *cpp.vim* *ft-cpp-syntax* Most of things are same as |ft-c-syntax|. Variable Highlight ~ -cpp_no_c11 don't highlight C++11 standard items +cpp_no_cpp11 don't highlight C++11 standard items +cpp_no_cpp14 don't highlight C++14 standard items CSH *csh.vim* *ft-csh-syntax* @@ -1130,6 +1149,8 @@ there are very long lines in the file. To disable translations: > :let diff_translations = 0 +Also see |diff-slow|. + DIRCOLORS *dircolors.vim* *ft-dircolors-syntax* @@ -1412,34 +1433,28 @@ form, then > :let fortran_fixed_source=1 in your vimrc prior to the :syntax on command. -If the form of the source code depends upon the file extension, then it is -most convenient to set fortran_free_source in a ftplugin file. For more -information on ftplugin files, see |ftplugin|. For example, if all your -fortran files with an .f90 extension are written in free source form and the -rest in fixed source form, add the following code to your ftplugin file > - let s:extfname = expand("%:e") - if s:extfname ==? "f90" - let fortran_free_source=1 - unlet! fortran_fixed_source - else - let fortran_fixed_source=1 - unlet! fortran_free_source - endif -Note that this will work only if the "filetype plugin indent on" command -precedes the "syntax on" command in your vimrc file. +If the form of the source code depends, in a non-standard way, upon the file +extension, then it is most convenient to set fortran_free_source in a ftplugin +file. For more information on ftplugin files, see |ftplugin|. Note that this +will work only if the "filetype plugin indent on" command precedes the "syntax +on" command in your .vimrc file. + When you edit an existing fortran file, the syntax script will assume free source form if the fortran_free_source variable has been set, and assumes fixed source form if the fortran_fixed_source variable has been set. If neither of these variables have been set, the syntax script attempts to -determine which source form has been used by examining the first five columns -of the first 250 lines of your file. If no signs of free source form are -detected, then the file is assumed to be in fixed source form. The algorithm -should work in the vast majority of cases. In some cases, such as a file that -begins with 250 or more full-line comments, the script may incorrectly decide -that the fortran code is in fixed form. If that happens, just add a -non-comment statement beginning anywhere in the first five columns of the -first twenty five lines, save (:w) and then reload (:e!) the file. +determine which source form has been used by examining the file extension +using conventions common to the ifort, gfortran, Cray, NAG, and PathScale +compilers (.f, .for, .f77 for fixed-source, .f90, .f95, .f03, .f08 for +free-source). If none of this works, then the script examines the first five +columns of the first 500 lines of your file. If no signs of free source form +are detected, then the file is assumed to be in fixed source form. The +algorithm should work in the vast majority of cases. In some cases, such as a +file that begins with 500 or more full-line comments, the script may +incorrectly decide that the fortran code is in fixed form. If that happens, +just add a non-comment statement beginning anywhere in the first five columns +of the first twenty five lines, save (:w) and then reload (:e!) the file. Tabs in fortran files ~ Tabs are not recognized by the Fortran standards. Tabs are not a good idea in @@ -3304,6 +3319,32 @@ must not click outside of the pixel strings, but feel free to improve it. It will look much better with a font in a quadratic cell size, e.g. for X: > :set guifont=-*-clean-medium-r-*-*-8-*-*-*-*-80-* + +YAML *yaml.vim* *ft-yaml-syntax* + + *g:yaml_schema* *b:yaml_schema* +A YAML schema is a combination of a set of tags and a mechanism for resolving +non-specific tags. For user this means that YAML parser may, depending on +plain scalar contents, treat plain scalar (which can actually be only string +and nothing else) as a value of the other type: null, boolean, floating-point, +integer. `g:yaml_schema` option determines according to which schema values +will be highlighted specially. Supported schemas are + +Schema Description ~ +failsafe No additional highlighting. +json Supports JSON-style numbers, booleans and null. +core Supports more number, boolean and null styles. +pyyaml In addition to core schema supports highlighting timestamps, + but there are some differences in what is recognized as + numbers and many additional boolean values not present in core + schema. + +Default schema is `core`. + +Note that schemas are not actually limited to plain scalars, but this is the +only difference between schemas defined in YAML specification and the only +difference defined in the syntax file. + ============================================================================== 5. Defining a syntax *:syn-define* *E410* @@ -3401,7 +3442,7 @@ DEFINING KEYWORDS *:syn-keyword* :syntax keyword Type contained int long char :syntax keyword Type int long contained char :syntax keyword Type int long char contained -< *E789* +< *E789* *E890* When you have a keyword with an optional tail, like Ex commands in Vim, you can put the optional characters inside [], to define all the variations at once: > @@ -3655,6 +3696,7 @@ Whether or not it is actually concealed depends on the value of the 'conceallevel' option. The 'concealcursor' option is used to decide whether concealable items in the current line are displayed unconcealed to be able to edit the line. +Another way to conceal text with with |matchadd()|. concealends *:syn-concealends* @@ -4101,7 +4143,7 @@ example, for instance, can be done like this: > As can be seen here, the \z actually does double duty. In the start pattern, it marks the "\(\I\i*\)" sub-expression as external; in the end pattern, it -changes the \1 back-reference into an external reference referring to the +changes the \z1 back-reference into an external reference referring to the first external sub-expression in the start pattern. External references can also be used in skip patterns: > :syn region foo start="start \(\I\i*\)" skip="not end \z1" end="end \z1" @@ -4236,7 +4278,7 @@ If the "maxlines={N}" argument is given, the number of lines that are searched for a comment or syncing pattern is restricted to N lines backwards (after adding "minlines"). This is useful if you have few things to sync on and a slow machine. Example: > - :syntax sync ccomment maxlines=500 + :syntax sync maxlines=500 ccomment < *:syn-sync-linebreaks* When using a pattern that matches multiple lines, a change in one line may @@ -4504,7 +4546,7 @@ mentioned for the default values. See |:verbose-cmd| for more information. *highlight-args* *E416* *E417* *E423* There are three types of terminals for highlighting: term a normal terminal (vt100, xterm) -cterm a color terminal (MS-DOS console, color-xterm, these have the "Co" +cterm a color terminal (Windows console, color-xterm, these have the "Co" termcap entry) gui the GUI @@ -4629,6 +4671,8 @@ ctermbg={color-nr} *highlight-ctermbg* Note that for some color terminals these names may result in the wrong colors! + You can also use "NONE" to remove the color. + *:hi-normal-cterm* When setting the "ctermfg" or "ctermbg" colors for the Normal group, these will become the colors used for the non-highlighted text. @@ -4650,7 +4694,7 @@ ctermbg={color-nr} *highlight-ctermbg* *E419* *E420* When Vim knows the normal foreground and background colors, "fg" and "bg" can be used as color names. This only works after setting the - colors for the Normal group and for the MS-DOS console. Example, for + colors for the Normal group and for the Windows console. Example, for reverse video: > :highlight Visual ctermfg=bg ctermbg=fg < Note that the colors are used that are valid at the moment this @@ -4676,7 +4720,7 @@ font={font-name} *highlight-font* When setting the font for the "Normal" group, this becomes the default font (until the 'guifont' option is changed; the last one set is used). - The following only works with Motif and Athena, not with other GUIs: + The following only works with Motif not with other GUIs: When setting the font for the "Menu" group, the menus will be changed. When setting the font for the "Tooltip" group, the tooltips will be changed. @@ -4864,11 +4908,6 @@ Menu Current font, background and foreground colors of the menus. Also used for the toolbar. Applicable highlight arguments: font, guibg, guifg. - NOTE: For Motif and Athena the font argument actually - specifies a fontset at all times, no matter if 'guifontset' is - empty, and as such it is tied to the current |:language| when - set. - *hl-Scrollbar* Scrollbar Current background and foreground of the main window's scrollbars. @@ -4878,11 +4917,6 @@ Scrollbar Current background and foreground of the main window's Tooltip Current font, background and foreground of the tooltips. Applicable highlight arguments: font, guibg, guifg. - NOTE: For Motif and Athena the font argument actually - specifies a fontset at all times, no matter if 'guifontset' is - empty, and as such it is tied to the current |:language| when - set. - ============================================================================== 13. Linking groups *:hi-link* *:highlight-link* *E412* *E413* diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 44e770dc12..70e6953211 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -48,8 +48,8 @@ A double click with the mouse in the non-GUI tab pages line opens a new, empty tab page. It is placed left of the position of the click. The first click may select another tab page first, causing an extra screen update. -This also works in a few GUI versions, esp. Win32 and Motif. But only when -clicking right of the labels. +This also works in a few GUI versions, esp. Win32. But only when clicking +right of the labels. In the GUI tab pages line you can use the right mouse button to open menu. |tabline-menu|. @@ -197,22 +197,29 @@ REORDERING TAB PAGES: Move the current tab page to after tab page N. Use zero to make the current tab page the first one. Without N the tab page is made the last one. > + :.tabmove " do nothing :-tabmove " move the tab page to the left - :tabmove " move the tab page to the right - :.tabmove " as above - :+tabmove " as above + :+tabmove " move the tab page to the right :0tabmove " move the tab page to the beginning of the tab " list - :$tabmove " move the tab page to the end of the tab list -< + :tabmove 0 " as above + :tabmove " move the tab page to the last + :$tabmove " as above + :tabmove $ " as above + :tabm[ove] +[N] :tabm[ove] -[N] Move the current tab page N places to the right (with +) or to - the left (with -). + the left (with -). > + :tabmove - " move the tab page to the left + :tabmove -1 " as above + :tabmove + " move the tab page to the right + :tabmove +1 " as above + Note that although it is possible to move a tab behind the N-th one by using -:Ntabmove, it is impossible to move it by N places by using :+Ntabmove. For -clarification what +N means in this context see |[range]|. +:Ntabmove. And move it by N places by using :+Ntabmove. For clarification what ++N means in this context see |[range]|. LOOPING OVER TAB PAGES: @@ -234,7 +241,8 @@ LOOPING OVER TAB PAGES: current tab page. {cmd} can contain '|' to concatenate several commands. {cmd} must not open or close tab pages or reorder them. - Also see |:windo|, |:argdo| and |:bufdo|. + Also see |:windo|, |:argdo|, |:bufdo|, |:cdo|, |:ldo|, |:cfdo| + and |:lfdo|. ============================================================================== 3. Other items *tab-page-other* @@ -265,8 +273,8 @@ window on the same buffer and then edit another buffer. Thus ":tabnew" triggers: WinLeave leave current window TabLeave leave current tab page - TabEnter enter new tab page WinEnter enter window in new tab page + TabEnter enter new tab page BufLeave leave current buffer BufEnter enter new empty buffer @@ -274,8 +282,8 @@ When switching to another tab page the order is: BufLeave WinLeave TabLeave - TabEnter WinEnter + TabEnter BufEnter When entering a new tab page (|:tabnew|), TabNew is triggered before TabEnter diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 7d3697db07..75d820d072 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -84,11 +84,13 @@ changed, to avoid confusion when using ":tnext". It is changed when using ":tag {ident}". The ignore-case matches are not found for a ":tag" command when the -'ignorecase' option is off. They are found when a pattern is used (starting -with a "/") and for ":tselect", also when 'ignorecase' is off. Note that -using ignore-case tag searching disables binary searching in the tags file, -which causes a slowdown. This can be avoided by fold-case sorting the tag -file. See the 'tagbsearch' option for an explanation. +'ignorecase' option is off and 'tagcase' is "followic" or when 'tagcase' is +"match". They are found when a pattern is used (starting with a "/") and for +":tselect", also when 'ignorecase' is off and 'tagcase' is "followic" or when +'tagcase' is "match". Note that using ignore-case tag searching disables +binary searching in the tags file, which causes a slowdown. This can be +avoided by fold-case sorting the tag file. See the 'tagbsearch' option for an +explanation. ============================================================================== 2. Tag stack *tag-stack* *tagstack* *E425* @@ -418,12 +420,13 @@ file "tags". It can also be used to access a common tags file. The next file in the list is not used when: - A matching static tag for the current buffer has been found. - A matching global tag has been found. -This also depends on the 'ignorecase' option. If it is off, and the tags file -only has a match without matching case, the next tags file is searched for a -match with matching case. If no tag with matching case is found, the first -match without matching case is used. If 'ignorecase' is on, and a matching -global tag with or without matching case is found, this one is used, no -further tags files are searched. +This also depends on whether case is ignored. Case is ignored when +'ignorecase' is set and 'tagcase' is "followic", or when 'tagcase' is +"ignore". If case is not ignored, and the tags file only has a match without +matching case, the next tags file is searched for a match with matching case. +If no tag with matching case is found, the first match without matching case +is used. If case is ignored, and a matching global tag with or without +matching case is found, this one is used, no further tags files are searched. When a tag file name starts with "./", the '.' is replaced with the path of the current file. This makes it possible to use a tags file in the directory @@ -556,8 +559,10 @@ that indicates if the file was sorted. When this line is found, Vim uses binary searching for the tags file: !_TAG_FILE_SORTED<Tab>1<Tab>{anything} ~ -A tag file may be case-fold sorted to avoid a linear search when 'ignorecase' -is on. See 'tagbsearch' for details. The value '2' should be used then: +A tag file may be case-fold sorted to avoid a linear search when case is +ignored. (Case is ignored when 'ignorecase' is set and 'tagcase' is +"followic", or when 'tagcase' is "ignore".) See 'tagbsearch' for details. +The value '2' should be used then: !_TAG_FILE_SORTED<Tab>2<Tab>{anything} ~ The other tag that Vim recognizes, but only when compiled with the diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index 618d4e5df9..7d47368ba3 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -1,4 +1,4 @@ -*term.txt* For Vim version 7.4. Last change: 2014 May 13 +*term.txt* For Vim version 7.4. Last change: 2015 Nov 24 VIM REFERENCE MANUAL by Bram Moolenaar @@ -357,7 +357,7 @@ adjust the various t_ codes. This avoids the problem that the xterm can produce different codes, depending on the mode it is in (8-bit, VT102, VT220, etc.). The result is that codes like <xF1> are no longer needed. Note: This is only done on startup. If the xterm options are changed after -Vim has started, the escape sequences may not be recognized any more. +Vim has started, the escape sequences may not be recognized anymore. *xterm-resize* Window resizing with xterm only works if the allowWindowOps resource is @@ -425,11 +425,9 @@ cleared when scrolling). Unfortunately it is not possible to deduce from the termcap how cursor positioning should be done when using a scrolling region: Relative to the beginning of the screen or relative to the beginning of the scrolling region. -Most terminals use the first method. A known exception is the MS-DOS console -(pcterm). The 't_CS' option should be set to any string when cursor -positioning is relative to the start of the scrolling region. It should be -set to an empty string otherwise. It defaults to "yes" when 'term' is -"pcterm". +Most terminals use the first method. The 't_CS' option should be set to any +string when cursor positioning is relative to the start of the scrolling +region. It should be set to an empty string otherwise. Note for xterm users: The shifted cursor keys normally don't work. You can make them work with the xmodmap command and some mappings in Vim. @@ -672,10 +670,9 @@ border, the text is scrolled. A selection can be started by pressing the left mouse button on the first character, moving the mouse to the last character, then releasing the mouse button. You will not always see the selection until you release the button, -only in some versions (GUI, MS-DOS, WIN32) will the dragging be shown -immediately. Note that you can make the text scroll by moving the mouse at -least one character in the first/last line in the window when 'scrolloff' is -non-zero. +only in some versions (GUI, Windows) will the dragging be shown immediately. +Note that you can make the text scroll by moving the mouse at least one +character in the first/last line in the window when 'scrolloff' is non-zero. In Normal, Visual and Select mode clicking the right mouse button causes the Visual area to be extended. When 'mousemodel' is "popup", the left button has @@ -689,9 +686,9 @@ work on systems where the window manager consumes the mouse events when the alt key is pressed (it may move the window). *double-click* -Double, triple and quadruple clicks are supported when the GUI is active, -for MS-DOS and Win32, and for an xterm (if the gettimeofday() function is -available). For selecting text, extra clicks extend the selection: +Double, triple and quadruple clicks are supported when the GUI is active, for +Windows and for an xterm. For selecting text, extra clicks extend the +selection: click select ~ double word or % match *<2-LeftMouse>* triple line *<3-LeftMouse>* @@ -747,7 +744,7 @@ Mouse clicks can be mapped. The codes for mouse clicks are: The X1 and X2 buttons refer to the extra buttons found on some mice. The 'Microsoft Explorer' mouse has these buttons available to the right thumb. -Currently X1 and X2 only work on Win32 environments. +Currently X1 and X2 only work on Win32 and X11 environments. Examples: > :noremap <MiddleMouse> <LeftMouse><MiddleMouse> diff --git a/runtime/doc/uganda.txt b/runtime/doc/uganda.txt index ee1c36d676..c228c65542 100644 --- a/runtime/doc/uganda.txt +++ b/runtime/doc/uganda.txt @@ -190,7 +190,7 @@ child, you should have the intention to do this for at least one year. How do you know that the money will be spent right? First of all you have my personal guarantee as the author of Vim. I trust the people that are working -at the centre, I know them personally. Further more, the centre has been +at the centre, I know them personally. Furthermore, the centre has been co-sponsored and inspected by World Vision, Save the Children Fund and is now under the supervision of Pacific Academy Outreach Society. The centre is visited about once a year to check the progress (at our own cost). I have diff --git a/runtime/doc/undo.txt b/runtime/doc/undo.txt index 1342621516..c6c70ab6d2 100644 --- a/runtime/doc/undo.txt +++ b/runtime/doc/undo.txt @@ -250,8 +250,8 @@ ignored if its owner differs from the owner of the edited file, except when the owner of the undo file is the current user. Set 'verbose' to get a message about that when opening a file. -Undo files are normally saved in the same directory as the file. This can be -changed with the 'undodir' option. +Location of the undo files is controlled by the 'undodir' option, by default +they are saved to the dedicated directory in the application data folder. You can also save and restore undo histories by using ":wundo" and ":rundo" respectively: diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt index cd25b14e32..6a288f8965 100644 --- a/runtime/doc/usr_02.txt +++ b/runtime/doc/usr_02.txt @@ -1,4 +1,4 @@ -*usr_02.txt* For Vim version 7.4. Last change: 2010 Jul 20 +*usr_02.txt* For Vim version 7.4. Last change: 2015 Apr 12 VIM USER MANUAL - by Bram Moolenaar @@ -29,11 +29,10 @@ To start Vim, enter this command: > gvim file.txt -In UNIX you can type this at any command prompt. If you are running Microsoft -Windows, open an MS-DOS prompt window and enter the command. - In either case, Vim starts editing a file called file.txt. Because this -is a new file, you get a blank window. This is what your screen will look -like: +On Unix you can type this at any command prompt. If you are running Windows, +open a command prompt window and enter the command. In either case, Vim +starts editing a file called file.txt. Because this is a new file, you get a +blank window. This is what your screen will look like: +---------------------------------------+ |# | @@ -61,10 +60,9 @@ use this command: > the editing occurs inside your command window. In other words, if you are running inside an xterm, the editor uses your xterm window. If you are using -an MS-DOS command prompt window under Microsoft Windows, the editing occurs -inside this window. The text in the window will look the same for both -versions, but with gvim you have extra features, like a menu bar. More about -that later. +the command prompt under Microsoft Windows, the editing occurs inside this +window. The text in the window will look the same for both versions, but with +gvim you have extra features, like a menu bar. More about that later. ============================================================================== *02.2* Inserting text @@ -80,7 +78,7 @@ mistakes; you can correct them later. To enter the following programmer's limerick, this is what you type: > iA very intelligent turtle - Found programming UNIX a hurdle + Found programming Unix a hurdle After typing "turtle" you press the <Enter> key to start a new line. Finally you press the <Esc> key to stop Insert mode and go back to Normal mode. You @@ -88,7 +86,7 @@ now have two lines of text in your Vim window: +---------------------------------------+ |A very intelligent turtle | - |Found programming UNIX a hurdle | + |Found programming Unix a hurdle | |~ | |~ | | | @@ -110,7 +108,7 @@ of the window. This indicates you are in Insert mode. +---------------------------------------+ |A very intelligent turtle | - |Found programming UNIX a hurdle | + |Found programming Unix a hurdle | |~ | |~ | |-- INSERT -- | @@ -187,7 +185,7 @@ look like this: +---------------------------------------+ |intelligent turtle | - |Found programming UNIX a hurdle | + |Found programming Unix a hurdle | |~ | |~ | | | @@ -202,7 +200,7 @@ insert mode (the final <Esc>). The result: +---------------------------------------+ |A young intelligent turtle | - |Found programming UNIX a hurdle | + |Found programming Unix a hurdle | |~ | |~ | | | @@ -215,7 +213,7 @@ To delete a whole line use the "dd" command. The following line will then move up to fill the gap: +---------------------------------------+ - |Found programming UNIX a hurdle | + |Found programming Unix a hurdle | |~ | |~ | |~ | @@ -332,7 +330,7 @@ Insert mode. Then you can type the text for the new line. Suppose the cursor is somewhere in the first of these two lines: A very intelligent turtle ~ - Found programming UNIX a hurdle ~ + Found programming Unix a hurdle ~ If you now use the "o" command and type new text: > @@ -342,7 +340,7 @@ The result is: A very intelligent turtle ~ That liked using Vim ~ - Found programming UNIX a hurdle ~ + Found programming Unix a hurdle ~ The "O" command (uppercase) opens a line above the cursor. @@ -518,9 +516,11 @@ Summary: *help-summary* > :help subject() < Function "subject". > :help -subject -< Command-line option "-subject". > +< Command-line argument "-subject". > :help +subject < Compile-time feature "+subject". > + :help /* +< Regular expression item "*" > :help EventName < Autocommand event "EventName". > :help digraphs.txt diff --git a/runtime/doc/usr_03.txt b/runtime/doc/usr_03.txt index a8139d60ca..b8f65d9309 100644 --- a/runtime/doc/usr_03.txt +++ b/runtime/doc/usr_03.txt @@ -1,4 +1,4 @@ -*usr_03.txt* For Vim version 7.4. Last change: 2006 Jun 21 +*usr_03.txt* For Vim version 7.4. Last change: 2015 Dec 12 VIM USER MANUAL - by Bram Moolenaar @@ -57,8 +57,11 @@ paragraph, much faster than using "l". "b" does the same in the other direction. A word ends at a non-word character, such as a ".", "-" or ")". To change -what Vim considers to be a word, see the 'iskeyword' option. - It is also possible to move by white-space separated WORDs. This is not a +what Vim considers to be a word, see the 'iskeyword' option. If you try this +out in the help directly, 'iskeyword' needs to be reset for the examples to +work: > + :set iskeyword& +It is also possible to move by white-space separated WORDs. This is not a word in the normal sense, that's why the uppercase is used. The commands for moving by WORDs are also uppercase, as this figure shows: @@ -528,7 +531,7 @@ MATCHING ANY SINGLE CHARACTER The . (dot) character matches any existing character. For example, the pattern "c.m" matches a string whose first character is a c, whose second -character is anything, and whose the third character is m. Example: +character is anything, and whose third character is m. Example: We use a computer that became the cummin winter. ~ xxx xxx xxx diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index 86fcf0cc2f..5aecf33557 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -37,9 +37,10 @@ for you), you can edit it this way: > If you don't have a vimrc file yet, see |init.vim| to find out where you can create a vimrc file. -For Unix and Macintosh this file is always used and is recommended: +This file is always used and is recommended: - ~/.config/nvim/init.vim ~ + ~/.config/nvim/init.vim (Unix and OSX) ~ + ~/AppData/Local/nvim/init.vim (Windows) ~ The vimrc file can contain all the commands that you type after a colon. The most simple ones are for setting options. For example, if you want Vim to diff --git a/runtime/doc/usr_06.txt b/runtime/doc/usr_06.txt index 1cb3eb8673..b4b495ff9f 100644 --- a/runtime/doc/usr_06.txt +++ b/runtime/doc/usr_06.txt @@ -152,7 +152,7 @@ You could also write your own color scheme. This is how you do it: directory. For Unix, this should work: > !mkdir -p ~/.config/nvim/colors - !cp $VIMRUNTIME/colors/morning.vim ~/.vim/colors/mine.vim + !cp $VIMRUNTIME/colors/morning.vim ~/.config/nvim/colors/mine.vim < This is done from Vim, because it knows the value of $VIMRUNTIME. diff --git a/runtime/doc/usr_09.txt b/runtime/doc/usr_09.txt index 05cc32bceb..d68d734b8f 100644 --- a/runtime/doc/usr_09.txt +++ b/runtime/doc/usr_09.txt @@ -134,10 +134,10 @@ The following command makes the mouse work like a Microsoft Windows mouse: > :behave mswin -The default behavior of the mouse on UNIX systems is xterm. The default -behavior on a Microsoft Windows system is selected during the installation -process. For details about what the two behaviors are, see |:behave|. Here -follows a summary. +The default behavior of the mouse on Unix systems is xterm. The default +behavior on Windows systems is selected during the installation process. For +details about what the two behaviors are, see |:behave|. Here follows a +summary. XTERM MOUSE BEHAVIOR diff --git a/runtime/doc/usr_10.txt b/runtime/doc/usr_10.txt index 64b0181c35..bf7ba18222 100644 --- a/runtime/doc/usr_10.txt +++ b/runtime/doc/usr_10.txt @@ -698,10 +698,10 @@ still be something that an external command can do better or faster. through an external program. In other words, it runs the system command represented by {program}, giving it the block of text represented by {motion} as input. The output of this command then replaces the selected block. - Because this summarizes badly if you are unfamiliar with UNIX filters, take + Because this summarizes badly if you are unfamiliar with Unix filters, take a look at an example. The sort command sorts a file. If you execute the following command, the unsorted file input.txt will be sorted and written to -output.txt. (This works on both UNIX and Microsoft Windows.) > +output.txt. This works on both Unix and Windows. > sort <input.txt >output.txt diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt index 2a810af6c2..1a72c300d7 100644 --- a/runtime/doc/usr_11.txt +++ b/runtime/doc/usr_11.txt @@ -293,7 +293,6 @@ If you really don't want to see this message, you can add the 'A' flag to the 'updatecount' Number of key strokes after which the swap file is flushed to disk. 'updatetime' Timeout after which the swap file is flushed to disk. -'swapsync' Whether the disk is synced when the swap file is flushed. 'directory' List of directory names where to store the swap file. 'maxmem' Limit for memory usage before writing text to the swap file. 'maxmemtot' Same, but for all files in total. diff --git a/runtime/doc/usr_12.txt b/runtime/doc/usr_12.txt index fba1b53274..237abae55f 100644 --- a/runtime/doc/usr_12.txt +++ b/runtime/doc/usr_12.txt @@ -309,7 +309,7 @@ matches can be found. ============================================================================== *12.8* Find where a word is used -If you are a UNIX user, you can use a combination of Vim and the grep command +If you are a Unix user, you can use a combination of Vim and the grep command to edit all the files that contain a given word. This is extremely useful if you are working on a program and want to view or edit all the files that contain a specific variable. @@ -324,7 +324,7 @@ will only list the files containing the word and not print the matching lines. The word it is searching for is "frame_counter". Actually, this can be any regular expression. (Note: What grep uses for regular expressions is not exactly the same as what Vim uses.) - The entire command is enclosed in backticks (`). This tells the UNIX shell + The entire command is enclosed in backticks (`). This tells the Unix shell to run this command and pretend that the results were typed on the command line. So what happens is that the grep command is run and produces a list of files, these files are put on the Vim command line. This results in Vim diff --git a/runtime/doc/usr_23.txt b/runtime/doc/usr_23.txt index bdb3b7afd6..4761203512 100644 --- a/runtime/doc/usr_23.txt +++ b/runtime/doc/usr_23.txt @@ -25,7 +25,7 @@ Back in the early days, the old Teletype machines used two characters to start a new line. One to move the carriage back to the first position (carriage return, <CR>), another to move the paper up (line feed, <LF>). When computers came out, storage was expensive. Some people decided that -they did not need two characters for end-of-line. The UNIX people decided +they did not need two characters for end-of-line. The Unix people decided they could use <Line Feed> only for end-of-line. The Apple people standardized on <CR>. The MS-DOS (and Microsoft Windows) folks decided to keep the old <CR><LF>. @@ -34,7 +34,7 @@ have line-break problems. The Vim editor automatically recognizes the different file formats and handles things properly behind your back. The option 'fileformats' contains the various formats that will be tried when a new file is edited. The following command, for example, tells Vim to -try UNIX format first and MS-DOS format second: > +try Unix format first and MS-DOS format second: > :set fileformats=unix,dos @@ -97,12 +97,12 @@ CONVERSION You can use the 'fileformat' option to convert from one file format to another. Suppose, for example, that you have an MS-DOS file named README.TXT -that you want to convert to UNIX format. Start by editing the MS-DOS format +that you want to convert to Unix format. Start by editing the MS-DOS format file: > vim README.TXT Vim will recognize this as a dos format file. Now change the file format to -UNIX: > +Unix: > :set fileformat=unix :write diff --git a/runtime/doc/usr_24.txt b/runtime/doc/usr_24.txt index 46a22c683c..ba9d083fe0 100644 --- a/runtime/doc/usr_24.txt +++ b/runtime/doc/usr_24.txt @@ -563,9 +563,9 @@ that combination. Thus CTRL-K dP also works. Since there is no digraph for "dP" Vim will also search for a "Pd" digraph. Note: - The digraphs depend on the character set that Vim assumes you are - using. On MS-DOS they are different from MS-Windows. Always use - ":digraphs" to find out which digraphs are currently available. + The digraphs depend on the character set that Vim assumes you + are using. Always use ":digraphs" to find out which digraphs are + currently available. You can define your own digraphs. Example: > diff --git a/runtime/doc/usr_27.txt b/runtime/doc/usr_27.txt index fb096593f2..d837610698 100644 --- a/runtime/doc/usr_27.txt +++ b/runtime/doc/usr_27.txt @@ -83,7 +83,7 @@ matter if 'ignorecase' or 'smartcase' was changed. Note: If your search takes much longer than you expected, you can interrupt - it with CTRL-C on Unix and CTRL-Break on MS-DOS and MS-Windows. + it with CTRL-C on Unix and CTRL-Break on Windows. ============================================================================== *27.2* Wrapping around the file end diff --git a/runtime/doc/usr_29.txt b/runtime/doc/usr_29.txt index 22de2f6ce6..e495aad06d 100644 --- a/runtime/doc/usr_29.txt +++ b/runtime/doc/usr_29.txt @@ -255,7 +255,8 @@ function. RELATED ITEMS -You can set 'ignorecase' to make case in tag names be ignored. +To make case in tag names be ignored, you can set 'ignorecase' while leaving +'tagcase' as "followic", or set 'tagcase' to "ignore". The 'tagbsearch' option tells if the tags file is sorted or not. The default is to assume a sorted tags file, which makes a tags search a lot faster, but diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index fe9d9b4d62..fc8419a522 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1,4 +1,4 @@ -*usr_41.txt* For Vim version 7.4. Last change: 2014 Aug 16 +*usr_41.txt* For Vim version 7.4. Last change: 2015 Nov 30 VIM USER MANUAL - by Bram Moolenaar @@ -888,6 +888,11 @@ Mappings: *mapping-functions* maparg() get rhs of a mapping wildmenumode() check if the wildmode is active +Testing: *test-functions* + assert_equal() assert that two expressions values are equal + assert_false() assert that an expression is false + assert_true() assert that an expression is true + Various: *various-functions* mode() get current editing mode visualmode() last visual mode used @@ -916,6 +921,7 @@ Various: *various-functions* py3eval() evaluate Python expression (|+python3|) pyeval() evaluate Python expression (|+python|) + wordcount() get byte/word/char count of buffer ============================================================================== *41.7* Defining a function @@ -1494,8 +1500,8 @@ Here is a summary of items that apply to Vim scripts. They are also mentioned elsewhere, but form a nice checklist. The end-of-line character depends on the system. For Unix a single <NL> -character is used. For MS-DOS, Windows and the like, <CR><LF> is used. -This is important when using mappings that end in a <CR>. See |:source_crnl|. +character is used. For Windows <CR><LF> is used. This is important when +using mappings that end in a <CR>. See |:source_crnl|. WHITE SPACE diff --git a/runtime/doc/usr_43.txt b/runtime/doc/usr_43.txt index e61e6af660..bab446af3c 100644 --- a/runtime/doc/usr_43.txt +++ b/runtime/doc/usr_43.txt @@ -1,4 +1,4 @@ -*usr_43.txt* For Vim version 7.4. Last change: 2008 Dec 28 +*usr_43.txt* For Vim version 7.4. Last change: 2015 Oct 23 VIM USER MANUAL - by Bram Moolenaar @@ -45,6 +45,7 @@ three-line comment. You do this with only two steps: setlocal softtabstop=4 noremap <buffer> <LocalLeader>c o/**************<CR><CR>/<Esc> + let b:undo_ftplugin = "setl softtabstop< | unmap <buffer> <LocalLeader>c" Try editing a C file. You should notice that the 'softtabstop' option is set to 4. But when you edit another file it's reset to the default zero. That is @@ -59,6 +60,11 @@ buffer. This works with any mapping command: ":map!", ":vmap", etc. The |<LocalLeader>| in the mapping is replaced with the value of the "maplocalleader" variable. +The line to set b:undo_ftplugin is for when the filetype is set to another +value. In that case you will want to undo your preferences. The +b:undo_ftplugin variable is executed as a command. Watch out for characters +with a special meaning inside a string, such as a backslash. + You can find examples for filetype plugins in this directory: > $VIMRUNTIME/ftplugin/ diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 117e977e0d..af4224993f 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 7.4. Last change: 2014 Aug 06 +*various.txt* For Vim version 7.4. Last change: 2015 Nov 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -222,6 +222,10 @@ g8 Print the hex values of the bytes used in the modified, but can be forced with "!". See |termopen()| and |nvim-terminal-emulator| for more information. + To switch to terminal mode automatically: +> + autocmd BufEnter term://* startinsert +< *:!cmd* *:!* *E34* :!{cmd} Execute {cmd} with 'shell'. See also |:terminal|. @@ -272,19 +276,15 @@ g8 Print the hex values of the bytes used in the :!! Repeat last ":!{cmd}". *:ve* *:version* -:ve[rsion] Print the version number of the editor. If the - compiler used understands "__DATE__" the compilation - date is mentioned. Otherwise a fixed release-date is - shown. - The following lines contain information about which - features were enabled when Vim was compiled. When - there is a preceding '+', the feature is included, - when there is a '-' it is excluded. To change this, - you have to edit feature.h and recompile Vim. - To check for this in an expression, see |has()|. - Here is an overview of the features. - The first column shows the smallest version in which - they are included: +:ve[rsion] Print the version number of the editor. The following + lines contain information about which features were + enabled when Vim was compiled. When there is a + preceding '+', the feature is included, when there is + a '-' it is excluded. To change this, you have to + edit feature.h and recompile Vim. To check for this + in an expression, see |has()|. Here is an overview of + the features. The first column shows the smallest + version in which they are included: T tiny S small N normal @@ -299,9 +299,6 @@ g8 Print the hex values of the bytes used in the *+acl* |ACL| support included B *+arabic* |Arabic| language support N *+autocmd* |:autocmd|, automatic commands -m *+balloon_eval* |balloon-eval| support. Included when compiling with - supported GUI (Motif, GTK, GUI) and either - Netbeans/Sun Workshop integration or |+eval| feature. N *+browse* |:browse| command N *+byte_offset* support for 'o' flag in 'statusline' option, "go" and ":goto" commands. @@ -321,7 +318,6 @@ N *+dialog_gui* Support for |:confirm| with GUI dialog. N *+dialog_con* Support for |:confirm| with console dialog. N *+dialog_con_gui* Support for |:confirm| with GUI and console dialog. N *+digraphs* |digraphs| *E196* - *+dnd* Support for DnD into the "~ register |quote_~|. N *+eval* expression evaluation |eval.txt| N *+ex_extra* Vim's extra Ex commands: |:center|, |:left|, |:normal|, |:retab| and |:right| @@ -331,12 +327,7 @@ N *+file_in_path* |gf|, |CTRL-W_f| and |<cfile>| N *+find_in_path* include file searches: |[I|, |:isearch|, |CTRL-W_CTRL-I|, |:checkpath|, etc. N *+folding* |folding| - *+footer* |gui-footer| N *+gettext* message translations |multi-lang| - *+GUI_Athena* Unix only: Athena |GUI| - *+GUI_neXtaw* Unix only: neXtaw |GUI| - *+GUI_GTK* Unix only: GTK+ |GUI| - *+GUI_Motif* Unix only: Motif |GUI| *+iconv* Compiled with the |iconv()| function *+iconv/dyn* Likewise |iconv-dynamic| |/dyn| N *+insert_expand* |insert_expand| Insert mode completion @@ -376,9 +367,11 @@ N *+startuptime* |--startuptime| argument N *+statusline* Options 'statusline', 'rulerformat' and special formats of 'titlestring' and 'iconstring' N *+syntax* Syntax highlighting |syntax| +N *+tablineat* 'tabline' option recognizing %@Func@ items. N *+tag_binary* binary searching in tags file |tag-binary-search| N *+tag_old_static* old method for static tags |tag-old-static| m *+tag_any_white* any white space allowed in tags file |tag-any-white| +B *+termguicolors* 24-bit color in xterm-compatible terminals support *+terminfo* uses |terminfo| instead of termcap N *+termresponse* support for |t_RV| and |v:termresponse| N *+textobjects* |text-objects| selection @@ -576,17 +569,11 @@ which it was defined is reported. :5sleep "sleep for five seconds :sleep 100m "sleep for a hundred milliseconds 10gs "sleep for ten seconds -< Can be interrupted with CTRL-C (CTRL-Break on MS-DOS). +< Can be interrupted with CTRL-C (CTRL-Break on Windows). "gs" stands for "goto sleep". While sleeping the cursor is positioned in the text, if at a visible position. - - *g_CTRL-A* -g CTRL-A Only when Vim was compiled with MEM_PROFILING defined - (which is very rare): print memory usage statistics. - Only useful for debugging Vim. - ============================================================================== 2. Using Vim like less or more *less* @@ -604,4 +591,12 @@ highlighting. The "h" key will give you a short overview of the available commands. +If you want to set options differently when using less, define the +LessInitFunc in your vimrc, for example: > + + func LessInitFunc() + set nocursorcolumn nocursorline + endfunc +< + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt index 7b61dbe6d7..ec35694c9e 100644 --- a/runtime/doc/vi_diff.txt +++ b/runtime/doc/vi_diff.txt @@ -1,4 +1,4 @@ -*vi_diff.txt* For Vim version 7.4. Last change: 2013 Aug 22 +*vi_diff.txt* For Vim version 7.4. Last change: 2015 Nov 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -44,8 +44,7 @@ Memory usage limits The option 'maxmem' ('mm') is used to set the maximum memory used for one buffer (in kilobytes). 'maxmemtot' is used to set the maximum memory used for -all buffers (in kilobytes). The defaults depend on the system used. For -MS-DOS, 'maxmemtot' is set depending on the amount of memory available. +all buffers (in kilobytes). The defaults depend on the system used. These are not hard limits, but tell Vim when to move text into a swap file. If you don't like Vim to swap to a file, set 'maxmem' and 'maxmemtot' to a very large value. The swap file will then only be used for recovery. If you @@ -57,15 +56,9 @@ argument when starting Vim. Support for different systems. Vim can be used on: - - All Unix systems (it works on all systems it was tested on, although - the GUI and Perl interface may not work everywhere). - - MS-DOS in real-mode (no additional drivers required). - - In protected mode on Windows 3.1 and MS-DOS (DPMI driver required). - - Windows 95 and Windows NT, with support for long file names. - - Macintosh - Note that on some systems features need to be disabled to reduce - resource usage, esp. on MS-DOS. For some outdated systems you need to - use an older Vim version. + - Modern Unix systems (*BSD, Linux, etc.) + - Windows (XP SP 2 or greater) + - OS X Multi level undo. |undo| 'u' goes backward in time, 'CTRL-R' goes forward again. Set option @@ -83,8 +76,7 @@ Graphical User Interface (GUI). |gui| Included support for GUI: menu's, mouse, scrollbars, etc. You can define your own menus. Better support for CTRL/SHIFT/ALT keys in combination with special keys and mouse. Supported for various - platforms, such as X11 (with Motif and Athena interfaces), GTK, Win32 - (Windows 95 and later), and Macintosh. + platforms such as Win32. Multiple windows and buffers. |windows.txt| Vim can split the screen into several windows, each editing a @@ -347,9 +339,8 @@ Printing. |printing| Mouse support. |mouse-using| The mouse is supported in the GUI version, in an xterm for Unix, for - BSDs with sysmouse, for Linux with gpm, for MS-DOS, and Win32. It - can be used to position the cursor, select the visual area, paste a - register, etc. + BSDs with sysmouse, for Linux with gpm, and for Win32. It can be used + to position the cursor, select the visual area, paste a register, etc. Usage of key names. |<>| |key-notation| Special keys now all have a name like <Up>, <End>, etc. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index c6e98bc40c..508712ca75 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -11,7 +11,7 @@ the "{Nvim}" tag. This document is a complete and centralized list of all these differences. 1. Configuration |nvim-configuration| -2. Option defaults |nvim-option-defaults| +2. Defaults |nvim-defaults| 3. Changed features |nvim-features-changed| 4. New features |nvim-features-new| 5. Missing legacy features |nvim-features-missing| @@ -21,14 +21,17 @@ these differences. ============================================================================== 1. Configuration *nvim-configuration* -- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for storing +- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for storing configuration. - Use `$XDG_CONFIG_HOME/nvim` instead of `.vim` to store configuration files. -- Use `$XDG_DATA_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent +- Use `$XDG_DATA_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent session information. ============================================================================== -2. Option defaults *nvim-option-defaults* +2. Defaults *nvim-defaults* + +- Syntax highlighting is enabled by default +- ":filetype plugin indent on" is enabled by default - 'autoindent' is set by default - 'autoread' is set by default @@ -45,7 +48,7 @@ these differences. - 'listchars' defaults to "tab:> ,trail:-,nbsp:+" - 'mouse' defaults to "a" - 'nocompatible' is always set -- 'nrformats' defaults to "hex" +- 'nrformats' defaults to "bin,hex" - 'sessionoptions' doesn't include "options" - 'smarttab' is set by default - 'tabpagemax' defaults to 50 @@ -68,54 +71,72 @@ are always available and may be used simultaneously in separate plugins. The |nvim-python|). |mkdir()| behaviour changed: -1. Assuming /tmp/foo does not exist and /tmp can be written to +1. Assuming /tmp/foo does not exist and /tmp can be written to mkdir('/tmp/foo/bar', 'p', 0700) will create both /tmp/foo and /tmp/foo/bar with 0700 permissions. Vim mkdir will create /tmp/foo with 0755. -2. If you try to create an existing directory with `'p'` (e.g. mkdir('/', +2. If you try to create an existing directory with `'p'` (e.g. mkdir('/', 'p')) mkdir() will silently exit. In Vim this was an error. 3. mkdir() error messages now include strerror() text when mkdir fails. 'encoding' cannot be changed after startup. |string()| and |:echo| behaviour changed: -1. No maximum recursion depth limit is applied to nested container +1. No maximum recursion depth limit is applied to nested container structures. -2. |string()| fails immediately on nested containers, not when recursion limit +2. |string()| fails immediately on nested containers, not when recursion limit was exceeded. 2. When |:echo| encounters duplicate containers like > let l = [] echo [l, l] < - it does not use "[...]" (was: "[[], [...]]", now: "[[], []]"). "..." is + it does not use "[...]" (was: "[[], [...]]", now: "[[], []]"). "..." is only used for recursive containers. -3. |:echo| printing nested containers adds "@level" after "..." designating - the level at which recursive container was printed: |:echo-self-refer|. - Same thing applies to |string()| (though it uses construct like - "{E724@level}"), but this is not reliable because |string()| continues to +3. |:echo| printing nested containers adds "@level" after "..." designating + the level at which recursive container was printed: |:echo-self-refer|. + Same thing applies to |string()| (though it uses construct like + "{E724@level}"), but this is not reliable because |string()| continues to error out. +4. Stringifyed infinite and NaN values now use |str2float()| and can be evaled + back. +5. (internal) Trying to print or stringify VAR_UNKNOWN in Vim results in + nothing, |E908|, in Neovim it is internal error. + +|json_decode()| behaviour changed: +1. It may output |msgpack-special-dict|. +2. |msgpack-special-dict| is emitted also in case of duplicate keys, while in + Vim it errors out. +3. It accepts only valid JSON. Trailing commas are not accepted. + +|json_encode()| behaviour slightly changed: now |msgpack-special-dict| values +are accepted, but |v:none| is not. + +*v:none* variable is absent. In Vim it represents “no value†in “js†strings +like "[,]" parsed as "[v:none]" by |js_decode()|. + +*js_encode()* and *js_decode()* functions are also absent. -Viminfo text files were replaced with binary (messagepack) ShaDa files. +Viminfo text files were replaced with binary (messagepack) ShaDa files. Additional differences: - |shada-c| has no effect. - |shada-s| now limits size of every item and not just registers. -- When reading ShaDa files items are merged according to the timestamp. +- When reading ShaDa files items are merged according to the timestamp. |shada-merging| -- 'viminfo' option got renamed to 'shada'. Old option is kept as an alias for +- 'viminfo' option got renamed to 'shada'. Old option is kept as an alias for compatibility reasons. -- |:wviminfo| was renamed to |:wshada|, |:rviminfo| to |:rshada|. Old +- |:wviminfo| was renamed to |:wshada|, |:rviminfo| to |:rshada|. Old commands are still kept. - |:oldfiles| supports !. -- When writing (|:wshada| without bang or at exit) it merges much more data, - and does this according to the timestamp. Vim merges only marks. +- When writing (|:wshada| without bang or at exit) it merges much more data, + and does this according to the timestamp. Vim merges only marks. |shada-merging| -- ShaDa file format was designed with forward and backward compatibility in +- ShaDa file format was designed with forward and backward compatibility in mind. |shada-compatibility| -- Some errors make ShaDa code keep temporary file in-place for user to decide - what to do with it. Vim deletes temporary file in these cases. +- Some errors make ShaDa code keep temporary file in-place for user to decide + what to do with it. Vim deletes temporary file in these cases. |shada-error-handling| -- Vim keeps no timestamps at all, neither in viminfo file nor in the instance +- Vim keeps no timestamps at all, neither in viminfo file nor in the instance itself. - ShaDa file keeps search direction (|v:searchforward|), viminfo does not. @@ -134,8 +155,8 @@ Meta (alt) chords are recognized (even in the terminal). Note: Meta chords are case-sensitive (<M-a> is distinguished from <M-A>). -Some `CTRL-SHIFT-...` key chords are distinguished from `CTRL-...` variants (even in -the terminal). Specifically, the following are known to work: +Some `CTRL-SHIFT-...` key chords are distinguished from `CTRL-...` variants +(even in the terminal). Specifically, the following are known to work: <C-Tab>, <C-S-Tab> <C-BS>, <C-S-BS> <C-Enter>, <C-S-Enter> @@ -199,9 +220,15 @@ Other options: 'cpoptions' ('g', 'w', 'H', '*', '-', 'j', and all POSIX flags were removed) 'guioptions' (only the 't' flag was removed) 'guipty' + 'imactivatefunc' + 'imactivatekey' + 'imstatusfunc' 'macatsui' + 'restorescreen' 'shelltype' 'shortname' + 'swapsync' + 'term' 'termencoding' (Vim 7.4.852 also removed this for Windows) 'textauto' 'textmode' @@ -209,11 +236,14 @@ Other options: 'toolbariconsize' 'ttybuiltin' 'ttymouse' + 'ttyscroll' + 'ttytype' 'weirdinvert' Other commands: :Print :fixdel + :helpfind :mode (no longer accepts an argument) :open :shell diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index c009bec66e..51b73223b6 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1,4 +1,4 @@ -*windows.txt* For Vim version 7.4. Last change: 2014 Dec 05 +*windows.txt* For Vim version 7.4. Last change: 2015 Nov 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -681,7 +681,6 @@ can also get to them with the buffer list commands, like ":bnext". When using the |:tab| modifier each argument is opened in a tab page. The last window is used if it's empty. Also see |++opt| and |+cmd|. - {only available when compiled with a GUI} ============================================================================== 8. Do a command in all buffers or windows *list-repeat* @@ -702,11 +701,12 @@ can also get to them with the buffer list commands, like ":bnext". the current window. {cmd} can contain '|' to concatenate several commands. {cmd} must not open or close windows or reorder them. - Also see |:tabdo|, |:argdo| and |:bufdo|. + Also see |:tabdo|, |:argdo|, |:bufdo|, |:cdo|, |:ldo|, + |:cfdo| and |:lfdo|. *:bufdo* :[range]bufdo[!] {cmd} Execute {cmd} in each buffer in the buffer list or if - [range[ is given only for buffers for which their + [range] is given only for buffers for which their buffer name is in the [range]. It works like doing this: > :bfirst @@ -728,7 +728,8 @@ can also get to them with the buffer list commands, like ":bnext". autocommand event is disabled by adding it to 'eventignore'. This considerably speeds up editing each buffer. - Also see |:tabdo|, |:argdo| and |:windo|. + Also see |:tabdo|, |:argdo|, |:windo|, |:cdo|, |:ldo|, + |:cfdo| and |:lfdo|. Examples: > @@ -984,8 +985,8 @@ list of buffers. |unlisted-buffer| (the term "unlisted" is a bit confusing then...). Each buffer has a unique number. That number will not change, - so you can always go to a specific buffer with ":buffer N" or - "N CTRL-^", where N is the buffer number. + thus you can always go to a specific buffer with ":buffer N" + or "N CTRL-^", where N is the buffer number. Indicators (chars in the same column are mutually exclusive): u an unlisted buffer (only displayed when [!] is used) @@ -1098,13 +1099,13 @@ list of buffers. |unlisted-buffer| the current buffer remains being edited. See |:buffer-!| for [!]. This will also edit a buffer that is not in the buffer list, without setting the 'buflisted' flag. - Also see ||+cmd|. + Also see |+cmd|. :[N]b[uffer][!] [+cmd] {bufname} Edit buffer for {bufname} from the buffer list. See |:buffer-!| for [!]. This will also edit a buffer that is not in the buffer list, without setting the 'buflisted' flag. - Also see ||+cmd|. + Also see |+cmd|. :[N]sb[uffer] [+cmd] [N] *:sb* *:sbuffer* Split window and edit buffer [N] from the buffer list. If [N] @@ -1112,7 +1113,7 @@ list of buffers. |unlisted-buffer| "useopen" setting of 'switchbuf' when splitting. This will also edit a buffer that is not in the buffer list, without setting the 'buflisted' flag. - Also see ||+cmd|. + Also see |+cmd|. :[N]sb[uffer] [+cmd] {bufname} Split window and edit buffer for {bufname} from the buffer @@ -1121,13 +1122,13 @@ list of buffers. |unlisted-buffer| Note: If what you want to do is split the buffer, make a copy under another name, you can do it this way: > :w foobar | sp # -< Also see ||+cmd|. +< Also see |+cmd|. :[N]bn[ext][!] [+cmd] [N] *:bn* *:bnext* *E87* Go to [N]th next buffer in buffer list. [N] defaults to one. Wraps around the end of the buffer list. See |:buffer-!| for [!]. - Also see ||+cmd|. + Also see |+cmd|. If you are in a help buffer, this takes you to the next help buffer (if there is one). Similarly, if you are in a normal (non-help) buffer, this takes you to the next normal buffer. @@ -1140,21 +1141,21 @@ list of buffers. |unlisted-buffer| :[N]sbn[ext] [+cmd] [N] Split window and go to [N]th next buffer in buffer list. Wraps around the end of the buffer list. Uses 'switchbuf' - Also see ||+cmd|. + Also see |+cmd|. :[N]bN[ext][!] [+cmd] [N] *:bN* *:bNext* *:bp* *:bprevious* *E88* :[N]bp[revious][!] [+cmd] [N] Go to [N]th previous buffer in buffer list. [N] defaults to one. Wraps around the start of the buffer list. See |:buffer-!| for [!] and 'switchbuf'. - Also see ||+cmd|. + Also see |+cmd|. :[N]sbN[ext] [+cmd] [N] *:sbN* *:sbNext* *:sbp* *:sbprevious* :[N]sbp[revious] [+cmd] [N] Split window and go to [N]th previous buffer in buffer list. Wraps around the start of the buffer list. Uses 'switchbuf'. - Also see ||+cmd|. + Also see |+cmd|. :br[ewind][!] [+cmd] *:br* *:brewind* Go to first buffer in buffer list. If the buffer list is diff --git a/runtime/filetype.vim b/runtime/filetype.vim index b06e702637..c5a3577a62 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2015 Jan 07 +" Last Change: 2015 Oct 13 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -139,7 +139,7 @@ au BufNewFile,BufRead .arch-inventory,=tagging-method setf arch au BufNewFile,BufRead *.art setf art " AsciiDoc -au BufNewFile,BufRead *.asciidoc setf asciidoc +au BufNewFile,BufRead *.asciidoc,*.adoc setf asciidoc " ASN.1 au BufNewFile,BufRead *.asn,*.asn1 setf asn @@ -304,6 +304,9 @@ au BufNewFile,BufRead *.bl setf blank " Blkid cache file au BufNewFile,BufRead */etc/blkid.tab,*/etc/blkid.tab.old setf xml +" Bazel (http://bazel.io) +autocmd BufRead,BufNewFile *.bzl,BUILD,WORKSPACE setfiletype bzl + " C or lpc au BufNewFile,BufRead *.c call s:FTlpc() @@ -822,7 +825,7 @@ au BufNewFile,BufRead *.gs setf grads au BufNewFile,BufRead *.gretl setf gretl " Groovy -au BufNewFile,BufRead *.groovy setf groovy +au BufNewFile,BufRead *.gradle,*.groovy setf groovy " GNU Server Pages au BufNewFile,BufRead *.gsp setf gsp @@ -868,7 +871,7 @@ func! s:FThtml() setf xhtml return endif - if getline(n) =~ '{%\s*\(extends\|block\|load\)\>' + if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+' setf htmldjango return endif @@ -1164,7 +1167,7 @@ func! s:FTm() let n = 1 while n < 10 let line = getline(n) - if line =~ '^\s*\(#\s*\(include\|import\)\>\|/\*\|//\)' + if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\|//\)' setf objc return endif @@ -1262,8 +1265,8 @@ au BufNewFile,BufRead */etc/modules.conf,*/etc/modules,*/etc/conf.modules setf m " Mplayer config au BufNewFile,BufRead mplayer.conf,*/.mplayer/config setf mplayerconf -" Moterola S record -au BufNewFile,BufRead *.s19,*.s28,*.s37 setf srec +" Motorola S record +au BufNewFile,BufRead *.s19,*.s28,*.s37,*.mot,*.srec setf srec " Mrxvtrc au BufNewFile,BufRead mrxvtrc,.mrxvtrc setf mrxvtrc @@ -1290,7 +1293,7 @@ au BufNewFile,BufRead *.mush setf mush au BufNewFile,BufRead Mutt{ng,}rc setf muttrc " Nano -au BufNewFile,BufRead */etc/nanorc,.nanorc setf nanorc +au BufNewFile,BufRead */etc/nanorc,*.nanorc setf nanorc " Nastran input/DMAP "au BufNewFile,BufRead *.dat setf nastran @@ -1332,7 +1335,7 @@ func! s:FTmm() let n = 1 while n < 10 let line = getline(n) - if line =~ '^\s*\(#\s*\(include\|import\)\>\|/\*\)' + if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)' setf objcpp return endif @@ -1393,7 +1396,7 @@ else au BufNewFile,BufRead *.pl call s:FTpl() endif au BufNewFile,BufRead *.plx,*.al setf perl -au BufNewFile,BufRead *.p6,*.pm6 setf perl6 +au BufNewFile,BufRead *.p6,*.pm6,*.pl6 setf perl6 func! s:FTpl() if exists("g:filetype_pl") @@ -1422,6 +1425,7 @@ au BufNewFile,BufRead *.pm " Perl POD au BufNewFile,BufRead *.pod setf pod +au BufNewFile,BufRead *.pod6 setf pod6 " Php, php3, php4, etc. " Also Phtml (was used for PHP 2 in the past) @@ -1854,7 +1858,7 @@ au BufNewFile,BufRead sgml.catalog* call s:StarSetf('catalog') " Shell scripts (sh, ksh, bash, bash2, csh); Allow .profile_foo etc. " Gentoo ebuilds and Arch Linux PKGBUILDs are actually bash scripts -au BufNewFile,BufRead .bashrc*,bashrc,bash.bashrc,.bash_profile*,.bash_logout*,.bash_aliases*,*.bash,*.ebuild,PKGBUILD* call SetFileTypeSH("bash") +au BufNewFile,BufRead .bashrc*,bashrc,bash.bashrc,.bash[_-]profile*,.bash[_-]logout*,.bash[_-]aliases*,*.bash,*/{,.}bash[_-]completion{,.d,.sh}{,/*},*.ebuild,*.eclass,PKGBUILD* call SetFileTypeSH("bash") au BufNewFile,BufRead .kshrc*,*.ksh call SetFileTypeSH("ksh") au BufNewFile,BufRead */etc/profile,.profile*,*.sh,*.env call SetFileTypeSH(getline(1)) @@ -2032,6 +2036,10 @@ func! s:FTRules() setf conf " Better than hog return endif + if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d' + setf javascript + return + endif try let config_lines = readfile('/etc/udev/udev.conf') catch /^Vim\%((\a\+)\)\=:E484/ @@ -2114,6 +2122,9 @@ au BufNewFile,BufRead *.cm setf voscm " Sysctl au BufNewFile,BufRead */etc/sysctl.conf,*/etc/sysctl.d/*.conf setf sysctl +" Systemd unit files +au BufNewFile,BufRead */systemd/*.{automount,mount,path,service,socket,swap,target,timer} setf systemd + " Synopsys Design Constraints au BufNewFile,BufRead *.sdc setf sdc @@ -2169,6 +2180,9 @@ au BufNewFile,BufRead *.tli setf tli " Telix Salt au BufNewFile,BufRead *.slt setf tsalt +" Tera Term Language +au BufRead,BufNewFile *.ttl setf teraterm + " Terminfo au BufNewFile,BufRead *.ti setf terminfo diff --git a/runtime/ftplugin/bzl.vim b/runtime/ftplugin/bzl.vim new file mode 100644 index 0000000000..0296b0c0b8 --- /dev/null +++ b/runtime/ftplugin/bzl.vim @@ -0,0 +1,94 @@ +" Vim filetype plugin file +" Language: Bazel (http://bazel.io) +" Maintainer: David Barnett (https://github.com/google/vim-ft-bzl) +" Last Change: 2015 Aug 11 + +"" +" @section Introduction, intro +" Core settings for the bzl filetype, used for BUILD and *.bzl files for the +" Bazel build system (http://bazel.io/). + +if exists('b:did_ftplugin') + finish +endif + + +" Vim 7.4.051 has opinionated settings in ftplugin/python.vim that try to force +" PEP8 conventions on every python file, but these conflict with Google's +" indentation guidelines. As a workaround, we explicitly source the system +" ftplugin, but save indentation settings beforehand and restore them after. +let s:save_expandtab = &l:expandtab +let s:save_shiftwidth = &l:shiftwidth +let s:save_softtabstop = &l:softtabstop +let s:save_tabstop = &l:tabstop + +" NOTE: Vim versions before 7.3.511 had a ftplugin/python.vim that was broken +" for compatible mode. +let s:save_cpo = &cpo +set cpo&vim + +" Load base python ftplugin (also defines b:did_ftplugin). +source $VIMRUNTIME/ftplugin/python.vim + +" NOTE: Vim versions before 7.4.104 and later set this in ftplugin/python.vim. +setlocal comments=b:#,fb:- + +" Restore pre-existing indentation settings. +let &l:expandtab = s:save_expandtab +let &l:shiftwidth = s:save_shiftwidth +let &l:softtabstop = s:save_softtabstop +let &l:tabstop = s:save_tabstop + +setlocal formatoptions-=t + +" Make gf work with imports in BUILD files. +setlocal includeexpr=substitute(v:fname,'//','','') + +" Enable syntax-based folding, if specified. +if get(g:, 'ft_bzl_fold', 0) + setlocal foldmethod=syntax + setlocal foldtext=BzlFoldText() +endif + +if exists('*BzlFoldText') + finish +endif + +function BzlFoldText() abort + let l:start_num = nextnonblank(v:foldstart) + let l:end_num = prevnonblank(v:foldend) + + if l:end_num <= l:start_num + 1 + " If the fold is empty, don't print anything for the contents. + let l:content = '' + else + " Otherwise look for something matching the content regex. + " And if nothing matches, print an ellipsis. + let l:content = '...' + for l:line in getline(l:start_num + 1, l:end_num - 1) + let l:content_match = matchstr(l:line, '\m\C^\s*name = \zs.*\ze,$') + if !empty(l:content_match) + let l:content = l:content_match + break + endif + endfor + endif + + " Enclose content with start and end + let l:start_text = getline(l:start_num) + let l:end_text = substitute(getline(l:end_num), '^\s*', '', '') + let l:text = l:start_text . ' ' . l:content . ' ' . l:end_text + + " Compute the available width for the displayed text. + let l:width = winwidth(0) - &foldcolumn - (&number ? &numberwidth : 0) + let l:lines_folded = ' ' . string(1 + v:foldend - v:foldstart) . ' lines' + + " Expand tabs, truncate, pad, and concatenate + let l:text = substitute(l:text, '\t', repeat(' ', &tabstop), 'g') + let l:text = strpart(l:text, 0, l:width - len(l:lines_folded)) + let l:padding = repeat(' ', l:width - len(l:lines_folded) - len(l:text)) + return l:text . l:padding . l:lines_folded +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/runtime/ftplugin/changelog.vim b/runtime/ftplugin/changelog.vim index 244245e271..257e9cd9d4 100644 --- a/runtime/ftplugin/changelog.vim +++ b/runtime/ftplugin/changelog.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file -" Language: generic Changelog file -" Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2014-01-10 +" Language: generic Changelog file +" Maintainer: Martin Florian <marfl@posteo.de> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2015-10-25 " Variables: " g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) - " description: the timeformat used in ChangeLog entries. @@ -167,7 +168,7 @@ if &filetype == 'changelog' let cursor = stridx(line, '{cursor}') call setline(lnum, substitute(line, '{cursor}', '', '')) endif - startinsert! + startinsert endfunction " Internal function to create a new entry in the ChangeLog. @@ -223,7 +224,8 @@ if &filetype == 'changelog' endfunction if exists(":NewChangelogEntry") != 2 - noremap <buffer> <silent> <Leader>o <Esc>:call <SID>new_changelog_entry('')<CR> + nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR> + xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR> command! -nargs=0 NewChangelogEntry call s:new_changelog_entry('') endif diff --git a/runtime/ftplugin/fortran.vim b/runtime/ftplugin/fortran.vim index 7591edd821..5d42409fd4 100644 --- a/runtime/ftplugin/fortran.vim +++ b/runtime/ftplugin/fortran.vim @@ -1,9 +1,9 @@ " Vim settings file " Language: Fortran 2008 (and older: Fortran 2003, 95, 90, 77, 66) -" Version: 0.49 -" Last Change: 2013 Oct. 01 +" Version: 0.50 +" Last Change: 2015 Nov. 30 " Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www2.unb.ca/~ajit/> -" Usage: Do :help fortran-plugin from Vim +" Usage: For instructions, do :help fortran-plugin from Vim " Credits: " Useful suggestions were made by Stefano Zacchiroli, Hendrik Merx, Ben " Fritz, and David Barnett. @@ -20,7 +20,10 @@ set cpoptions&vim let b:did_ftplugin = 1 " Determine whether this is a fixed or free format source file -" if this hasn't been done yet +" if this hasn't been done yet using the priority: +" buffer-local value +" > global value +" > file extension as in Intel ifort, gcc (gfortran), NAG, Pathscale, and Cray compilers if !exists("b:fortran_fixed_source") if exists("fortran_free_source") " User guarantees free source form @@ -28,13 +31,19 @@ if !exists("b:fortran_fixed_source") elseif exists("fortran_fixed_source") " User guarantees fixed source form let b:fortran_fixed_source = 1 + elseif expand("%:e") ==? "f\<90\|95\|03\|08\>" + " Free-form file extension defaults as in Intel ifort, gcc(gfortran), NAG, Pathscale, and Cray compilers + let b:fortran_fixed_source = 0 + elseif expand("%:e") ==? "f\|f77\|for" + " Fixed-form file extension defaults + let b:fortran_fixed_source = 1 else - " Modern Fortran allows both fixed and free source form - " assume fixed source form unless signs of free source form - " are detected in the first five columns of the first s:lmax lines + " Modern fortran still allows both fixed and free source form + " Assume fixed source form unless signs of free source form + " are detected in the first five columns of the first s:lmax lines. " Detection becomes more accurate and time-consuming if more lines " are checked. Increase the limit below if you keep lots of comments at - " the very top of each file and you have a fast computer + " the very top of each file and you have a fast computer. let s:lmax = 500 if ( s:lmax > line("$") ) let s:lmax = line("$") diff --git a/runtime/ftplugin/hog.vim b/runtime/ftplugin/hog.vim new file mode 100644 index 0000000000..4ee0a9f934 --- /dev/null +++ b/runtime/ftplugin/hog.vim @@ -0,0 +1,39 @@ +" Vim filetype plugin +" Language: hog (snort.conf) +" Maintainer: . Victor Roemer, <vroemer@badsec.org>. +" Last Change: Mar 1, 2013 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:undo_ftplugin = "setl fo< com< cms< def< inc<" + +let s:cpo_save = &cpo +set cpo&vim + +setlocal formatoptions=croq +setlocal comments=:# +setlocal commentstring=\c#\ %s +setlocal define=\c^\s\{-}var +setlocal include=\c^\s\{-}include + +" Move around configurations +let s:hog_keyword_match = '\c^\s*\<\(preprocessor\\|config\\|output\\|include\\|ipvar\\|portvar\\|var\\|dynamicpreprocessor\\|' . + \ 'dynamicengine\\|dynamicdetection\\|activate\\|alert\\|drop\\|block\\|dynamic\\|log\\|pass\\|reject\\|sdrop\\|sblock\)\>' + +exec "nnoremap <buffer><silent> ]] :call search('" . s:hog_keyword_match . "', 'W' )<CR>" +exec "nnoremap <buffer><silent> [[ :call search('" . s:hog_keyword_match . "', 'bW' )<CR>" + +if exists("loaded_matchit") + let b:match_words = + \ '^\s*\<\%(preprocessor\|config\|output\|include\|ipvar\|portvar' . + \ '\|var\|dynamicpreprocessor\|dynamicengine\|dynamicdetection' . + \ '\|activate\|alert\|drop\|block\|dynamic\|log\|pass\|reject' . + \ '\|sdrop\|sblock\>\):$,\::\,:;' + let b:match_skip = 'r:\\.\{-}$\|^\s*#.\{-}$\|^\s*$' +endif + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/ftplugin/j.vim b/runtime/ftplugin/j.vim index bcd606ffa0..9dc1692534 100644 --- a/runtime/ftplugin/j.vim +++ b/runtime/ftplugin/j.vim @@ -2,7 +2,7 @@ " Language: J " Maintainer: David Bürgin <676c7473@gmail.com> " URL: https://github.com/glts/vim-j -" Last Change: 2014-04-05 +" Last Change: 2015-09-27 if exists('b:did_ftplugin') finish @@ -12,43 +12,49 @@ let b:did_ftplugin = 1 let s:save_cpo = &cpo set cpo&vim -setlocal iskeyword=48-57,A-Z,_,a-z +setlocal iskeyword=48-57,A-Z,a-z,_ setlocal comments=:NB. setlocal commentstring=NB.\ %s setlocal formatoptions-=t -setlocal shiftwidth=2 softtabstop=2 expandtab setlocal matchpairs=(:) +setlocal path-=/usr/include -let b:undo_ftplugin = 'setlocal matchpairs< expandtab< softtabstop< shiftwidth< formatoptions< commentstring< comments< iskeyword<' +" Includes. To make the shorthand form "require 'web/cgi'" work, double the +" last path component. Also strip off leading folder names like "~addons/". +setlocal include=\\v^\\s*(load\|require)\\s*'\\zs\\f+\\ze' +setlocal includeexpr=substitute(substitute(tr(v:fname,'\\','/'),'\\v^[^~][^/.]*(/[^/.]+)$','&\\1',''),'\\v^\\~[^/]+/','','') +setlocal suffixesadd=.ijs + +let b:undo_ftplugin = 'setlocal matchpairs< formatoptions< commentstring< comments< iskeyword< path< include< includeexpr< suffixesadd<' " Section movement with ]] ][ [[ []. The start/end patterns below are amended " inside the function in order to avoid matching on the current cursor line. -let s:sectionstart = '.\{-}\<\%([0-4]\|13\|noun\|adverb\|conjunction\|verb\|monad\|dyad\)\s\+\%(:\s*0\|def\s\+0\|define\)\>.*' +let s:sectionstart = '\%(\s*Note\|.\{-}\<\%([0-4]\|13\|noun\|adverb\|conjunction\|verb\|monad\|dyad\)\s\+\%(:\s*0\|def\s\+0\|define\)\)\>.*' let s:sectionend = '\s*)\s*' function! s:SearchSection(end, backwards, visualmode) abort if a:visualmode !=# '' normal! gv endif - let flags = a:backwards ? 'bsW' : 'sW' + let l:flags = a:backwards ? 'bsW' : 'sW' if a:end - call search('^' . s:sectionend . (a:backwards ? '\n\_.\{-}\%#' : '$'), flags) + call search('^' . s:sectionend . (a:backwards ? '\n\_.\{-}\%#' : '$'), l:flags) else - call search('^' . s:sectionstart . (a:backwards ? '\n\_.\{-}\%#' : '$'), flags) + call search('^' . s:sectionstart . (a:backwards ? '\n\_.\{-}\%#' : '$'), l:flags) endif endfunction -noremap <script> <buffer> <silent> ]] :<C-U>call <SID>SearchSection(0, 0, '')<CR> -xnoremap <script> <buffer> <silent> ]] :<C-U>call <SID>SearchSection(0, 0, visualmode())<CR> +noremap <buffer> <silent> ]] :<C-U>call <SID>SearchSection(0, 0, '')<CR> +xnoremap <buffer> <silent> ]] :<C-U>call <SID>SearchSection(0, 0, visualmode())<CR> sunmap <buffer> ]] -noremap <script> <buffer> <silent> ][ :<C-U>call <SID>SearchSection(1, 0, '')<CR> -xnoremap <script> <buffer> <silent> ][ :<C-U>call <SID>SearchSection(1, 0, visualmode())<CR> +noremap <buffer> <silent> ][ :<C-U>call <SID>SearchSection(1, 0, '')<CR> +xnoremap <buffer> <silent> ][ :<C-U>call <SID>SearchSection(1, 0, visualmode())<CR> sunmap <buffer> ][ -noremap <script> <buffer> <silent> [[ :<C-U>call <SID>SearchSection(0, 1, '')<CR> -xnoremap <script> <buffer> <silent> [[ :<C-U>call <SID>SearchSection(0, 1, visualmode())<CR> +noremap <buffer> <silent> [[ :<C-U>call <SID>SearchSection(0, 1, '')<CR> +xnoremap <buffer> <silent> [[ :<C-U>call <SID>SearchSection(0, 1, visualmode())<CR> sunmap <buffer> [[ -noremap <script> <buffer> <silent> [] :<C-U>call <SID>SearchSection(1, 1, '')<CR> -xnoremap <script> <buffer> <silent> [] :<C-U>call <SID>SearchSection(1, 1, visualmode())<CR> +noremap <buffer> <silent> [] :<C-U>call <SID>SearchSection(1, 1, '')<CR> +xnoremap <buffer> <silent> [] :<C-U>call <SID>SearchSection(1, 1, visualmode())<CR> sunmap <buffer> [] let b:undo_ftplugin .= ' | silent! execute "unmap <buffer> ]]"' @@ -66,7 +72,7 @@ endif " Enhanced "%" matching (see ":help matchit") if exists('loaded_matchit') && !exists('b:match_words') let b:match_ignorecase = 0 - let b:match_words = '^.\{-}\<\%([0-4]\|13\|noun\|adverb\|conjunction\|verb\|monad\|dyad\)\s\+\%(\:\s*0\|def\s\+0\|define\)\>:^\s*\:\s*$:^\s*)\s*$' + let b:match_words = '^\%(\s*Note\|.\{-}\<\%([0-4]\|13\|noun\|adverb\|conjunction\|verb\|monad\|dyad\)\s\+\%(\:\s*0\|def\s\+0\|define\)\)\>:^\s*\:\s*$:^\s*)\s*$' \ . ',\<\%(for\%(_\a\k*\)\=\|if\|select\|try\|whil\%(e\|st\)\)\.:\<\%(case\|catch[dt]\=\|else\%(if\)\=\|fcase\)\.:\<end\.' let b:undo_ftplugin .= ' | unlet! b:match_ignorecase b:match_words' endif diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 133a28e626..04ab539fb1 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -24,14 +24,18 @@ setlocal buftype=nofile noswapfile setlocal nomodifiable readonly bufhidden=hide nobuflisted tabstop=8 if !exists("g:no_plugin_maps") && !exists("g:no_man_maps") - nnoremap <silent> <buffer> <C-]> :call man#get_page(v:count)<CR> + nnoremap <silent> <buffer> <C-]> :call man#get_page(v:count, expand('<cword>'))<CR> nnoremap <silent> <buffer> <C-T> :call man#pop_page()<CR> nnoremap <silent> <nowait><buffer> q <C-W>c if &keywordprg !=# ':Man' - nnoremap <silent> <buffer> K :call man#get_page(v:count)<CR> + nnoremap <silent> <buffer> K :call man#get_page(v:count, expand('<cword>'))<CR> endif endif +if exists('g:ft_man_folding_enable') && (g:ft_man_folding_enable == 1) + setlocal foldmethod=indent foldnestmax=1 foldenable +endif + let b:undo_ftplugin = 'setlocal iskeyword<' " vim: set sw=2: diff --git a/runtime/ftplugin/spec.vim b/runtime/ftplugin/spec.vim index 0b6750de94..6d5bf4b806 100644 --- a/runtime/ftplugin/spec.vim +++ b/runtime/ftplugin/spec.vim @@ -2,7 +2,7 @@ " Filename: spec.vim " Maintainer: Igor Gnatenko i.gnatenko.brain@gmail.com " Former Maintainer: Gustavo Niemeyer <niemeyer@conectiva.com> (until March 2014) -" Last Change: Sun Mar 2 11:24 MSK 2014 Igor Gnatenko +" Last Change: Mon Jun 01 21:15 MSK 2015 Igor Gnatenko if exists("b:did_ftplugin") finish @@ -18,7 +18,9 @@ if !exists("no_plugin_maps") && !exists("no_spec_maps") endif endif -noremap <buffer> <unique> <script> <Plug>SpecChangelog :call <SID>SpecChangelog("")<CR> +if !hasmapto("call <SID>SpecChangelog(\"\")<CR>") + noremap <buffer> <unique> <script> <Plug>SpecChangelog :call <SID>SpecChangelog("")<CR> +endif if !exists("*s:GetRelVer") function! s:GetRelVer() diff --git a/runtime/ftplugin/systemd.vim b/runtime/ftplugin/systemd.vim new file mode 100644 index 0000000000..60b3fd996d --- /dev/null +++ b/runtime/ftplugin/systemd.vim @@ -0,0 +1,7 @@ +" Vim filetype plugin file +" Language: systemd.unit(5) + +if !exists('b:did_ftplugin') + " Looks a lot like dosini files. + runtime! ftplugin/dosini.vim +endif diff --git a/runtime/indent/bzl.vim b/runtime/indent/bzl.vim new file mode 100644 index 0000000000..24e5b870cd --- /dev/null +++ b/runtime/indent/bzl.vim @@ -0,0 +1,97 @@ +" Vim indent file +" Language: Bazel (http://bazel.io) +" Maintainer: David Barnett (https://github.com/google/vim-ft-bzl) +" Last Change: 2015 Aug 11 + +if exists('b:did_indent') + finish +endif + +" Load base python indent. +if !exists('*GetPythonIndent') + runtime! indent/python.vim +endif + +let b:did_indent = 1 + +" Only enable bzl google indent if python google indent is enabled. +if !get(g:, 'no_google_python_indent') + setlocal indentexpr=GetBzlIndent(v:lnum) +endif + +if exists('*GetBzlIndent') + finish +endif + +let s:save_cpo = &cpo +set cpo-=C + +" Maximum number of lines to look backwards. +let s:maxoff = 50 + +"" +" Determine the correct indent level given an {lnum} in the current buffer. +function GetBzlIndent(lnum) abort + let l:use_recursive_indent = !get(g:, 'no_google_python_recursive_indent') + if l:use_recursive_indent + " Backup and override indent setting variables. + if exists('g:pyindent_nested_paren') + let l:pyindent_nested_paren = g:pyindent_nested_paren + endif + if exists('g:pyindent_open_paren') + let l:pyindent_open_paren = g:pyindent_open_paren + endif + " Vim 7.3.693 and later defines a shiftwidth() function to get the effective + " shiftwidth value. Fall back to &shiftwidth if the function doesn't exist. + let l:sw_expr = exists('*shiftwidth') ? 'shiftwidth()' : '&shiftwidth' + let g:pyindent_nested_paren = l:sw_expr . ' * 2' + let g:pyindent_open_paren = l:sw_expr . ' * 2' + endif + + let l:indent = -1 + + " Indent inside parens. + " Align with the open paren unless it is at the end of the line. + " E.g. + " open_paren_not_at_EOL(100, + " (200, + " 300), + " 400) + " open_paren_at_EOL( + " 100, 200, 300, 400) + call cursor(a:lnum, 1) + let [l:par_line, l:par_col] = searchpairpos('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" . + \ " synIDattr(synID(line('.'), col('.'), 1), 'name')" . + \ " =~ '\\(Comment\\|String\\)$'") + if l:par_line > 0 + call cursor(l:par_line, 1) + if l:par_col != col('$') - 1 + let l:indent = l:par_col + endif + endif + + " Delegate the rest to the original function. + if l:indent == -1 + let l:indent = GetPythonIndent(a:lnum) + endif + + if l:use_recursive_indent + " Restore global variables. + if exists('l:pyindent_nested_paren') + let g:pyindent_nested_paren = l:pyindent_nested_paren + else + unlet g:pyindent_nested_paren + endif + if exists('l:pyindent_open_paren') + let g:pyindent_open_paren = l:pyindent_open_paren + else + unlet g:pyindent_open_paren + endif + endif + + return l:indent +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/runtime/indent/fortran.vim b/runtime/indent/fortran.vim index e76c64b671..d492889fc7 100644 --- a/runtime/indent/fortran.vim +++ b/runtime/indent/fortran.vim @@ -1,9 +1,11 @@ " Vim indent file -" Language: Fortran95 (and Fortran90, Fortran77, F and elf90) -" Version: 0.40 -" Last Change: 2011 Dec. 28 -" Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www.unb.ca/chem/ajit/> -" Usage: Do :help fortran-indent from Vim +" Language: Fortran 2008 (and older: Fortran 2003, 95, 90, and 77) +" Version: 0.42 +" Last Change: 2015 Nov. 30 +" Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www2.unb.ca/~ajit/> +" Usage: For instructions, do :help fortran-indent from Vim +" Credits: +" Useful suggestions were made by: Albert Oliver Serra. " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -25,7 +27,10 @@ if exists("b:fortran_indent_more") || exists("g:fortran_indent_more") endif " Determine whether this is a fixed or free format source file -" if this hasn't been done yet +" if this hasn't been done yet using the priority: +" buffer-local value +" > global value +" > file extension as in Intel ifort, gcc (gfortran), NAG, Pathscale, and Cray compilers if !exists("b:fortran_fixed_source") if exists("fortran_free_source") " User guarantees free source form @@ -33,13 +38,19 @@ if !exists("b:fortran_fixed_source") elseif exists("fortran_fixed_source") " User guarantees fixed source form let b:fortran_fixed_source = 1 + elseif expand("%:e") ==? "f\<90\|95\|03\|08\>" + " Free-form file extension defaults as in Intel ifort, gcc(gfortran), NAG, Pathscale, and Cray compilers + let b:fortran_fixed_source = 0 + elseif expand("%:e") ==? "f\|f77\|for" + " Fixed-form file extension defaults + let b:fortran_fixed_source = 1 else - " f90 and f95 allow both fixed and free source form - " assume fixed source form unless signs of free source form - " are detected in the first five columns of the first 250 lines + " Modern fortran still allows both fixed and free source form + " Assume fixed source form unless signs of free source form + " are detected in the first five columns of the first s:lmax lines. " Detection becomes more accurate and time-consuming if more lines " are checked. Increase the limit below if you keep lots of comments at - " the very top of each file and you have a fast computer + " the very top of each file and you have a fast computer. let s:lmax = 500 if ( s:lmax > line("$") ) let s:lmax = line("$") @@ -129,7 +140,7 @@ function FortranGetIndent(lnum) if getline(v:lnum) =~? '^\s*\(\d\+\s\)\=\s*' \. '\(else\|else\s*if\|else\s*where\|case\|' \. 'end\s*\(if\|where\|select\|interface\|' - \. 'type\|forall\|associate\|enum\)\)\>' + \. 'type\|forall\|associate\|enum\|block\)\)\>' let ind = ind - &sw " Fix indent for case statement immediately after select if prevstat =~? '\<select\s\+\(case\|type\)\>' @@ -141,8 +152,11 @@ function FortranGetIndent(lnum) if prevstat =~ '&\s*$' && prev2stat !~ '&\s*$' let ind = ind + &sw endif + if prevstat =~ '&\s*$' && prevstat =~ '\<else\s*if\>' + let ind = ind - &sw + endif "Line after last continuation line - if prevstat !~ '&\s*$' && prev2stat =~ '&\s*$' + if prevstat !~ '&\s*$' && prev2stat =~ '&\s*$' && prevstat !~? '\<then\>' let ind = ind - &sw endif diff --git a/runtime/indent/hog.vim b/runtime/indent/hog.vim new file mode 100644 index 0000000000..02ac7d4d1b --- /dev/null +++ b/runtime/indent/hog.vim @@ -0,0 +1,77 @@ +" Vim indent file +" Language: hog (Snort.conf) +" Maintainer: Victor Roemer, <vroemer@badsec.org> +" Last Change: Mar 7, 2013 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 +let b:undo_indent = 'setlocal smartindent< indentexpr< indentkeys<' + +setlocal nosmartindent +setlocal indentexpr=GetHogIndent() +setlocal indentkeys+=!^F,o,O,0# + +" Only define the function once. +if exists("*GetHogIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +let s:syn_blocks = '\<SnortRuleTypeBody\>' + +function s:IsInBlock(lnum) + return synIDattr(synID(a:lnum, 1, 1), 'name') =~ s:syn_blocks +endfunction + +function GetHogIndent() + let prevlnum = prevnonblank(v:lnum-1) + + " Comment blocks have identical indent + if getline(v:lnum) =~ '^\s*#' && getline(prevlnum) =~ '^\s*#' + return indent(prevlnum) + endif + + " Ignore comment lines when calculating indent + while getline(prevlnum) =~ '^\s*#' + let prevlnum = prevnonblank(prevlnum-1) + if !prevlnum + return previndent + endif + endwhile + + " Continuation of a line that wasn't indented + let prevline = getline(prevlnum) + if prevline =~ '^\k\+.*\\\s*$' + return &sw + endif + + " Continuation of a line that was indented + if prevline =~ '\k\+.*\\\s*$' + return indent(prevlnum) + endif + + " Indent the next line if previous line contained a start of a block + " definition ('{' or '('). + if prevline =~ '^\k\+[^#]*{}\@!\s*$' " TODO || prevline =~ '^\k\+[^#]*()\@!\s*$' + return &sw + endif + + " Match inside of a block + if s:IsInBlock(v:lnum) + if prevline =~ "^\k\+.*$" + return &sw + else + return indent(prevlnum) + endif + endif + + return 0 +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/html.vim b/runtime/indent/html.vim index d64a0e5cd3..8aaf82e21f 100644 --- a/runtime/indent/html.vim +++ b/runtime/indent/html.vim @@ -2,7 +2,7 @@ " Header: "{{{ " Maintainer: Bram Moolenaar " Original Author: Andy Wokula <anwoku@yahoo.de> -" Last Change: 2014 Aug 23 +" Last Change: 2015 Sep 25 " Version: 1.0 " Description: HTML indent script with cached state for faster indenting on a " range of lines. @@ -94,7 +94,7 @@ func! HtmlIndent_CheckUserSettings() let autotags = g:html_indent_autotags endif let b:hi_removed_tags = {} - if autotags + if len(autotags) > 0 call s:RemoveITags(b:hi_removed_tags, split(autotags, ",")) endif @@ -178,13 +178,15 @@ let s:countonly = 0 " 3 "script" " 4 "style" " 5 comment start +" 6 conditional comment start " -1 closing tag " -2 "/pre" " -3 "/script" " -4 "/style" " -5 comment end +" -6 conditional comment end let s:indent_tags = {} -let s:endtags = [0,0,0,0,0,0] " long enough for the highest index +let s:endtags = [0,0,0,0,0,0,0] " long enough for the highest index "}}} " Add a list of tag names for a pair of <tag> </tag> to "tags". @@ -245,6 +247,10 @@ call s:AddITags(s:indent_tags, [ \ 'header', 'group', 'keygen', 'mark', 'math', 'meter', 'nav', 'output', \ 'progress', 'ruby', 'section', 'svg', 'texture', 'time', 'video', \ 'wbr', 'text']) + +" Tags added for web components: +call s:AddITags(s:indent_tags, [ + \ 'content', 'shadow', 'template']) "}}} " Add Block Tags: these contain alien content @@ -253,6 +259,7 @@ call s:AddBlockTag('pre', 2) call s:AddBlockTag('script', 3) call s:AddBlockTag('style', 4) call s:AddBlockTag('<!--', 5, '-->') +call s:AddBlockTag('<!--[', 6, '![endif]-->') "}}} " Return non-zero when "tagname" is an opening tag, not being a block tag, for @@ -287,7 +294,7 @@ func! s:CountITags(text) let s:nextrel = 0 " relative indent steps for next line [unit &sw]: let s:block = 0 " assume starting outside of a block let s:countonly = 1 " don't change state - call substitute(a:text, '<\zs/\=\w\+\>\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g') + call substitute(a:text, '<\zs/\=\w\+\(-\w\+\)*\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g') let s:countonly = 0 endfunc "}}} @@ -299,7 +306,7 @@ func! s:CountTagsAndState(text) let s:nextrel = 0 " relative indent steps for next line [unit &sw]: let s:block = b:hi_newstate.block - let tmp = substitute(a:text, '<\zs/\=\w\+\>\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g') + let tmp = substitute(a:text, '<\zs/\=\w\+\(-\w\+\)*\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g') if s:block == 3 let b:hi_newstate.scripttype = s:GetScriptType(matchstr(tmp, '\C.*<SCRIPT\>\zs[^>]*')) endif @@ -311,6 +318,9 @@ func! s:CheckTag(itag) "{{{ " Returns an empty string or "SCRIPT". " a:itag can be "tag" or "/tag" or "<!--" or "-->" + if (s:CheckCustomTag(a:itag)) + return "" + endif let ind = s:get_tag(a:itag) if ind == -1 " closing tag @@ -365,6 +375,36 @@ func! s:CheckBlockTag(blocktag, ind) return "" endfunc "}}} +" Used by s:CheckTag(). +func! s:CheckCustomTag(ctag) + "{{{ + " Returns 1 if ctag is the tag for a custom element, 0 otherwise. + " a:ctag can be "tag" or "/tag" or "<!--" or "-->" + let pattern = '\%\(\w\+-\)\+\w\+' + if match(a:ctag, pattern) == -1 + return 0 + endif + if matchstr(a:ctag, '\/\ze.\+') == "/" + " closing tag + if s:block != 0 + " ignore ctag within a block + return 1 + endif + if s:nextrel == 0 + let s:curind -= 1 + else + let s:nextrel -= 1 + endif + else + " opening tag + if s:block != 0 + return 1 + endif + let s:nextrel += 1 + endif + return 1 +endfunc "}}} + " Return the <script> type: either "javascript" or "" func! s:GetScriptType(str) "{{{ @@ -388,7 +428,7 @@ func! s:FreshState(lnum) " State: " lnum last indented line == prevnonblank(a:lnum - 1) " block = 0 a:lnum located within special tag: 0:none, 2:<pre>, - " 3:<script>, 4:<style>, 5:<!-- + " 3:<script>, 4:<style>, 5:<!--, 6:<!--[ " baseindent use this indent for line a:lnum as a start - kind of " autoindent (if block==0) " scripttype = '' type attribute of a script tag (if block==3) @@ -427,10 +467,13 @@ func! s:FreshState(lnum) " FI " look back for a blocktag - call cursor(a:lnum, 1) - let [stopline, stopcol] = searchpos('\c<\zs\/\=\%(pre\>\|script\>\|style\>\)', "bW") - if stopline > 0 - " fugly ... why isn't there searchstr() + let stopline2 = v:lnum + 1 + if has_key(b:hi_indent, 'block') && b:hi_indent.block > 5 + let [stopline2, stopcol2] = searchpos('<!--', 'bnW') + endif + let [stopline, stopcol] = searchpos('\c<\zs\/\=\%(pre\>\|script\>\|style\>\)', "bnW") + if stopline > 0 && stopline < stopline2 + " ugly ... why isn't there searchstr() let tagline = tolower(getline(stopline)) let blocktag = matchstr(tagline, '\/\=\%(pre\>\|script\>\|style\>\)', stopcol - 1) if blocktag[0] != "/" @@ -450,23 +493,29 @@ func! s:FreshState(lnum) " blocktag == "/..." let swendtag = match(tagline, '^\s*</') >= 0 if !swendtag - let [bline, bcol] = searchpos('<'.blocktag[1:].'\>', "bW") + let [bline, bcol] = searchpos('<'.blocktag[1:].'\>', "bnW") call s:CountITags(tolower(getline(bline)[: bcol-2])) let state.baseindent = indent(bline) + (s:curind + s:nextrel) * s:ShiftWidth() return state endif endif endif + if stopline > stopline2 + let stopline = stopline2 + let stopcol = stopcol2 + endif " else look back for comment - call cursor(a:lnum, 1) - let [comlnum, comcol, found] = searchpos('\(<!--\)\|-->', 'bpW', stopline) - if found == 2 + let [comlnum, comcol, found] = searchpos('\(<!--\[\)\|\(<!--\)\|-->', 'bpnW', stopline) + if found == 2 || found == 3 " comment opener found, assume a:lnum within comment - let state.block = 5 + let state.block = (found == 3 ? 5 : 6) let state.blocklnr = comlnum " check preceding tags in the line: call s:CountITags(tolower(getline(comlnum)[: comcol-2])) + if found == 2 + let state.baseindent = b:hi_indent.baseindent + endif let state.blocktagind = indent(comlnum) + (s:curind + s:nextrel) * s:ShiftWidth() return state endif @@ -782,6 +831,20 @@ func! s:Alien5() return indent(prevlnum) endfunc "}}} +" Return the indent for conditional comment: <!--[ ![endif]--> +func! s:Alien6() + "{{{ + let curtext = getline(v:lnum) + if curtext =~ '\s*\zs<!\[endif\]-->' + " current line starts with end of comment, line up with comment start. + let lnum = search('<!--', 'bn') + if lnum > 0 + return indent(lnum) + endif + endif + return b:hi_indent.baseindent + s:ShiftWidth() +endfunc "}}} + " When the "lnum" line ends in ">" find the line containing the matching "<". func! HtmlIndent_FindTagStart(lnum) "{{{ @@ -883,7 +946,7 @@ endfunc "}}} " THE MAIN INDENT FUNCTION. Return the amount of indent for v:lnum. func! HtmlIndent() "{{{ - if prevnonblank(v:lnum - 1) <= 1 + if prevnonblank(v:lnum - 1) < 1 " First non-blank line has no indent. return 0 endif diff --git a/runtime/indent/j.vim b/runtime/indent/j.vim index e268e1fcd3..ea3d50936b 100644 --- a/runtime/indent/j.vim +++ b/runtime/indent/j.vim @@ -2,7 +2,7 @@ " Language: J " Maintainer: David Bürgin <676c7473@gmail.com> " URL: https://github.com/glts/vim-j -" Last Change: 2014-04-05 +" Last Change: 2015-01-11 if exists('b:did_indent') finish @@ -26,25 +26,25 @@ if !exists('g:j_indent_definitions') endif function GetJIndent() abort - let prevlnum = prevnonblank(v:lnum-1) - if prevlnum == 0 + let l:prevlnum = prevnonblank(v:lnum - 1) + if l:prevlnum == 0 return 0 endif - let indent = indent(prevlnum) - let prevline = getline(prevlnum) - if prevline =~# '^\s*\%(case\|catch[dt]\=\|do\|else\%(if\)\=\|fcase\|for\%(_\a\k*\)\=\|if\|select\|try\|whil\%(e\|st\)\)\.\%(\%(\<end\.\)\@!.\)*$' + let l:indent = indent(l:prevlnum) + let l:prevline = getline(l:prevlnum) + if l:prevline =~# '^\s*\%(case\|catch[dt]\=\|do\|else\%(if\)\=\|fcase\|for\%(_\a\k*\)\=\|if\|select\|try\|whil\%(e\|st\)\)\.\%(\%(\<end\.\)\@!.\)*$' " Increase indentation after an initial control word that starts or " continues a block and is not terminated by "end." - let indent += shiftwidth() - elseif g:j_indent_definitions && (prevline =~# '\<\%([1-4]\|13\|adverb\|conjunction\|verb\|monad\|dyad\)\s\+\%(:\s*0\|def\s\+0\|define\)\>' || prevline =~# '^\s*:\s*$') + let l:indent += shiftwidth() + elseif g:j_indent_definitions && (l:prevline =~# '\<\%([1-4]\|13\|adverb\|conjunction\|verb\|monad\|dyad\)\s\+\%(:\s*0\|def\s\+0\|define\)\>' || l:prevline =~# '^\s*:\s*$') " Increase indentation in explicit definitions of adverbs, conjunctions, " and verbs - let indent += shiftwidth() + let l:indent += shiftwidth() endif " Decrease indentation in lines that start with either control words that " continue or end a block, or the special items ")" and ":" if getline(v:lnum) =~# '^\s*\%()\|:\|\%(case\|catch[dt]\=\|do\|else\%(if\)\=\|end\|fcase\)\.\)' - let indent -= shiftwidth() + let l:indent -= shiftwidth() endif - return indent + return l:indent endfunction diff --git a/runtime/indent/sh.vim b/runtime/indent/sh.vim index 0394ee22e8..5bd8c77fab 100644 --- a/runtime/indent/sh.vim +++ b/runtime/indent/sh.vim @@ -1,16 +1,21 @@ " Vim indent file -" Language: Shell Script -" Maintainer: Peter Aronoff <telemachus@arpinum.org> -" Original Author: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2014-08-22 +" Language: Shell Script +" Maintainer: Christian Brabandt <cb@256bit.org> +" Previous Maintainer: Peter Aronoff <telemachus@arpinum.org> +" Original Author: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2015-12-15 +" License: Vim (see :h license) +" Repository: https://github.com/chrisbra/vim-sh-indent if exists("b:did_indent") finish endif let b:did_indent = 1 +let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<' + setlocal indentexpr=GetShIndent() -setlocal indentkeys+=0=then,0=do,0=else,0=elif,0=fi,0=esac,0=done,),0=;;,0=;& +setlocal indentkeys+=0=then,0=do,0=else,0=elif,0=fi,0=esac,0=done,0=end,),0=;;,0=;& setlocal indentkeys+=0=fin,0=fil,0=fip,0=fir,0=fix setlocal indentkeys-=:,0# setlocal nosmartindent @@ -54,8 +59,8 @@ function! GetShIndent() let ind = indent(lnum) let line = getline(lnum) - if line =~ '^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\)\>' - if line !~ '\<\%(fi\|esac\|done\)\>\s*\%(#.*\)\=$' + if line =~ '^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>' + if line !~ '\<\%(fi\|esac\|done\|end\)\>\s*\%(#.*\)\=$' let ind += s:indent_value('default') endif elseif s:is_case_label(line, pnum) @@ -76,7 +81,7 @@ function! GetShIndent() let pine = line let line = getline(v:lnum) - if line =~ '^\s*\%(then\|do\|else\|elif\|fi\|done\)\>' || line =~ '^\s*}' + if line =~ '^\s*\%(then\|do\|else\|elif\|fi\|done\|end\)\>' || line =~ '^\s*}' let ind -= s:indent_value('default') elseif line =~ '^\s*esac\>' && s:is_case_empty(getline(v:lnum - 1)) let ind -= s:indent_value('default') diff --git a/runtime/indent/systemd.vim b/runtime/indent/systemd.vim new file mode 100644 index 0000000000..a05a87bb1c --- /dev/null +++ b/runtime/indent/systemd.vim @@ -0,0 +1,10 @@ +" Vim indent file +" Language: systemd.unit(5) + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Looks a lot like dosini files. +runtime! indent/dosini.vim diff --git a/runtime/indent/teraterm.vim b/runtime/indent/teraterm.vim new file mode 100644 index 0000000000..ba24257b02 --- /dev/null +++ b/runtime/indent/teraterm.vim @@ -0,0 +1,67 @@ +" Vim indent file +" Language: Tera Term Language (TTL) +" Based on Tera Term Version 4.86 +" Maintainer: Ken Takata +" URL: https://github.com/k-takata/vim-teraterm +" Last Change: 2015 Jun 4 +" Filenames: *.ttl +" License: VIM License + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent +setlocal noautoindent +setlocal indentexpr=GetTeraTermIndent(v:lnum) +setlocal indentkeys=!^F,o,O,e +setlocal indentkeys+==elseif,=endif,=loop,=next,=enduntil,=endwhile + +if exists("*GetTeraTermIndent") + finish +endif + +" The shiftwidth() function is relatively new. +" Don't require it to exist. +if exists('*shiftwidth') + function s:sw() abort + return shiftwidth() + endfunction +else + function s:sw() abort + return &shiftwidth + endfunction +endif + +function! GetTeraTermIndent(lnum) + let l:prevlnum = prevnonblank(a:lnum-1) + if l:prevlnum == 0 + " top of file + return 0 + endif + + " grab the previous and current line, stripping comments. + let l:prevl = substitute(getline(l:prevlnum), ';.*$', '', '') + let l:thisl = substitute(getline(a:lnum), ';.*$', '', '') + let l:previ = indent(l:prevlnum) + + let l:ind = l:previ + + if l:prevl =~ '^\s*if\>.*\<then\s*$' + " previous line opened a block + let l:ind += s:sw() + endif + if l:prevl =~ '^\s*\%(elseif\|else\|do\|until\|while\|for\)\>' + " previous line opened a block + let l:ind += s:sw() + endif + if l:thisl =~ '^\s*\%(elseif\|else\|endif\|enduntil\|endwhile\|loop\|next\)\>' + " this line closed a block + let l:ind -= s:sw() + endif + + return l:ind +endfunction + +" vim: ts=8 sw=2 sts=2 diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim index ff4af027b4..7511325af4 100644 --- a/runtime/indent/vim.vim +++ b/runtime/indent/vim.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: Vim script " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2014 Sep 19 +" Last Change: 2014 Dec 12 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -89,7 +89,7 @@ function GetVimIndentIntern() " Subtract a 'shiftwidth' on a :endif, :endwhile, :catch, :finally, :endtry, " :endfun, :else and :augroup END. - if cur_text =~ '^\s*\(ene\@!\|cat\|fina\|el\|aug\%[roup]\s*!\=\s\+END\)' + if cur_text =~ '^\s*\(ene\@!\|cat\|fina\|el\|aug\%[roup]\s*!\=\s\+[eE][nN][dD]\)' let ind = ind - &sw endif diff --git a/runtime/indent/yaml.vim b/runtime/indent/yaml.vim index 1d03715773..aa4906ce0a 100644 --- a/runtime/indent/yaml.vim +++ b/runtime/indent/yaml.vim @@ -1,6 +1,7 @@ " Vim indent file " Language: YAML " Maintainer: Nikolai Pavlov <zyx.vim@gmail.com> +" Last Change: 2015 Nov 01 " Only load this indent file when no other was loaded. if exists('b:did_indent') @@ -13,7 +14,7 @@ set cpo&vim let b:did_indent = 1 setlocal indentexpr=GetYAMLIndent(v:lnum) -setlocal indentkeys=!^F,o,O,0#,0},0],<:>,- +setlocal indentkeys=!^F,o,O,0#,0},0],<:>,0- setlocal nosmartindent let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<' @@ -115,8 +116,13 @@ function GetYAMLIndent(lnum) \ s:liststartregex)) elseif line =~# s:mapkeyregex " Same for line containing mapping key - return indent(s:FindPrevLEIndentedLineMatchingRegex(a:lnum, - \ s:mapkeyregex)) + let prevmapline = s:FindPrevLEIndentedLineMatchingRegex(a:lnum, + \ s:mapkeyregex) + if getline(prevmapline) =~# '^\s*- ' + return indent(prevmapline) + 2 + else + return indent(prevmapline) + endif elseif prevline =~# '^\s*- ' " - List with " multiline scalar diff --git a/runtime/macros/less.bat b/runtime/macros/less.bat index bbe619bc92..7395a70003 100644 --- a/runtime/macros/less.bat +++ b/runtime/macros/less.bat @@ -4,7 +4,7 @@ rem Read stdin if no arguments were given. rem Written by Ken Takata. if "%1"=="" ( - vim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" - + nvim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" - ) else ( - vim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" %* + nvim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" %* ) diff --git a/runtime/macros/less.sh b/runtime/macros/less.sh index e29958f7ad..125162f10a 100755 --- a/runtime/macros/less.sh +++ b/runtime/macros/less.sh @@ -8,9 +8,9 @@ if test -t 1; then echo "Missing filename" 1>&2 exit fi - vim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' - + nvim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' - else - vim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' "$@" + nvim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' "$@" fi else # Output is not a terminal, cat arguments or stdin diff --git a/runtime/macros/less.vim b/runtime/macros/less.vim index d38bd3781d..7ccbeb32ea 100644 --- a/runtime/macros/less.vim +++ b/runtime/macros/less.vim @@ -1,6 +1,6 @@ " Vim script to work like "less" " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2014 May 13 +" Last Change: 2015 Nov 15 " Avoid loading this file twice, allow the user to define his own script. if exists("loaded_less") @@ -48,6 +48,12 @@ set nows let s:lz = &lz set lz +" Allow the user to define a function, which can set options specifically for +" this script. +if exists('*LessInitFunc') + call LessInitFunc() +endif + " Used after each command: put cursor at end and display position if &wrap noremap <SID>L L0:redraw<CR>:file<CR> diff --git a/runtime/makemenu.vim b/runtime/makemenu.vim index b78fdfd601..839dbdac07 100644 --- a/runtime/makemenu.vim +++ b/runtime/makemenu.vim @@ -177,6 +177,7 @@ SynMenu DE.Doxygen.C\ with\ doxygen:c.doxygen SynMenu DE.Doxygen.C++\ with\ doxygen:cpp.doxygen SynMenu DE.Doxygen.IDL\ with\ doxygen:idl.doxygen SynMenu DE.Doxygen.Java\ with\ doxygen:java.doxygen +SynMenu DE.Doxygen.DataScript\ with\ doxygen:datascript.doxygen SynMenu DE.Dracula:dracula SynMenu DE.DSSSL:dsl SynMenu DE.DTD:dtd diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 09406f260b..07dcd31b1b 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2014 Nov 19 +" Last Change: 2015 Jul 22 " If there already is an option window, jump to that one. if bufwinnr("option-window") > 0 @@ -289,6 +289,10 @@ call append("$", " \tset tl=" . &tl) call append("$", "tags\tlist of file names to search for tags") call append("$", "\t(global or local to buffer)") call <SID>OptionG("tag", &tag) +call append("$", "tagcase\thow to handle case when searching in tags files:") +call append("$", "\t\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"") +call append("$", "\t(global or local to buffer)") +call <SID>OptionG("tc", &tc) call append("$", "tagrelative\tfile names in a tags file are relative to the tags file") call <SID>BinOptionG("tr", &tr) call append("$", "tagstack\ta :tag command will use the tagstack") @@ -406,6 +410,10 @@ call append("$", "highlight\twhich highlighting to use for various occasions") call <SID>OptionG("hl", &hl) call append("$", "hlsearch\thighlight all matches for the last used search pattern") call <SID>BinOptionG("hls", &hls) +if has("termguicolors") + call append("$", "termguicolors\tuse GUI colors for the terminal") + call <SID>BinOptionG("tgc", &tgc) +endif if has("syntax") call append("$", "cursorcolumn\thighlight the screen column of the cursor") call append("$", "\t(local to window)") @@ -540,10 +548,6 @@ if has("title") call append("$", "iconstring\twhen not empty, text for the icon of this window") call <SID>OptionG("iconstring", &iconstring) endif -if has("win32") - call append("$", "restorescreen\trestore the screen contents when exiting Vim") - call <SID>BinOptionG("rs", &rs) -endif call <SID>Header("using the mouse") @@ -677,6 +681,8 @@ call append("$", "errorbells\tring the bell for error messages") call <SID>BinOptionG("eb", &eb) call append("$", "visualbell\tuse a visual bell instead of beeping") call <SID>BinOptionG("vb", &vb) +call append("$", "belloff\tdo not ring the bell for these reasons") +call <SID>OptionG("belloff", &belloff) if has("multi_lang") call append("$", "helplang\tlist of preferred languages for finding help") call <SID>OptionG("hlg", &hlg) @@ -926,6 +932,9 @@ call <SID>BinOptionL("bin") call append("$", "endofline\tlast line in the file has an end-of-line") call append("$", "\t(local to buffer)") call <SID>BinOptionL("eol") +call append("$", "fixendofline\tfixes missing end-of-line at end of text file") +call append("$", "\t(local to buffer)") +call <SID>BinOptionL("fixeol") if has("multi_byte") call append("$", "bomb\tprepend a Byte Order Mark to the file") call append("$", "\t(local to buffer)") @@ -973,8 +982,6 @@ call <SID>OptionG("dir", &dir) call append("$", "swapfile\tuse a swap file for this buffer") call append("$", "\t(local to buffer)") call <SID>BinOptionL("swf") -call append("$", "swapsync\t\"sync\", \"fsync\" or empty; how to flush a swap file to disk") -call <SID>OptionG("sws", &sws) call append("$", "updatecount\tnumber of characters typed to cause a swap file update") call append("$", " \tset uc=" . &uc) call append("$", "updatetime\ttime in msec after which the swap file will be updated") diff --git a/runtime/plugin/matchit.vim b/runtime/plugin/matchit.vim index 74c1a1eb92..70867b1f93 100644 --- a/runtime/plugin/matchit.vim +++ b/runtime/plugin/matchit.vim @@ -303,7 +303,7 @@ fun! s:CleanUp(options, mode, startline, startcol, ...) let regexp = s:Wholematch(matchline, a:1, currcol-1) let endcol = matchend(matchline, regexp) if endcol > currcol " This is NOT off by one! - execute "normal!" . (endcol - currcol) . "l" + call cursor(0, endcol) endif endif " a:0 endif " a:mode != "o" && etc. diff --git a/runtime/plugin/matchparen.vim b/runtime/plugin/matchparen.vim index 3804ab949a..873302efee 100644 --- a/runtime/plugin/matchparen.vim +++ b/runtime/plugin/matchparen.vim @@ -1,6 +1,6 @@ " Vim plugin for showing matching parens " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2014 Jul 19 +" Last Change: 2015 Dec 31 " Exit quickly when: " - this plugin was already loaded (or disabled) @@ -55,14 +55,19 @@ function! s:Highlight_Matching_Pair() let before = 0 let text = getline(c_lnum) - let c = text[c_col - 1] + let matches = matchlist(text, '\(.\)\=\%'.c_col.'c\(.\=\)') + if empty(matches) + let [c_before, c] = ['', ''] + else + let [c_before, c] = matches[1:2] + endif let plist = split(&matchpairs, '.\zs[:,]') let i = index(plist, c) if i < 0 " not found, in Insert mode try character before the cursor if c_col > 1 && (mode() == 'i' || mode() == 'R') - let before = 1 - let c = text[c_col - 2] + let before = strlen(c_before) + let c = c_before let i = index(plist, c) endif if i < 0 diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim index cad4d31a04..3776ac30f8 100644 --- a/runtime/plugin/netrwPlugin.vim +++ b/runtime/plugin/netrwPlugin.vim @@ -20,19 +20,7 @@ if &cp || exists("g:loaded_netrwPlugin") finish endif -let g:loaded_netrwPlugin = "v153" -if v:version < 702 - echohl WarningMsg - echo "***warning*** you need vim version 7.2 for this version of netrw" - echohl None - finish -endif -if v:version < 703 || (v:version == 703 && !has("patch465")) - echohl WarningMsg - echo "***warning*** this version of netrw needs vim 7.3.465 or later" - echohl Normal - finish -endif +let g:loaded_netrwPlugin = "v154" let s:keepcpo = &cpo set cpo&vim "DechoRemOn @@ -102,6 +90,12 @@ if !exists("g:netrw_nogx") vno <silent> <Plug>NetrwBrowseXVis :<c-u>call netrw#BrowseXVis()<cr> endif endif +if exists("g:netrw_usetab") && g:netrw_usetab + if maparg('<c-tab>','n') == "" + nmap <unique> <c-tab> <Plug>NetrwShrink + endif + nno <silent> <Plug>NetrwShrink :call netrw#Shrink()<cr> +endif " --------------------------------------------------------------------- " LocalBrowse: invokes netrw#LocalBrowseCheck() on directory buffers {{{2 @@ -135,15 +129,19 @@ fun! s:LocalBrowse(dirname) elseif isdirectory(a:dirname) " call Decho("(LocalBrowse) dirname<".a:dirname."> ft=".&ft." (isdirectory, not amiga)") " call Dredir("LocalBrowse ft last set: ","verbose set ft") +" call Decho("(s:LocalBrowse) COMBAK#23: buf#".bufnr("%")." file<".expand("%")."> line#".line(".")." col#".col(".")) sil! call netrw#LocalBrowseCheck(a:dirname) +" call Decho("(s:LocalBrowse) COMBAK#24: buf#".bufnr("%")." file<".expand("%")."> line#".line(".")." col#".col(".")) if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt exe w:netrw_bannercnt +" call Decho("(s:LocalBrowse) COMBAK#25: buf#".bufnr("%")." file<".expand("%")."> line#".line(".")." col#".col(".")) endif else " not a directory, ignore it " call Decho("(LocalBrowse) dirname<".a:dirname."> not a directory, ignoring...") endif +" call Decho("(s:LocalBrowse) COMBAK#26: buf#".bufnr("%")." file<".expand("%")."> line#".line(".")." col#".col(".")) " call Dret("s:LocalBrowse") endfun diff --git a/runtime/plugin/rplugin.vim b/runtime/plugin/rplugin.vim index 2b2d333738..b4b03032b3 100644 --- a/runtime/plugin/rplugin.vim +++ b/runtime/plugin/rplugin.vim @@ -1,5 +1,16 @@ -if exists('loaded_remote_plugins') || &cp +if exists('g:loaded_remote_plugins') finish endif -let loaded_remote_plugins = 1 -call remote#host#LoadRemotePlugins() +let g:loaded_remote_plugins = 1 + +command! UpdateRemotePlugins call remote#host#UpdateRemotePlugins() + +augroup nvim-rplugin + autocmd! + autocmd FuncUndefined * + \ call remote#host#LoadRemotePluginsEvent( + \ 'FuncUndefined', expand('<amatch>')) + autocmd CmdUndefined * + \ call remote#host#LoadRemotePluginsEvent( + \ 'CmdUndefined', expand('<amatch>')) +augroup END diff --git a/runtime/plugin/spellfile.vim b/runtime/plugin/spellfile.vim index 437296090c..e03659d6d6 100644 --- a/runtime/plugin/spellfile.vim +++ b/runtime/plugin/spellfile.vim @@ -1,15 +1,8 @@ " Vim plugin for downloading spell files -" Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2006 Feb 01 -" Exit quickly when: -" - this plugin was already loaded -" - when 'compatible' is set -" - some autocommands are already taking care of spell files if exists("loaded_spellfile_plugin") || &cp || exists("#SpellFileMissing") finish endif let loaded_spellfile_plugin = 1 -" The function is in the autoload directory. autocmd SpellFileMissing * call spellfile#LoadFile(expand('<amatch>')) diff --git a/runtime/plugin/tohtml.vim b/runtime/plugin/tohtml.vim index eb47b1a7c3..b438dea811 100644 --- a/runtime/plugin/tohtml.vim +++ b/runtime/plugin/tohtml.vim @@ -1,6 +1,6 @@ " Vim plugin for converting a syntax highlighted file to HTML. " Maintainer: Ben Fritz <fritzophrenic@gmail.com> -" Last Change: 2013 Jul 08 +" Last Change: 2015 Sep 08 " " The core of the code is in $VIMRUNTIME/autoload/tohtml.vim and " $VIMRUNTIME/syntax/2html.vim @@ -67,20 +67,24 @@ if exists('g:loaded_2html_plugin') finish endif -let g:loaded_2html_plugin = 'vim7.4_v1' +let g:loaded_2html_plugin = 'vim7.4_v2' " " Changelog: {{{ -" 7.4_v1 (this version): Fix modeline mangling for new "Vim:" format, and +" 7.4_v2 (this version): Fix error raised when converting a diff containing +" an empty buffer. Jan Stocker: allow g:html_font to +" take a list so it is easier to specfiy fallback +" fonts in the generated CSS. +" 7.4_v1 (Vim 7.4.0000): Fix modeline mangling for new "Vim:" format, and " also for version-specific modelines like "vim>703:". " " 7.3 updates: {{{ -" 7.3_v14 (ad6996a23e3e): Allow suppressing line number anchors using +" 7.3_v14 (Vim 7.3.1246): Allow suppressing line number anchors using " g:html_line_ids=0. Allow customizing " important IDs (like line IDs and fold IDs) using " g:html_id_expr evalutated when the buffer conversion " is started. -" 7.3_v13 (2eb30f341e8d): Keep foldmethod at manual in the generated file and +" 7.3_v13 (Vim 7.3.1088): Keep foldmethod at manual in the generated file and " insert modeline to set it to manual. " Fix bug: diff mode with 2 unsaved buffers creates a " duplicate of one buffer instead of including both. @@ -91,7 +95,7 @@ let g:loaded_2html_plugin = 'vim7.4_v1' " Fix XML validation error: &nsbp; not part of XML. " Allow TOhtml to chain together with other commands " using |. -" 7.3_v12 (9910cbff5f16): Fix modeline mangling to also work for when multiple +" 7.3_v12 (Vim 7.3.0616): Fix modeline mangling to also work for when multiple " highlight groups make up the start-of-modeline text. " Improve render time of page with uncopyable regions " by not using one-input-per-char. Change name of @@ -117,23 +121,23 @@ let g:loaded_2html_plugin = 'vim7.4_v1' " http://groups.google.com/d/topic/vim_dev/B6FSGfq9VoI/discussion. " This patch has not yet been included in Vim, thus " these changes are removed in the next version. -" 7.3_v10 (fd09a9c8468e): Fix error E684 when converting a range wholly inside +" 7.3_v10 (Vim 7.3.0227): Fix error E684 when converting a range wholly inside " multiple nested folds with dynamic folding on. " Also fix problem with foldtext in this situation. -" 7.3_v9 (0877b8d6370e): Add html_pre_wrap option active with html_use_css +" 7.3_v9 (Vim 7.3.0170): Add html_pre_wrap option active with html_use_css " and without html_no_pre, default value same as " 'wrap' option, (Andy Spencer). Don't use " 'fileencoding' for converted document encoding if " 'buftype' indicates a special buffer which isn't " written. -" 7.3_v8 (85c5a72551e2): Add html_expand_tabs option to allow leaving tab +" 7.3_v8 (Vim 7.3.0100): Add html_expand_tabs option to allow leaving tab " characters in generated output (Andy Spencer). " Escape text that looks like a modeline so Vim " doesn't use anything in the converted HTML as a " modeline. Bugfixes: Fix folding when a fold starts " before the conversion range. Remove fold column when " there are no folds. -" 7.3_v7 (840c3cadb842): see betas released on vim_dev below: +" 7.3_v7 (Vim 7-3-0063): see betas released on vim_dev below: " 7.3_v7b3: Fixed bug, convert Unicode to UTF-8 all the way. " 7.3_v7b2: Remove automatic detection of encodings that are not " supported by all major browsers according to @@ -147,23 +151,22 @@ let g:loaded_2html_plugin = 'vim7.4_v1' " charset, and make sure the 'fenc' of the generated " file matches its indicated charset. Add charsets for " all of Vim's natively supported encodings. -" 7.3_v6 (0d3f0e3d289b): Really fix bug with 'nowrapscan', 'magic' and other +" 7.3_v6 (Vim 7.3.0000): Really fix bug with 'nowrapscan', 'magic' and other " user settings interfering with diff mode generation, " trailing whitespace (e.g. line number column) when " using html_no_pre, and bugs when using " html_hover_unfold. " 7.3_v5 ( unreleased ): Fix bug with 'nowrapscan' and also with out-of-sync " folds in diff mode when first line was folded. -" 7.3_v4 (7e008c174cc3): Bugfixes, especially for xhtml markup, and diff mode -" 7.3_v3 (a29075150aee): Refactor option handling and make html_use_css +" 7.3_v4 (Vim 7.3.0000): Bugfixes, especially for xhtml markup, and diff mode +" 7.3_v3 (Vim 7.3.0000): Refactor option handling and make html_use_css " default to true when not set to anything. Use strict " doctypes where possible. Rename use_xhtml option to " html_use_xhtml for consistency. Use .xhtml extension " when using this option. Add meta tag for settings. -" 7.3_v2 (80229a724a11): Fix syntax highlighting in diff mode to use both the +" 7.3_v2 (Vim 7.3.0000): Fix syntax highlighting in diff mode to use both the " diff colors and the normal syntax colors -" 7.3_v1 (e7751177126b): Add conceal support and meta tags in output -" Pre-v1 baseline: Mercurial changeset 3c9324c0800e +" 7.3_v1 (Vim 7.3.0000): Add conceal support and meta tags in output "}}} "}}} diff --git a/runtime/synmenu.vim b/runtime/synmenu.vim index 2db72a2b60..76f60131f2 100644 --- a/runtime/synmenu.vim +++ b/runtime/synmenu.vim @@ -161,6 +161,7 @@ an 50.30.290 &Syntax.DE.Doxygen.C\ with\ doxygen :cal SetSyn("c.doxygen")<CR> an 50.30.300 &Syntax.DE.Doxygen.C++\ with\ doxygen :cal SetSyn("cpp.doxygen")<CR> an 50.30.310 &Syntax.DE.Doxygen.IDL\ with\ doxygen :cal SetSyn("idl.doxygen")<CR> an 50.30.320 &Syntax.DE.Doxygen.Java\ with\ doxygen :cal SetSyn("java.doxygen")<CR> +an 50.30.320 &Syntax.DE.Doxygen.DataScript\ with\ doxygen :cal SetSyn("datascript.doxygen")<CR> an 50.30.330 &Syntax.DE.Dracula :cal SetSyn("dracula")<CR> an 50.30.340 &Syntax.DE.DSSSL :cal SetSyn("dsl")<CR> an 50.30.350 &Syntax.DE.DTD :cal SetSyn("dtd")<CR> diff --git a/runtime/syntax/2html.vim b/runtime/syntax/2html.vim index 187b1be1b0..ddc7819be2 100644 --- a/runtime/syntax/2html.vim +++ b/runtime/syntax/2html.vim @@ -1,6 +1,6 @@ " Vim syntax support file " Maintainer: Ben Fritz <fritzophrenic@gmail.com> -" Last Change: 2013 Jul 08 +" Last Change: 2015 Sep 08 " " Additional contributors: " @@ -26,7 +26,11 @@ let s:end=line('$') " Font if exists("g:html_font") - let s:htmlfont = "'". g:html_font . "', monospace" + if type(g:html_font) == type([]) + let s:htmlfont = "'". join(g:html_font,"','") . "', monospace" + else + let s:htmlfont = "'". g:html_font . "', monospace" + endif else let s:htmlfont = "monospace" endif diff --git a/runtime/syntax/amiga.vim b/runtime/syntax/amiga.vim index e70f995f68..4ccab1950f 100644 --- a/runtime/syntax/amiga.vim +++ b/runtime/syntax/amiga.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: AmigaDos -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Sep 11, 2006 -" Version: 6 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 7 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_AMIGA " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/asterisk.vim b/runtime/syntax/asterisk.vim index 58e867d42a..fd4ec39c91 100644 --- a/runtime/syntax/asterisk.vim +++ b/runtime/syntax/asterisk.vim @@ -1,8 +1,9 @@ " Vim syntax file " Language: Asterisk config file -" Maintainer: brc007 +" Maintainer: Jean Aunis <jean.aunis@yahoo.fr> +" Previous Maintainer: brc007 " Updated for 1.2 by Tilghman Lesher (Corydon76) -" Last Change: 2006 Mar 20 +" Last Change: 2015 Feb 27 " version 0.4 " if version < 600 @@ -17,18 +18,19 @@ syn sync fromstart syn keyword asteriskTodo TODO contained syn match asteriskComment ";.*" contains=asteriskTodo syn match asteriskContext "\[.\{-}\]" -syn match asteriskExten "^\s*exten\s*=>\?\s*[^,]\+" contains=asteriskPattern +syn match asteriskExten "^\s*\zsexten\s*=>\?\s*[^,]\+\ze," contains=asteriskPattern nextgroup=asteriskPriority +syn match asteriskExten "^\s*\zssame\s*=>\?\s*\ze" nextgroup=asteriskPriority syn match asteriskExten "^\s*\(register\|channel\|ignorepat\|include\|\(no\)\?load\)\s*=>\?" syn match asteriskPattern "_\(\[[[:alnum:]#*\-]\+\]\|[[:alnum:]#*]\)*\.\?" contained syn match asteriskPattern "[^A-Za-z0-9,]\zs[[:alnum:]#*]\+\ze" contained syn match asteriskApp ",\zs[a-zA-Z]\+\ze$" syn match asteriskApp ",\zs[a-zA-Z]\+\ze(" " Digits plus oldlabel (newlabel) -syn match asteriskPriority ",\zs[[:digit:]]\+\(+[[:alpha:]][[:alnum:]_]*\)\?\(([[:alpha:]][[:alnum:]_]*)\)\?\ze," contains=asteriskLabel +syn match asteriskPriority "\zs[[:digit:]]\+\(+[[:alpha:]][[:alnum:]_]*\)\?\(([[:alpha:]][[:alnum:]_]*)\)\?\ze," contains=asteriskLabel " oldlabel plus digits (newlabel) -syn match asteriskPriority ",\zs[[:alpha:]][[:alnum:]_]*+[[:digit:]]\+\(([[:alpha:]][[:alnum:]_]*)\)\?\ze," contains=asteriskLabel +syn match asteriskPriority "\zs[[:alpha:]][[:alnum:]_]*+[[:digit:]]\+\(([[:alpha:]][[:alnum:]_]*)\)\?\ze," contains=asteriskLabel " s or n plus digits (newlabel) -syn match asteriskPriority ",\zs[sn]\(+[[:digit:]]\+\)\?\(([[:alpha:]][[:alnum:]_]*)\)\?\ze," contains=asteriskLabel +syn match asteriskPriority "\zs[sn]\(+[[:digit:]]\+\)\?\(([[:alpha:]][[:alnum:]_]*)\)\?\ze," contains=asteriskLabel syn match asteriskLabel "(\zs[[:alpha:]][[:alnum:]]*\ze)" contained syn match asteriskError "^\s*#\s*[[:alnum:]]*" syn match asteriskInclude "^\s*#\s*\(include\|exec\)\s.*" diff --git a/runtime/syntax/autohotkey.vim b/runtime/syntax/autohotkey.vim index 42d4cfdfe4..764f94b11a 100644 --- a/runtime/syntax/autohotkey.vim +++ b/runtime/syntax/autohotkey.vim @@ -1,7 +1,8 @@ " Vim syntax file " Language: AutoHotkey script file -" Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2008-06-22 +" Maintainer: SungHyun Nam <goweol@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2015-10-29 if exists("b:current_syntax") finish @@ -179,6 +180,7 @@ syn keyword autohotkeyRepeat syn keyword autohotkeyConditional \ IfExist IfNotExist If IfEqual IfLess IfGreater Else + \ IfWinExist IfWinNotExist syn match autohotkeyPreProcStart \ nextgroup= diff --git a/runtime/syntax/awk.vim b/runtime/syntax/awk.vim index f80a582226..40491eea1a 100644 --- a/runtime/syntax/awk.vim +++ b/runtime/syntax/awk.vim @@ -1,13 +1,15 @@ " Vim syntax file " Language: awk, nawk, gawk, mawk " Maintainer: Antonio Colombo <azc100@gmail.com> -" Last Change: 2012 May 18 +" Last Change: 2014 Oct 21 " AWK ref. is: Alfred V. Aho, Brian W. Kernighan, Peter J. Weinberger " The AWK Programming Language, Addison-Wesley, 1988 " GAWK ref. is: Arnold D. Robbins " Effective AWK Programming, Third Edition, O'Reilly, 2001 +" Effective AWK Programming, Fourth Edition, O'Reilly, 2015 +" (also available with the gawk source distribution) " MAWK is a "new awk" meaning it implements AWK ref. " mawk conforms to the Posix 1003.2 (draft 11.3) @@ -33,17 +35,27 @@ set cpo&vim syn keyword awkStatement break continue delete exit syn keyword awkStatement function getline next syn keyword awkStatement print printf return -" GAWK ref. p. 117 +" GAWK ref. Chapter 7 syn keyword awkStatement nextfile -" AWK ref. p. 42, GAWK ref. p. 142-166 -syn keyword awkFunction atan2 close cos exp fflush int log rand sin sqrt srand -syn keyword awkFunction gsub index length match split sprintf sub -syn keyword awkFunction substr system -" GAWK ref. p. 142-166 -syn keyword awkFunction asort gensub mktime strftime strtonum systime +" +" GAWK ref. Chapter 9, Functions +" +" Numeric Functions +syn keyword awkFunction atan2 cos div exp int log rand sin sqrt srand +" String Manipulation Functions +syn keyword awkFunction asort asort1 gensub gsub index length match +syn keyword awkFunction patsplit split sprintf strtonum sub substr syn keyword awkFunction tolower toupper -syn keyword awkFunction and or xor compl lshift rshift -syn keyword awkFunction dcgettext bindtextdomain +" Input Output Functions +syn keyword awkFunction close fflush system +" Time Functions +syn keyword awkFunction mktime strftime systime +" Bit Manipulation Functions +syn keyword awkFunction and compl lshift or rshift xor +" Getting Type Function +syn keyword awkFunction isarray +" String-Translation Functions +syn keyword awkFunction bindtextdomain dcgettext dcngetext syn keyword awkConditional if else syn keyword awkRepeat while for @@ -51,13 +63,16 @@ syn keyword awkRepeat while for syn keyword awkTodo contained TODO syn keyword awkPatterns BEGIN END -" AWK ref. p. 36 -syn keyword awkVariables ARGC ARGV FILENAME FNR FS NF NR -syn keyword awkVariables OFMT OFS ORS RLENGTH RS RSTART SUBSEP -" GAWK ref. p. 120-126 -syn keyword awkVariables ARGIND BINMODE CONVFMT ENVIRON ERRNO -syn keyword awkVariables FIELDWIDTHS IGNORECASE LINT PROCINFO -syn keyword awkVariables RT RLENGTH TEXTDOMAIN + +" GAWK ref. Chapter 7 +" Built-in Variables That Control awk +syn keyword awkVariables BINMODE CONVFMT FIELDWIDTHS FPAT FS +syn keyword awkVariables IGNORECASE LINT OFMT OFS ORS PREC +syn keyword awkVariables ROUNDMODE RS SUBSEP TEXTDOMAIN +" Built-in Variables That Convey Information +syn keyword awkVariables ARGC ARGV ARGIND ENVIRON ERRNO FILENAME +syn keyword awkVariables FNR NF FUNCTAB NR PROCINFO RLENGTH RSTART +syn keyword awkVariables RT SYMTAB syn keyword awkRepeat do @@ -115,15 +130,15 @@ syn case match "syn match awkIdentifier "\<[a-zA-Z_][a-zA-Z0-9_]*\>" " Arithmetic operators: +, and - take care of ++, and -- -"syn match awkOperator "+\|-\|\*\|/\|%\|=" -"syn match awkOperator "+=\|-=\|\*=\|/=\|%=" -"syn match awkOperator "^\|^=" +syn match awkOperator "+\|-\|\*\|/\|%\|=" +syn match awkOperator "+=\|-=\|\*=\|/=\|%=" +syn match awkOperator "^\|^=" " Comparison expressions. -"syn match awkExpression "==\|>=\|=>\|<=\|=<\|\!=" -"syn match awkExpression "\~\|\!\~" -"syn match awkExpression "?\|:" -"syn keyword awkExpression in +syn match awkExpression "==\|>=\|=>\|<=\|=<\|\!=" +syn match awkExpression "\~\|\!\~" +syn match awkExpression "?\|:" +syn keyword awkExpression in " Boolean Logic (OR, AND, NOT) "syn match awkBoolLogic "||\|&&\|\!" @@ -187,8 +202,8 @@ if version >= 508 || !exists("did_awk_syn_inits") HiLink awkFloat Float HiLink awkFileIO Special - "HiLink awkOperator Special - "HiLink awkExpression Special + HiLink awkOperator Special + HiLink awkExpression Special HiLink awkBoolLogic Special HiLink awkPatterns Special diff --git a/runtime/syntax/bzl.vim b/runtime/syntax/bzl.vim new file mode 100644 index 0000000000..b0ee9454ff --- /dev/null +++ b/runtime/syntax/bzl.vim @@ -0,0 +1,16 @@ +" Vim syntax file +" Language: Bazel (http://bazel.io) +" Maintainer: David Barnett (https://github.com/google/vim-ft-bzl) +" Last Change: 2015 Aug 11 + +if exists('b:current_syntax') + finish +endif + + +runtime! syntax/python.vim + +let b:current_syntax = 'bzl' + +syn region bzlRule start='^\w\+($' end='^)\n*' transparent fold +syn region bzlList start='\[' end='\]' transparent fold diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim index 128e1a2a4a..ac4909edba 100644 --- a/runtime/syntax/c.vim +++ b/runtime/syntax/c.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: C " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2014 Nov 13 +" Last Change: 2015 Mar 05 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") @@ -47,16 +47,17 @@ if !exists("c_no_cformat") endif " cCppString: same as cString, but ends at end of line -if s:ft ==# "cpp" && !exists("cpp_no_cpp11") +if s:ft ==# "cpp" && !exists("cpp_no_cpp11") && !exists("c_no_cformat") " ISO C++11 syn region cString start=+\(L\|u\|u8\|U\|R\|LR\|u8R\|uR\|UR\)\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend syn region cCppString start=+\(L\|u\|u8\|U\|R\|LR\|u8R\|uR\|UR\)\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,cFormat,@Spell -elseif s:ft ==# "c" && !exists("c_no_c11") +elseif s:ft ==# "c" && !exists("c_no_c11") && !exists("c_no_cformat") " ISO C99 syn region cString start=+\%(L\|U\|u8\)\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend syn region cCppString start=+\%(L\|U\|u8\)\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,cFormat,@Spell else " older C or C++ + syn match cFormat display "%%" contained syn region cString start=+L\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend syn region cCppString start=+L\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,cFormat,@Spell endif @@ -80,7 +81,11 @@ syn match cSpecialCharacter display "L'\\x\x\+'" if (s:ft ==# "c" && !exists("c_no_c11")) || (s:ft ==# "cpp" && !exists("cpp_no_cpp11")) " ISO C11 or ISO C++ 11 - syn region cString start=+\%(U\|u8\=\)"+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend + if exists("c_no_cformat") + syn region cString start=+\%(U\|u8\=\)"+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,@Spell extend + else + syn region cString start=+\%(U\|u8\=\)"+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend + endif syn match cCharacter "[Uu]'[^\\]'" syn match cCharacter "[Uu]'[^']*'" contains=cSpecial if exists("c_gnu") @@ -330,6 +335,8 @@ if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu") " POSIX 2001 syn keyword cConstant SIGBUS SIGPOLL SIGPROF SIGSYS SIGURG syn keyword cConstant SIGVTALRM SIGXCPU SIGXFSZ + " non-POSIX signals + syn keyword cConstant SIGWINCH SIGINFO " Add POSIX errors as well syn keyword cConstant E2BIG EACCES EAGAIN EBADF EBADMSG EBUSY syn keyword cConstant ECANCELED ECHILD EDEADLK EDOM EEXIST EFAULT @@ -387,8 +394,13 @@ endif syn cluster cLabelGroup contains=cUserLabel syn match cUserCont display "^\s*\I\i*\s*:$" contains=@cLabelGroup syn match cUserCont display ";\s*\I\i*\s*:$" contains=@cLabelGroup -syn match cUserCont display "^\s*\I\i*\s*:[^:]"me=e-1 contains=@cLabelGroup -syn match cUserCont display ";\s*\I\i*\s*:[^:]"me=e-1 contains=@cLabelGroup +if s:ft ==# 'cpp' + syn match cUserCont display "^\s*\%(class\|struct\|enum\)\@!\I\i*\s*:[^:]"me=e-1 contains=@cLabelGroup + syn match cUserCont display ";\s*\%(class\|struct\|enum\)\@!\I\i*\s*:[^:]"me=e-1 contains=@cLabelGroup +else + syn match cUserCont display "^\s*\I\i*\s*:[^:]"me=e-1 contains=@cLabelGroup + syn match cUserCont display ";\s*\I\i*\s*:[^:]"me=e-1 contains=@cLabelGroup +endif syn match cUserLabel display "\I\i*" contained diff --git a/runtime/syntax/cmake.vim b/runtime/syntax/cmake.vim index 8e54d511e0..4586940c5e 100644 --- a/runtime/syntax/cmake.vim +++ b/runtime/syntax/cmake.vim @@ -2,10 +2,10 @@ " Program: CMake - Cross-Platform Makefile Generator " Module: $RCSfile: cmake-syntax.vim,v $ " Language: CMake +" Maintainer: Dimitri Merejkowsky <d.merej@gmail.com> +" Former Maintainer: Karthik Krishnan <karthik.krishnan@kitware.com> " Author: Andy Cedilnik <andy.cedilnik@kitware.com> -" Maintainer: Karthik Krishnan <karthik.krishnan@kitware.com> -" Last Change: 2012 Jun 01 -" (Dominique Pelle added @Spell) +" Last Change: 2015 Dec 17 " Version: $Revision: 1.10 $ " " Licence: The CMake license applies to this file. See @@ -31,9 +31,9 @@ syn region cmakeVariableValue start=/\${/ end=/}/ \ contained oneline contains=CONTAINED,cmakeTodo syn region cmakeEnvironment start=/\$ENV{/ end=/}/ \ contained oneline contains=CONTAINED,cmakeTodo -syn region cmakeString start=/"/ end=/"/ +syn region cmakeString start=/"/ end=/"/ \ contains=CONTAINED,cmakeTodo,cmakeOperators -syn region cmakeArguments start=/(/ end=/)/ +syn region cmakeArguments start=/(/ end=/)/ \ contains=ALLBUT,cmakeArguments,cmakeTodo syn keyword cmakeSystemVariables \ WIN32 UNIX APPLE CYGWIN BORLAND MINGW MSVC MSVC_IDE MSVC60 MSVC70 MSVC71 MSVC80 MSVC90 @@ -44,11 +44,11 @@ syn keyword cmakeDeprecated ABSTRACT_FILES BUILD_NAME SOURCE_FILES SOURCE_FILES_ \ nextgroup=cmakeArguments " The keywords are generated as: cmake --help-command-list | tr "\n" " " -syn keyword cmakeStatement +syn keyword cmakeStatement \ ADD_CUSTOM_COMMAND ADD_CUSTOM_TARGET ADD_DEFINITIONS ADD_DEPENDENCIES ADD_EXECUTABLE ADD_LIBRARY ADD_SUBDIRECTORY ADD_TEST AUX_SOURCE_DIRECTORY BUILD_COMMAND BUILD_NAME CMAKE_MINIMUM_REQUIRED CONFIGURE_FILE CREATE_TEST_SOURCELIST ELSE ELSEIF ENABLE_LANGUAGE ENABLE_TESTING ENDFOREACH ENDFUNCTION ENDIF ENDMACRO ENDWHILE EXEC_PROGRAM EXECUTE_PROCESS EXPORT_LIBRARY_DEPENDENCIES FILE FIND_FILE FIND_LIBRARY FIND_PACKAGE FIND_PATH FIND_PROGRAM FLTK_WRAP_UI FOREACH FUNCTION GET_CMAKE_PROPERTY GET_DIRECTORY_PROPERTY GET_FILENAME_COMPONENT GET_SOURCE_FILE_PROPERTY GET_TARGET_PROPERTY GET_TEST_PROPERTY IF INCLUDE INCLUDE_DIRECTORIES INCLUDE_EXTERNAL_MSPROJECT INCLUDE_REGULAR_EXPRESSION INSTALL INSTALL_FILES INSTALL_PROGRAMS INSTALL_TARGETS LINK_DIRECTORIES LINK_LIBRARIES LIST LOAD_CACHE LOAD_COMMAND MACRO MAKE_DIRECTORY MARK_AS_ADVANCED MATH MESSAGE OPTION OUTPUT_REQUIRED_FILES PROJECT QT_WRAP_CPP QT_WRAP_UI REMOVE REMOVE_DEFINITIONS SEPARATE_ARGUMENTS SET SET_DIRECTORY_PROPERTIES SET_SOURCE_FILES_PROPERTIES SET_TARGET_PROPERTIES SET_TESTS_PROPERTIES SITE_NAME SOURCE_GROUP STRING SUBDIR_DEPENDS SUBDIRS TARGET_LINK_LIBRARIES TRY_COMPILE TRY_RUN UNSET USE_MANGLED_MESA UTILITY_SOURCE VARIABLE_REQUIRES VTK_MAKE_INSTANTIATOR VTK_WRAP_JAVA VTK_WRAP_PYTHON VTK_WRAP_TCL WHILE WRITE_FILE \ nextgroup=cmakeArguments -syn keyword cmakeTodo - \ TODO FIXME XXX +syn keyword cmakeTodo + \ TODO FIXME XXX \ contained " Define the default highlighting. diff --git a/runtime/syntax/cobol.vim b/runtime/syntax/cobol.vim index 5315e75211..51ca3d8773 100644 --- a/runtime/syntax/cobol.vim +++ b/runtime/syntax/cobol.vim @@ -1,10 +1,10 @@ " Vim syntax file -" Language: COBOL -" Maintainer: Tim Pope <vimNOSPAM@tpope.info> +" Language: COBOL +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> " (formerly Davyd Ondrejko <vondraco@columbus.rr.com>) " (formerly Sitaram Chamarty <sitaram@diac.com> and -" James Mitchell <james_mitchell@acm.org>) -" $Id: cobol.vim,v 1.2 2007/05/05 18:23:43 vimboss Exp $ +" James Mitchell <james_mitchell@acm.org>) +" Last Change: 2015 Feb 13 " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded @@ -145,7 +145,7 @@ syn match cobolString /'[^']*\('\|$\)/ syn match cobolIndicator "\%7c[D-]" contained if exists("cobol_legacy_code") - syn region cobolCondFlow contains=ALLBUT,cobolLine start="\<\(IF\|INVALID\|END\|EOP\)\>" skip=/\('\|"\)[^"]\{-}\("\|'\|$\)/ end="\." keepend + syn region cobolCondFlow contains=ALLBUT,cobolLine,cobolBadLine start="\<\(IF\|INVALID\|END\|EOP\)\>" skip=/\('\|"\)[^"]\{-}\("\|'\|$\)/ end="\." keepend endif " many legacy sources have junk in columns 1-6: must be before others diff --git a/runtime/syntax/config.vim b/runtime/syntax/config.vim index 20ca19ced0..cd83b24352 100644 --- a/runtime/syntax/config.vim +++ b/runtime/syntax/config.vim @@ -1,11 +1,13 @@ " Vim syntax file " Language: configure.in script: M4 with sh " Maintainer: Christian Hammesr <ch@lathspell.westend.com> -" Last Change: 2008 Sep 03 +" Last Change: 2015 Jan 14 +" (patch from Yngve Inntjore Levinsen to detect AC_MSG) +" (patch from Khym Chanur to add @Spell) " Well, I actually even do not know much about m4. This explains why there " is probably very much missing here, yet ! -" But I missed a good hilighting when editing my GNU autoconf/automake +" But I missed good highlighting when editing my GNU autoconf/automake " script, so I wrote this quick and dirty patch. @@ -20,14 +22,19 @@ endif " define the config syntax syn match configdelimiter "[()\[\];,]" syn match configoperator "[=|&\*\+\<\>]" -syn match configcomment "\(dnl.*\)\|\(#.*\)" +syn match configcomment "\(dnl.*\)\|\(#.*\)" contains=configDnl,@Spell syn match configfunction "\<[A-Z_][A-Z0-9_]*\>" syn match confignumber "[-+]\=\<\d\+\(\.\d*\)\=\>" +syn keyword configDnl dnl contained syn keyword configkeyword if then else fi test for in do done syn keyword configspecial cat rm eval -syn region configstring start=+"+ skip=+\\"+ end=+"+ -syn region configstring start=+'+ skip=+\\'+ end=+'+ -syn region configstring start=+`+ skip=+\\'+ end=+`+ + +" This shortens the script, see syn-ext-match.. +syn region configstring start=+\z(["'`]\)+ skip=+\\\z1+ end=+\z1+ contains=@Spell + +" Anything inside AC_MSG_TYPE([...]) and AC_MSG_TYPE(...) is a string. +syn region configstring matchgroup=configfunction start="AC_MSG_[A-Z]*\ze(\[" matchgroup=configdelimiter end="\])" contains=configdelimiter,@Spell +syn region configstring matchgroup=configfunction start="AC_MSG_[A-Z]*\ze([^[]" matchgroup=configdelimiter end=")" contains=configdelimiter,@Spell " Define the default highlighting. " For version 5.7 and earlier: only when not done already @@ -43,6 +50,7 @@ if version >= 508 || !exists("did_config_syntax_inits") HiLink configdelimiter Delimiter HiLink configoperator Operator HiLink configcomment Comment + HiLink configDnl Comment HiLink configfunction Function HiLink confignumber Number HiLink configkeyword Keyword diff --git a/runtime/syntax/cpp.vim b/runtime/syntax/cpp.vim index 15f0cbb4ff..c7a68388be 100644 --- a/runtime/syntax/cpp.vim +++ b/runtime/syntax/cpp.vim @@ -1,8 +1,8 @@ " Vim syntax file " Language: C++ -" Current Maintainer: vim-jp (https://github.com/vim-jp/cpp-vim) +" Current Maintainer: vim-jp (https://github.com/vim-jp/vim-cpp) " Previous Maintainer: Ken Shan <ccshan@post.harvard.edu> -" Last Change: 2014 May 14 +" Last Change: 2015 Nov 10 " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded @@ -23,7 +23,8 @@ endif " C++ extensions syn keyword cppStatement new delete this friend using syn keyword cppAccess public protected private -syn keyword cppType inline virtual explicit export bool wchar_t +syn keyword cppModifier inline virtual explicit export +syn keyword cppType bool wchar_t syn keyword cppExceptions throw try catch syn keyword cppOperator operator typeid syn keyword cppOperator and bitor or xor compl bitand and_eq or_eq xor_eq not not_eq @@ -32,14 +33,27 @@ syn match cppCast "\<\(const\|static\|dynamic\|reinterpret\)_cast\s*$" syn keyword cppStorageClass mutable syn keyword cppStructure class typename template namespace syn keyword cppBoolean true false +syn keyword cppConstant __cplusplus " C++ 11 extensions if !exists("cpp_no_cpp11") - syn keyword cppType override final + syn keyword cppModifier override final + syn keyword cppType nullptr_t syn keyword cppExceptions noexcept - syn keyword cppStorageClass constexpr decltype + syn keyword cppStorageClass constexpr decltype thread_local syn keyword cppConstant nullptr - syn region cppRawString matchgroup=cppRawDelimiter start=+\%(u8\|[uLU]\)\=R"\z([[:alnum:]_{}[\]#<>%:;.?*\+\-/\^&|~!=,"']\{,16}\)(+ end=+)\z1"+ contains=@Spell + syn keyword cppConstant ATOMIC_FLAG_INIT ATOMIC_VAR_INIT + syn keyword cppConstant ATOMIC_BOOL_LOCK_FREE ATOMIC_CHAR_LOCK_FREE + syn keyword cppConstant ATOMIC_CHAR16_T_LOCK_FREE ATOMIC_CHAR32_T_LOCK_FREE + syn keyword cppConstant ATOMIC_WCHAR_T_LOCK_FREE ATOMIC_SHORT_LOCK_FREE + syn keyword cppConstant ATOMIC_INT_LOCK_FREE ATOMIC_LONG_LOCK_FREE + syn keyword cppConstant ATOMIC_LLONG_LOCK_FREE ATOMIC_POINTER_LOCK_FREE + syn region cppRawString matchgroup=cppRawStringDelimiter start=+\%(u8\|[uLU]\)\=R"\z([[:alnum:]_{}[\]#<>%:;.?*\+\-/\^&|~!=,"']\{,16}\)(+ end=+)\z1"+ contains=@Spell +endif + +" C++ 14 extensions +if !exists("cpp_no_cpp14") + syn match cppNumber display "\<0b[01]\+\(u\=l\{0,2}\|ll\=u\)\>" endif " The minimum and maximum operators in GNU C++ @@ -58,6 +72,7 @@ if version >= 508 || !exists("did_cpp_syntax_inits") HiLink cppExceptions Exception HiLink cppOperator Operator HiLink cppStatement Statement + HiLink cppModifier Type HiLink cppType Type HiLink cppStorageClass StorageClass HiLink cppStructure Structure diff --git a/runtime/syntax/crontab.vim b/runtime/syntax/crontab.vim index 36d75c4d13..fd1c30023f 100644 --- a/runtime/syntax/crontab.vim +++ b/runtime/syntax/crontab.vim @@ -5,7 +5,7 @@ " License: This file can be redistribued and/or modified under the same terms " as Vim itself. " Filenames: /tmp/crontab.* used by "crontab -e" -" Last Change: 2012-05-16 +" Last Change: 2015-01-20 " " crontab line format: " Minutes Hours Days Months Days_of_Week Commands # comments @@ -13,11 +13,17 @@ " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded if version < 600 - syntax clear + syntax clear elseif exists("b:current_syntax") - finish + finish endif +syntax match crontabNick "^\s*@\(reboot\|yearly\|annually\|monthly\|weekly\|daily\|midnight\|hourly\)\>" nextgroup=crontabCmd skipwhite + +syntax match crontabVar "^\s*\k\w*\s*="me=e-1 + +syntax case ignore + syntax match crontabMin "^\s*[-0-9/,.*]\+" nextgroup=crontabHr skipwhite syntax match crontabHr "\s[-0-9/,.*]\+" nextgroup=crontabDay skipwhite contained syntax match crontabDay "\s[-0-9/,.*]\+" nextgroup=crontabMnth skipwhite contained @@ -32,45 +38,41 @@ syntax region crontabCmd start="\S" end="$" skipwhite contained keepend contains syntax match crontabCmnt "^\s*#.*" contains=@Spell syntax match crontabPercent "[^\\]%.*"lc=1 contained -syntax match crontabNick "^\s*@\(reboot\|yearly\|annually\|monthly\|weekly\|daily\|midnight\|hourly\)\>" nextgroup=crontabCmd skipwhite - -syntax match crontabVar "^\s*\k\w*\s*="me=e-1 - " Define the default highlighting. " For version 5.7 and earlier: only when not done already " For version 5.8 and later: only when an item doesn't have highlighting yet if version >= 508 || !exists("did_crontab_syn_inits") - if version < 508 - let did_crontab_syn_inits = 1 - command -nargs=+ HiLink hi link <args> - else - command -nargs=+ HiLink hi def link <args> - endif - - HiLink crontabMin Number - HiLink crontabHr PreProc - HiLink crontabDay Type - - HiLink crontabMnth Number - HiLink crontabMnth12 Number - HiLink crontabMnthS Number - HiLink crontabMnthN Number - - HiLink crontabDow PreProc - HiLink crontabDow7 PreProc - HiLink crontabDowS PreProc - HiLink crontabDowN PreProc - - HiLink crontabNick Special - HiLink crontabVar Identifier - HiLink crontabPercent Special + if version < 508 + let did_crontab_syn_inits = 1 + command -nargs=+ HiLink hi link <args> + else + command -nargs=+ HiLink hi def link <args> + endif + + HiLink crontabMin Number + HiLink crontabHr PreProc + HiLink crontabDay Type + + HiLink crontabMnth Number + HiLink crontabMnth12 Number + HiLink crontabMnthS Number + HiLink crontabMnthN Number + + HiLink crontabDow PreProc + HiLink crontabDow7 PreProc + HiLink crontabDowS PreProc + HiLink crontabDowN PreProc + + HiLink crontabNick Special + HiLink crontabVar Identifier + HiLink crontabPercent Special " comment out next line for to suppress unix commands coloring. - HiLink crontabCmd Statement + HiLink crontabCmd Statement - HiLink crontabCmnt Comment + HiLink crontabCmnt Comment - delcommand HiLink + delcommand HiLink endif let b:current_syntax = "crontab" diff --git a/runtime/syntax/csh.vim b/runtime/syntax/csh.vim index 39603fa158..9dc2c4ef56 100644 --- a/runtime/syntax/csh.vim +++ b/runtime/syntax/csh.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: C-shell (csh) -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Version: 10 -" Last Change: Sep 11, 2006 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 11 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_CSH " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/datascript.vim b/runtime/syntax/datascript.vim index 2b4ec513b4..a983b8e34c 100644 --- a/runtime/syntax/datascript.vim +++ b/runtime/syntax/datascript.vim @@ -1,11 +1,12 @@ " Vim syntax file -" Language: Datascript +" Language: DataScript " Maintainer: Dominique Pelle <dominique.pelle@gmail.com> -" Last Change: 2014 Feb 26 +" Last Change: 2015 Jul 30 " " DataScript is a formal language for modelling binary datatypes, " bitstreams or file formats. For more information, see: -" http://datascript.berlios.de/DataScriptLanguageOverview.html +" +" http://dstools.sourceforge.net/DataScriptLanguageOverview.html if version < 600 syntax clear @@ -19,6 +20,8 @@ syn keyword dsPackage import package syn keyword dsType bit bool string syn keyword dsType int int8 int16 int32 int64 syn keyword dsType uint8 uint16 uint32 uint64 +syn keyword dsType varint16 varint32 varint64 +syn keyword dsType varuint16 varuint32 varuint64 syn keyword dsType leint16 leint32 leint64 syn keyword dsType leuint16 leuint32 leuint64 syn keyword dsEndian little big @@ -32,7 +35,8 @@ syn keyword dsOperator sizeof bitsizeof lengthof is sum forall in syn keyword dsStorageClass const syn keyword dsTodo contained TODO FIXME XXX syn keyword dsSql sql sql_table sql_database sql_pragma sql_index -syn keyword dsSql sql_integer sql_metadata sql_key foreign_key +syn keyword dsSql sql_integer sql_metadata sql_key sql_virtual +syn keyword dsSql using reference_key foreign_key to " dsCommentGroup allows adding matches for special things in comments. syn cluster dsCommentGroup contains=dsTodo @@ -61,6 +65,8 @@ syn region dsComment syn region dsString \ start=+L\="+ skip=+\\\\\|\\"+ end=+"+ contains=@Spell +syn sync ccomment dsComment + " Define the default highlighting. hi def link dsType Type hi def link dsEndian StorageClass diff --git a/runtime/syntax/dcl.vim b/runtime/syntax/dcl.vim index cf62cdf202..37bf38fe11 100644 --- a/runtime/syntax/dcl.vim +++ b/runtime/syntax/dcl.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: DCL (Digital Command Language - vms) -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Sep 11, 2006 -" Version: 6 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 7 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_DCL " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/debchangelog.vim b/runtime/syntax/debchangelog.vim index ed3d024499..4f1d6d4b11 100644 --- a/runtime/syntax/debchangelog.vim +++ b/runtime/syntax/debchangelog.vim @@ -3,8 +3,8 @@ " Maintainer: Debian Vim Maintainers <pkg-vim-maintainers@lists.alioth.debian.org> " Former Maintainers: Gerfried Fuchs <alfie@ist.org> " Wichert Akkerman <wakkerma@debian.org> -" Last Change: 2015 Apr 30 -" URL: http://anonscm.debian.org/hg/pkg-vim/vim/raw-file/unstable/runtime/syntax/debchangelog.vim +" Last Change: 2015 Oct 24 +" URL: https://anonscm.debian.org/cgit/pkg-vim/vim.git/plain/runtime/syntax/debchangelog.vim " Standard syntax initialization if version < 600 @@ -23,7 +23,7 @@ let binNMU='binary-only=yes' syn match debchangelogName contained "^[[:alnum:]][[:alnum:].+-]\+ " exe 'syn match debchangelogFirstKV contained "; \('.urgency.'\|'.binNMU.'\)"' exe 'syn match debchangelogOtherKV contained ", \('.urgency.'\|'.binNMU.'\)"' -syn match debchangelogTarget contained "\v %(frozen|unstable|sid|%(testing|%(old)=stable)%(-proposed-updates|-security)=|experimental|squeeze-%(backports%(-sloppy)=|volatile|lts|security)|wheezy-%(backports%(-sloppy)=|security)|jessie%(-backports|-security)=|stretch|%(devel|lucid|precise|trusty|utopic)%(-%(security|proposed|updates|backports|commercial|partner))=)+" +syn match debchangelogTarget contained "\v %(frozen|unstable|sid|%(testing|%(old)=stable)%(-proposed-updates|-security)=|experimental|squeeze-%(backports%(-sloppy)=|volatile|lts|security)|wheezy-%(backports%(-sloppy)=|security)|jessie%(-backports|-security)=|stretch|%(devel|precise|trusty|vivid|wily|xenial)%(-%(security|proposed|updates|backports|commercial|partner))=)+" syn match debchangelogVersion contained "(.\{-})" syn match debchangelogCloses contained "closes:\_s*\(bug\)\=#\=\_s\=\d\+\(,\_s*\(bug\)\=#\=\_s\=\d\+\)*" syn match debchangelogLP contained "\clp:\s\+#\d\+\(,\s*#\d\+\)*" diff --git a/runtime/syntax/debcontrol.vim b/runtime/syntax/debcontrol.vim index 6d140d9bd9..4fb53a55ca 100644 --- a/runtime/syntax/debcontrol.vim +++ b/runtime/syntax/debcontrol.vim @@ -3,8 +3,8 @@ " Maintainer: Debian Vim Maintainers <pkg-vim-maintainers@lists.alioth.debian.org> " Former Maintainers: Gerfried Fuchs <alfie@ist.org> " Wichert Akkerman <wakkerma@debian.org> -" Last Change: 2014 Oct 08 -" URL: http://anonscm.debian.org/hg/pkg-vim/vim/raw-file/unstable/runtime/syntax/debcontrol.vim +" Last Change: 2015 Oct 24 +" URL: https://anonscm.debian.org/cgit/pkg-vim/vim.git/plain/runtime/syntax/debcontrol.vim " Standard syntax initialization if version < 600 diff --git a/runtime/syntax/debsources.vim b/runtime/syntax/debsources.vim index c8d110c77b..e0c5f4075f 100644 --- a/runtime/syntax/debsources.vim +++ b/runtime/syntax/debsources.vim @@ -2,8 +2,8 @@ " Language: Debian sources.list " Maintainer: Debian Vim Maintainers <pkg-vim-maintainers@lists.alioth.debian.org> " Former Maintainer: Matthijs Mohlmann <matthijs@cacholong.nl> -" Last Change: 2015 May 25 -" URL: http://anonscm.debian.org/hg/pkg-vim/vim/raw-file/unstable/runtime/syntax/debsources.vim +" Last Change: 2015 Oct 24 +" URL: https://anonscm.debian.org/cgit/pkg-vim/vim.git/plain/runtime/syntax/debsources.vim " Standard syntax initialization if version < 600 @@ -27,7 +27,7 @@ let s:supported = [ \ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', \ 'squeeze', 'wheezy', 'jessie', 'stretch', 'sid', 'rc-buggy', \ - \ 'precise', 'trusty', 'utopic', 'vivid', 'wily', 'devel' + \ 'precise', 'trusty', 'vivid', 'wily', 'xenial', 'devel' \ ] let s:unsupported = [ \ 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato', @@ -35,7 +35,8 @@ let s:unsupported = [ \ \ 'warty', 'hoary', 'breezy', 'dapper', 'edgy', 'feisty', \ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid', - \ 'maverick', 'natty', 'oneiric', 'quantal', 'raring', 'saucy' + \ 'maverick', 'natty', 'oneiric', 'quantal', 'raring', 'saucy', + \ 'utopic' \ ] let &cpo=s:cpo diff --git a/runtime/syntax/diff.vim b/runtime/syntax/diff.vim index ff69c5860b..885feaebdd 100644 --- a/runtime/syntax/diff.vim +++ b/runtime/syntax/diff.vim @@ -2,7 +2,7 @@ " Language: Diff (context or unified) " Maintainer: Bram Moolenaar <Bram@vim.org> " Translations by Jakson Alves de Aquino. -" Last Change: 2015 Jan 07 +" Last Change: 2015 Feb 03 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") @@ -123,13 +123,17 @@ syn match diffNoEOL "^\\ Non hai un salto de liña na fin da liña" syn match diffCommon "^Subdirectorios comúns: .* e .*" " he -syn match diffOnly "^.*-ב קר ××¦×ž× .*" -syn match diffIdentical "^××™×”×– ×× ×™×” .*-ו .* ×יצבקה$" -syn match diffDiffer "^הזמ ×”×– ××™× ×•×© `.*'-ו `.*' ×יצבקה$" -syn match diffBDiffer "^הזמ ×”×– ××™× ×•×© `.*'-ו `.*' ××™×™×¨× ×™×‘ ×יצבק$" -syn match diffIsA "^.* .*-ל .* .* תוושהל ×Ÿ×ª×™× ×ל$" -syn match diffNoEOL "^\\ ץבוקה ףוסב השד.-הרוש ות רס." -syn match diffCommon "^.*-ו .* :תוהז תויקית-תת$" +" ^.* are expansive patterns for long lines, so disabled unless we can match +" some specific hebrew chars +if search('\%u05d5\|\%u05d1', 'nw', '', 100) + syn match diffOnly "^.*-ב קר ××¦×ž× .*" + syn match diffIdentical "^××™×”×– ×× ×™×” .*-ו .* ×יצבקה$" + syn match diffDiffer "^הזמ ×”×– ××™× ×•×© `.*'-ו `.*' ×יצבקה$" + syn match diffBDiffer "^הזמ ×”×– ××™× ×•×© `.*'-ו `.*' ××™×™×¨× ×™×‘ ×יצבק$" + syn match diffIsA "^.* .*-ל .* .* תוושהל ×Ÿ×ª×™× ×ל$" + syn match diffNoEOL "^\\ ץבוקה ףוסב השד.-הרוש ות רס." + syn match diffCommon "^.*-ו .* :תוהז תויקית-תת$" +endif " hr syn match diffOnly "^Samo u .*" diff --git a/runtime/syntax/dircolors.vim b/runtime/syntax/dircolors.vim index c94d720644..1b598c39b5 100644 --- a/runtime/syntax/dircolors.vim +++ b/runtime/syntax/dircolors.vim @@ -44,24 +44,24 @@ highlight default link dircolorsExtension Identifier highlight default link dircolorsEscape Special function! s:set_guicolors() abort - let s:guicolors = {} - - let s:guicolors[0] = "Black" - let s:guicolors[1] = "DarkRed" - let s:guicolors[2] = "DarkGreen" - let s:guicolors[3] = "DarkYellow" - let s:guicolors[4] = "DarkBlue" - let s:guicolors[5] = "DarkMagenta" - let s:guicolors[6] = "DarkCyan" - let s:guicolors[7] = "Gray" - let s:guicolors[8] = "DarkGray" - let s:guicolors[9] = "Red" - let s:guicolors[10] = "Green" - let s:guicolors[11] = "Yellow" - let s:guicolors[12] = "Blue" - let s:guicolors[13] = "Magenta" - let s:guicolors[14] = "Cyan" - let s:guicolors[15] = "White" + let s:termguicolors = {} + + let s:termguicolors[0] = "Black" + let s:termguicolors[1] = "DarkRed" + let s:termguicolors[2] = "DarkGreen" + let s:termguicolors[3] = "DarkYellow" + let s:termguicolors[4] = "DarkBlue" + let s:termguicolors[5] = "DarkMagenta" + let s:termguicolors[6] = "DarkCyan" + let s:termguicolors[7] = "Gray" + let s:termguicolors[8] = "DarkGray" + let s:termguicolors[9] = "Red" + let s:termguicolors[10] = "Green" + let s:termguicolors[11] = "Yellow" + let s:termguicolors[12] = "Blue" + let s:termguicolors[13] = "Magenta" + let s:termguicolors[14] = "Cyan" + let s:termguicolors[15] = "White" let xterm_palette = ["00", "5f", "87", "af", "d7", "ff"] @@ -70,7 +70,7 @@ function! s:set_guicolors() abort for r in xterm_palette for g in xterm_palette for b in xterm_palette - let s:guicolors[cur_col] = '#' . r . g . b + let s:termguicolors[cur_col] = '#' . r . g . b let cur_col += 1 endfor endfor @@ -78,14 +78,14 @@ function! s:set_guicolors() abort for i in range(24) let g = i * 0xa + 8 - let s:guicolors[i + 232] = '#' . g . g . g + let s:termguicolors[i + 232] = '#' . g . g . g endfor endfunction function! s:get_hi_str(color, place) abort if a:color >= 0 && a:color <= 255 if has('gui_running') - return ' gui' . a:place . '=' . s:guicolors[a:color] + return ' gui' . a:place . '=' . s:termguicolors[a:color] elseif a:color <= 7 || &t_Co == 256 || &t_Co == 88 return ' cterm' . a:place . '=' . a:color endif diff --git a/runtime/syntax/dnsmasq.vim b/runtime/syntax/dnsmasq.vim index 2a31aed77f..9fa32077b6 100644 --- a/runtime/syntax/dnsmasq.vim +++ b/runtime/syntax/dnsmasq.vim @@ -4,8 +4,8 @@ " :3s+-foo++g " Description: highlight dnsmasq configuration files " File: runtime/syntax/dnsmasq.vim -" Version: 2.70 -" Last Change: 2014 Apr 30 +" Version: 2.76 +" Last Change: 2015 Sep 27 " Modeline: vim: ts=8:sw=2:sts=2: " " License: VIM License @@ -131,10 +131,12 @@ syn match DnsmasqKeyword "^\s*dhcp-sequential-ip\>" syn match DnsmasqKeyword "^\s*dhcp-subscrid\>" syn match DnsmasqKeyword "^\s*dhcp-userclass\>" syn match DnsmasqKeyword "^\s*dhcp-vendorclass\>" +syn match DnsmasqKeyword "^\s*dhcp-hostsdir\>" syn match DnsmasqKeyword "^\s*dns-rr\>" syn match DnsmasqKeyword "^\s*dnssec\>" syn match DnsmasqKeyword "^\s*dnssec-check-unsigned\>" syn match DnsmasqKeyword "^\s*dnssec-no-timecheck\>" +syn match DnsmasqKeyword "^\s*dnssec-timestamp\>" syn match DnsmasqKeyword "^\s*dns-forward-max\>" syn match DnsmasqKeyword "^\s*domain\>" syn match DnsmasqKeyword "^\s*domain-needed\>" @@ -150,6 +152,7 @@ syn match DnsmasqKeyword "^\s*host-record\>" syn match DnsmasqKeyword "^\s*interface\>" syn match DnsmasqKeyword "^\s*interface-name\>" syn match DnsmasqKeyword "^\s*ipset\>" +syn match DnsmasqKeyword "^\s*ignore-address\>" syn match DnsmasqKeyword "^\s*keep-in-foreground\>" syn match DnsmasqKeyword "^\s*leasefile-ro\>" syn match DnsmasqKeyword "^\s*listen-address\>" @@ -164,6 +167,7 @@ syn match DnsmasqKeyword "^\s*log-facility\>" syn match DnsmasqKeyword "^\s*log-queries\>" syn match DnsmasqKeyword "^\s*max-ttl\>" syn match DnsmasqKeyword "^\s*max-cache-ttl\>" +syn match DnsmasqKeyword "^\s*min-cache-ttl\>" syn match DnsmasqKeyword "^\s*min-port\>" syn match DnsmasqKeyword "^\s*mx-host\>" syn match DnsmasqKeyword "^\s*mx-target\>" @@ -204,6 +208,7 @@ syn match DnsmasqKeyword "^\s*test\>" syn match DnsmasqKeyword "^\s*tftp-max\>" syn match DnsmasqKeyword "^\s*tftp-lowercase\>" syn match DnsmasqKeyword "^\s*tftp-no-blocksize\>" +syn match DnsmasqKeyword "^\s*tftp-no-fail\>" syn match DnsmasqKeyword "^\s*tftp-port-range\>" syn match DnsmasqKeyword "^\s*tftp-root\>" syn match DnsmasqKeyword "^\s*tftp-secure\>" diff --git a/runtime/syntax/elmfilt.vim b/runtime/syntax/elmfilt.vim index 0afa1694cf..e7d5df22aa 100644 --- a/runtime/syntax/elmfilt.vim +++ b/runtime/syntax/elmfilt.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: Elm Filter rules -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Sep 11, 2006 -" Version: 5 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 6 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_ELMFILT " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/exports.vim b/runtime/syntax/exports.vim index 40b4b8806d..f695dc8d21 100644 --- a/runtime/syntax/exports.vim +++ b/runtime/syntax/exports.vim @@ -1,10 +1,10 @@ " Vim syntax file " Language: exports -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Sep 06, 2005 -" Version: 4 +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 5 " Notes: This file includes both SysV and BSD 'isms -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_EXPORTS " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/fortran.vim b/runtime/syntax/fortran.vim index df33793bb2..26d063524e 100644 --- a/runtime/syntax/fortran.vim +++ b/runtime/syntax/fortran.vim @@ -1,15 +1,15 @@ " Vim syntax file -" Language: Fortran 2008 (and earlier versions: 2003, 95, 90, and 77) -" Version: 0.94 -" Last Change: 2012 June 18 -" Maintainer: Ajit J. Thakkar (ajit AT unb.ca); <http://www.unb.ca/chem/ajit/> +" Language: Fortran 2008 (and older: Fortran 2003, 95, 90, and 77) +" Version: 0.96 +" Last Change: 2015 Nov. 30 +" Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www2.unb.ca/~ajit/> " Usage: For instructions, do :help fortran-syntax from Vim " Credits: " Version 0.1 was based on the fortran 77 syntax file by Mario Eusebio and -" Preben Guldberg. Useful suggestions were made by: Andrej Panjkov, +" Preben Guldberg. Useful suggestions and contributions were made by: Andrej Panjkov, " Bram Moolenaar, Thomas Olsen, Michael Sternberg, Christian Reile, " Walter Dieudonné, Alexander Wagner, Roman Bertle, Charles Rendleman, -" Andrew Griffiths, Joe Krahn, and Hendrik Merx. +" Andrew Griffiths, Joe Krahn, Hendrik Merx, and Matt Thompson. if exists("b:current_syntax") finish @@ -19,8 +19,8 @@ let s:cpo_save = &cpo set cpo&vim " Choose fortran_dialect using the priority: -" source file directive > buffer-local value > global value > default -" try using directive in first three lines of file +" source file directive > buffer-local value > global value > file extension +" first try using directive in first three lines of file let b:fortran_retype = getline(1)." ".getline(2)." ".getline(3) if b:fortran_retype =~? '\<fortran_dialect\s*=\s*F\>' let b:fortran_dialect = "F" @@ -51,6 +51,12 @@ if !exists("b:fortran_fixed_source") elseif exists("fortran_fixed_source") " User guarantees fixed source form for all fortran files let b:fortran_fixed_source = 1 + elseif expand("%:e") ==? "f\<90\|95\|03\|08\>" + " Free-form file extension defaults as in Intel ifort, gcc(gfortran), NAG, Pathscale, and Cray compilers + let b:fortran_fixed_source = 0 + elseif expand("%:e") ==? "f\|f77\|for" + " Fixed-form file extension defaults + let b:fortran_fixed_source = 1 else " Modern fortran still allows both free and fixed source form. " Assume fixed source form unless signs of free source form @@ -67,8 +73,8 @@ if !exists("b:fortran_fixed_source") while s:ln <= s:lmax let s:test = strpart(getline(s:ln),0,5) if s:test !~ '^[Cc*]' && s:test !~ '^ *[!#]' && s:test =~ '[^ 0-9\t]' && s:test !~ '^[ 0-9]*\t' - let b:fortran_fixed_source = 0 - break + let b:fortran_fixed_source = 0 + break endif let s:ln = s:ln + 1 endwhile @@ -298,6 +304,41 @@ if b:fortran_dialect == "f08" syn keyword fortranIntrinsic bge bgt ble blt dshiftl dshiftr findloc iall iany iparity image_index lcobound ucobound maskl maskr num_images parity popcnt poppar shifta shiftl shiftr this_image syn keyword fortranIO newunit syn keyword fortranType contiguous + +" CUDA fortran + syn match fortranTypeCUDA "\<attributes\>" + syn keyword fortranTypeCUDA host global device value + syn keyword fortranTypeCUDA shared constant pinned texture + syn keyword fortranTypeCUDA dim1 dim2 dim3 dim4 + syn keyword fortranTypeCUDA cudadeviceprop cuda_count_kind cuda_stream_kind + syn keyword fortranTypeCUDA cudaEvent cudaFuncAttributes cudaArrayPtr + syn keyword fortranTypeCUDA cudaSymbol cudaChannelFormatDesc cudaPitchedPtr + syn keyword fortranTypeCUDA cudaExtent cudaMemcpy3DParms + syn keyword fortranTypeCUDA cudaFuncCachePreferNone cudaFuncCachePreferShared + syn keyword fortranTypeCUDA cudaFuncCachePreferL1 cudaLimitStackSize + syn keyword fortranTypeCUDA cudaLimitPrintfSize cudaLimitMallocHeapSize + syn keyword fortranTypeCUDA cudaSharedMemBankSizeDefault cudaSharedMemBankSizeFourByte cudaSharedMemBankSizeEightByte + syn keyword fortranTypeCUDA cudaEventDefault cudaEventBlockingSync cudaEventDisableTiming + syn keyword fortranTypeCUDA cudaMemcpyHostToDevice cudaMemcpyDeviceToHost + syn keyword fortranTypeCUDA cudaMemcpyDeviceToDevice + syn keyword fortranTypeCUDA cudaErrorNotReady cudaSuccess cudaErrorInvalidValue + syn keyword fortranTypeCUDA c_devptr + + syn match fortranStringCUDA "blockidx%[xyz]" + syn match fortranStringCUDA "blockdim%[xyz]" + syn match fortranStringCUDA "griddim%[xyz]" + syn match fortranStringCUDA "threadidx%[xyz]" + + syn keyword fortranIntrinsicCUDA warpsize syncthreads syncthreads_and syncthreads_count syncthreads_or threadfence threadfence_block threadfence_system gpu_time allthreads anythread ballot + syn keyword fortranIntrinsicCUDA atomicadd atomicsub atomicmax atomicmin atomicand atomicor atomicxor atomicexch atomicinc atomicdec atomiccas sizeof __shfl __shfl_up __shfl_down __shfl_xor + syn keyword fortranIntrinsicCUDA cudaChooseDevice cudaDeviceGetCacheConfig cudaDeviceGetLimit cudaDeviceGetSharedMemConfig cudaDeviceReset cudaDeviceSetCacheConfig cudaDeviceSetLimit cudaDeviceSetSharedMemConfig cudaDeviceSynchronize cudaGetDevice cudaGetDeviceCount cudaGetDeviceProperties cudaSetDevice cudaSetDeviceFlags cudaSetValidDevices + syn keyword fortranIntrinsicCUDA cudaThreadExit cudaThreadSynchronize cudaGetLastError cudaGetErrorString cudaPeekAtLastError cudaStreamCreate cudaStreamDestroy cudaStreamQuery cudaStreamSynchronize cudaStreamWaitEvent cudaEventCreate cudaEventCreateWithFlags cudaEventDestroy cudaEventElapsedTime cudaEventQuery cudaEventRecord cudaEventSynchronize + syn keyword fortranIntrinsicCUDA cudaFuncGetAttributes cudaFuncSetCacheConfig cudaFuncSetSharedMemConfig cudaSetDoubleForDevice cudaSetDoubleForHost cudaFree cudaFreeArray cudaFreeHost cudaGetSymbolAddress cudaGetSymbolSize + syn keyword fortranIntrinsicCUDA cudaHostAlloc cudaHostGetDevicePointer cudaHostGetFlags cudaHostRegister cudaHostUnregister cudaMalloc cudaMallocArray cudaMallocHost cudaMallocPitch cudaMalloc3D cudaMalloc3DArray + syn keyword fortranIntrinsicCUDA cudaMemcpy cudaMemcpyArraytoArray cudaMemcpyAsync cudaMemcpyFromArray cudaMemcpyFromSymbol cudaMemcpyFromSymbolAsync cudaMemcpyPeer cudaMemcpyPeerAsync cudaMemcpyToArray cudaMemcpyToSymbol cudaMemcpyToSymbolAsync cudaMemcpy2D cudaMemcpy2DArrayToArray cudaMemcpy2DAsync cudaMemcpy2DFromArray cudaMemcpy2DToArray cudaMemcpy3D cudaMemcpy3DAsync + syn keyword fortranIntrinsicCUDA cudaMemGetInfo cudaMemset cudaMemset2D cudaMemset3D cudaDeviceCanAccessPeer cudaDeviceDisablePeerAccess cudaDeviceEnablePeerAccess cudaPointerGetAttributes cudaDriverGetVersion cudaRuntimeGetVersion + + syn region none matchgroup=fortranType start="<<<" end=">>>" contains=ALLBUT,none endif syn cluster fortranCommentGroup contains=fortranTodo @@ -453,6 +494,11 @@ else hi! def link fortranConditionalR fortranConditional endif +" CUDA +hi def link fortranIntrinsicCUDA fortranIntrinsic +hi def link fortranTypeCUDA fortranType +hi def link fortranStringCUDA fortranString + hi def link fortranFormatSpec Identifier hi def link fortranFloat Float hi def link fortranPreCondit PreCondit diff --git a/runtime/syntax/gnuplot.vim b/runtime/syntax/gnuplot.vim index 03d813c99f..d85932d401 100644 --- a/runtime/syntax/gnuplot.vim +++ b/runtime/syntax/gnuplot.vim @@ -1,8 +1,9 @@ " Vim syntax file " Language: gnuplot 4.7.0 -" Maintainer: Andrew Rasmussen andyras@users.sourceforge.net +" Maintainer: Josh Wainwright <wainwright DOT ja AT gmail DOT com> +" Last Maintainer: Andrew Rasmussen andyras@users.sourceforge.net " Original Maintainer: John Hoelzel johnh51@users.sourceforge.net -" Last Change: 2014-02-24 +" Last Change: 2015-08-25 " Filenames: *.gnu *.plt *.gpi *.gih *.gp *.gnuplot scripts: #!*gnuplot " URL: http://www.vim.org/scripts/script.php?script_id=4873 " Original URL: http://johnh51.get.to/vim/syntax/gnuplot.vim @@ -364,18 +365,18 @@ syn keyword gnuplotKeyword samples " set size syn keyword gnuplotKeyword size square nosquare ratio noratio " set style -syn keyword gnuplotKeyword style function data noborder rectangle arrow -syn keyword gnuplotKeyword default nohead head heads size filled empty -syn keyword gnuplotKeyword nofilled front back boxplot range fraction -syn keyword gnuplotKeyword outliers nooutliers pointtype candlesticks -syn keyword gnuplotKeyword separation labels off auto x x2 sorted unsorted -syn keyword gnuplotKeyword fill empty transparent solid pattern border -syn keyword gnuplotKeyword increment userstyles financebars line default -syn keyword gnuplotKeyword linetype lt linecolor lc linewidth lw pointtype -syn keyword gnuplotKeyword pt pointsize ps pointinterval pi palette circle -syn keyword gnuplotKeyword radius graph screen wedge nowedge ellipse size -syn keyword gnuplotKeyword units xx xy yy histogram line textbox opaque -syn keyword gnuplotKeyword border noborder +syn keyword gnuplotKeyword style arrow auto back border boxplot +syn keyword gnuplotKeyword candlesticks circle clustered columnstacked data +syn keyword gnuplotKeyword default ellipse empty fill[ed] financebars +syn keyword gnuplotKeyword fraction front function gap graph head[s] +syn keyword gnuplotKeyword histogram increment labels lc line linecolor +syn keyword gnuplotKeyword linetype linewidth lt lw noborder nofilled +syn keyword gnuplotKeyword nohead nooutliers nowedge off opaque outliers +syn keyword gnuplotKeyword palette pattern pi pointinterval pointsize +syn keyword gnuplotKeyword pointtype ps pt radius range rectangle +syn keyword gnuplotKeyword rowstacked screen separation size solid sorted +syn keyword gnuplotKeyword textbox transparent units unsorted userstyles +syn keyword gnuplotKeyword wedge x x2 xx xy yy " set surface syn keyword gnuplotKeyword surface implicit explicit " set table @@ -496,8 +497,8 @@ syn keyword gnuplotTodo contained TODO FIXME XXX syn keyword gnuplotStatement cd call clear evaluate exit fit help history syn keyword gnuplotStatement load lower pause plot p print pwd quit raise syn keyword gnuplotStatement refresh replot rep reread reset save set show -syn keyword gnuplotStatement shell splot spstats system test undefine unset -syn keyword gnuplotStatement update +syn keyword gnuplotStatement shell splot spstats stats system test undefine +syn keyword gnuplotStatement unset update " ---- Define the default highlighting ---- " " For version 5.7 and earlier: only when not done already diff --git a/runtime/syntax/groovy.vim b/runtime/syntax/groovy.vim index c745960bd5..42fcf4abac 100644 --- a/runtime/syntax/groovy.vim +++ b/runtime/syntax/groovy.vim @@ -2,9 +2,9 @@ " Language: Groovy " Original Author: Alessio Pace <billy.corgan@tiscali.it> " Maintainer: Tobias Rapp <yahuxo@gmx.de> -" Version: 0.1.13 +" Version: 0.1.14 " URL: http://www.vim.org/scripts/script.php?script_id=945 -" Last Change: 2013 Apr 24 +" Last Change: 2015 Apr 21 " THE ORIGINAL AUTHOR'S NOTES: " @@ -220,7 +220,6 @@ syn region groovyComment start="/\*" end="\*/" contains=@groovyCommen syn match groovyCommentStar contained "^\s*\*[^/]"me=e-1 syn match groovyCommentStar contained "^\s*\*$" syn match groovyLineComment "//.*" contains=@groovyCommentSpecial2,groovyTodo,@Spell -syn match groovyLineComment "#.*" contains=@groovyCommentSpecial2,groovyTodo,@Spell GroovyHiLink groovyCommentString groovyString GroovyHiLink groovyComment2String groovyString GroovyHiLink groovyCommentCharacter groovyCharacter diff --git a/runtime/syntax/hex.vim b/runtime/syntax/hex.vim index 40c655395f..40f7f0dd10 100644 --- a/runtime/syntax/hex.vim +++ b/runtime/syntax/hex.vim @@ -1,7 +1,29 @@ " Vim syntax file -" Language: Intel hex MCS51 -" Maintainer: Sams Ricahrd <sams@ping.at> -" Last Change: 2003 Apr 25 +" Language: Intel HEX +" Maintainer: Markus Heidelberg <markus.heidelberg@web.de> +" Previous version: Sams Ricahrd <sams@ping.at> +" Last Change: 2015 Feb 24 + +" Each record (line) is built as follows: +" +" field digits states +" +" +----------+ +" | start | 1 (':') hexRecStart +" +----------+ +" | count | 2 hexDataByteCount +" +----------+ +" | address | 4 hexNoAddress, hexDataAddress, (hexAddressFieldUnknown) +" +----------+ +" | type | 2 hexRecType, (hexRecTypeUnknown) +" +----------+ +" | data | 0..510 hexDataOdd, hexDataEven, hexExtendedAddress, hexStartAddress, (hexDataFieldUnknown, hexDataUnexpected) +" +----------+ +" | checksum | 2 hexChecksum +" +----------+ +" +" States in parentheses in the upper format description indicate that they +" should not appear in a valid file. " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded @@ -11,21 +33,39 @@ elseif exists("b:current_syntax") finish endif -syn case ignore +syn match hexRecStart "^:" + +syn match hexDataByteCount "^:[0-9a-fA-F]\{2}" contains=hexRecStart nextgroup=hexAddress + +syn match hexAddress "[0-9a-fA-F]\{4}" transparent contained nextgroup=hexRecTypeUnknown,hexRecType +" The address field groups include the record type field in the last 2 +" characters, the proper match for highlighting follows below. +syn match hexAddressFieldUnknown "^:[0-9a-fA-F]\{8}" contains=hexDataByteCount nextgroup=hexDataFieldUnknown,hexChecksum +syn match hexDataAddress "^:[0-9a-fA-F]\{6}00" contains=hexDataByteCount nextgroup=hexDataOdd,hexChecksum +syn match hexNoAddress "^:[0-9a-fA-F]\{6}01" contains=hexDataByteCount nextgroup=hexDataUnexpected,hexChecksum +syn match hexNoAddress "^:[0-9a-fA-F]\{6}0[24]" contains=hexDataByteCount nextgroup=hexExtendedAddress +syn match hexNoAddress "^:[0-9a-fA-F]\{6}0[35]" contains=hexDataByteCount nextgroup=hexStartAddress + +syn match hexRecTypeUnknown "[0-9a-fA-F]\{2}" contained +syn match hexRecType "0[0-5]" contained + +syn match hexDataFieldUnknown "[0-9a-fA-F]\{2}" contained nextgroup=hexDataFieldUnknown,hexChecksum +" alternating highlight per byte for easier reading +syn match hexDataOdd "[0-9a-fA-F]\{2}" contained nextgroup=hexDataEven,hexChecksum +syn match hexDataEven "[0-9a-fA-F]\{2}" contained nextgroup=hexDataOdd,hexChecksum +" data bytes which should not exist +syn match hexDataUnexpected "[0-9a-fA-F]\{2}" contained nextgroup=hexDataUnexpected,hexChecksum +" Data digit pair regex usage also results in only highlighting the checksum +" if the number of data characters is even. -" storage types +" special data fields +syn match hexExtendedAddress "[0-9a-fA-F]\{4}" contained nextgroup=hexDataUnexpected,hexChecksum +syn match hexStartAddress "[0-9a-fA-F]\{8}" contained nextgroup=hexDataUnexpected,hexChecksum -syn match hexChecksum "[0-9a-fA-F]\{2}$" -syn match hexAdress "^:[0-9a-fA-F]\{6}" contains=hexDataByteCount -syn match hexRecType "^:[0-9a-fA-F]\{8}" contains=hexAdress -syn match hexDataByteCount contained "^:[0-9a-fA-F]\{2}" contains=hexStart -syn match hexStart contained "^:" -syn match hexExtAdrRec "^:02000002[0-9a-fA-F]\{4}" contains=hexSpecRec -syn match hexExtLinAdrRec "^:02000004[0-9a-fA-F]\{4}" contains=hexSpecRec -syn match hexSpecRec contained "^:0[02]00000[124]" contains=hexStart -syn match hexEOF "^:00000001" contains=hexStart +syn match hexChecksum "[0-9a-fA-F]\{2}$" contained -syn case match +" Folding Data Records below an Extended Segment/Linear Address Record +syn region hexExtAdrBlock start="^:[0-9a-fA-F]\{7}[24]" skip="^:[0-9a-fA-F]\{7}0" end="^:"me=s-1 fold transparent " Define the default highlighting. " For version 5.7 and earlier: only when not done already @@ -38,16 +78,21 @@ if version >= 508 || !exists("did_hex_syntax_inits") command -nargs=+ HiLink hi def link <args> endif - " The default methods for highlighting. Can be overridden later - HiLink hexStart SpecialKey - HiLink hexDataByteCount Constant - HiLink hexAdress Comment - HiLink hexRecType WarningMsg - HiLink hexChecksum Search - HiLink hexExtAdrRec hexAdress - HiLink hexEOF hexSpecRec - HiLink hexExtLinAdrRec hexAdress - HiLink hexSpecRec DiffAdd + " The default methods for highlighting. Can be overridden later + HiLink hexRecStart hexRecType + HiLink hexDataByteCount Constant + hi def hexAddressFieldUnknown term=italic cterm=italic gui=italic + HiLink hexDataAddress Comment + HiLink hexNoAddress DiffAdd + HiLink hexRecTypeUnknown hexRecType + HiLink hexRecType WarningMsg + hi def hexDataFieldUnknown term=italic cterm=italic gui=italic + hi def hexDataOdd term=bold cterm=bold gui=bold + hi def hexDataEven term=NONE cterm=NONE gui=NONE + HiLink hexDataUnexpected Error + HiLink hexExtendedAddress hexDataAddress + HiLink hexStartAddress hexDataAddress + HiLink hexChecksum DiffChange delcommand HiLink endif diff --git a/runtime/syntax/hog.vim b/runtime/syntax/hog.vim index 7f01d7fc11..f37f7ae899 100644 --- a/runtime/syntax/hog.vim +++ b/runtime/syntax/hog.vim @@ -1,350 +1,200 @@ -" Snort syntax file -" Language: Snort Configuration File (see: http://www.snort.org) -" Maintainer: Phil Wood, cornett@arpa.net -" Last Change: $Date: 2004/06/13 17:41:17 $ -" Filenames: *.hog *.rules snort.conf vision.conf -" URL: http://home.lanl.gov/cpw/vim/syntax/hog.vim -" Snort Version: 1.8 By Martin Roesch (roesch@clark.net, www.snort.org) -" TODO include all 1.8 syntax - -" For version 5.x: Clear all syntax items +" Vim syntax file +" Language: hog (Snort.conf + .rules) +" Maintainer: Victor Roemer, <vroemer@badsec.org>. +" Last Change: 2015 Oct 24 -> Rename syntax items from Snort -> Hog +" 2012 Oct 24 -> Originalish release + if version < 600 - syntax clear + syntax clear elseif exists("b:current_syntax") -" For version 6.x: Quit when a syntax file was already loaded - finish + finish endif -syn match hogComment +\s\#[^\-:.%#=*].*$+lc=1 contains=hogTodo,hogCommentString -syn region hogCommentString contained oneline start='\S\s\+\#+'ms=s+1 end='\#' - -syn match hogJunk "\<\a\+|\s\+$" -syn match hogNumber contained "\<\d\+\>" -syn region hogText contained oneline start='\S' end=',' skipwhite -syn region hogTexts contained oneline start='\S' end=';' skipwhite - -" Environment Variables -" ===================== -"syn match hogEnvvar contained "[\!]\=\$\I\i*" -"syn match hogEnvvar contained "[\!]\=\${\I\i*}" -syn match hogEnvvar contained "\$\I\i*" -syn match hogEnvvar contained "[\!]\=\${\I\i*}" - - -" String handling lifted from vim.vim written by Dr. Charles E. Campbell, Jr. -" Try to catch strings, if nothing else matches (therefore it must precede the others!) -" vmEscapeBrace handles ["] []"] (ie. stays as string) -syn region hogEscapeBrace oneline contained transparent start="[^\\]\(\\\\\)*\[\^\=\]\=" skip="\\\\\|\\\]" end="\]"me=e-1 -syn match hogPatSep contained "\\[|()]" -syn match hogNotPatSep contained "\\\\" -syn region hogString oneline start=+[^:a-zA-Z\->!\\]"+hs=e+1 skip=+\\\\\|\\"+ end=+"\s*;+he=s-1 contains=hogEscapeBrace,hogPatSep,hogNotPatSep oneline -""syn region hogString oneline start=+[^:a-zA-Z>!\\]'+lc=1 skip=+\\\\\|\\'+ end=+'+ contains=hogEscapeBrace,vimPatSep,hogNotPatSep -"syn region hogString oneline start=+=!+lc=1 skip=+\\\\\|\\!+ end=+!+ contains=hogEscapeBrace,hogPatSep,hogNotPatSep -"syn region hogString oneline start="=+"lc=1 skip="\\\\\|\\+" end="+" contains=hogEscapeBrace,hogPatSep,hogNotPatSep -"syn region hogString oneline start="[^\\]+\s*[^a-zA-Z0-9.]"lc=1 skip="\\\\\|\\+" end="+" contains=hogEscapeBrace,hogPatSep,hogNotPatSep -"syn region hogString oneline start="\s/\s*\A"lc=1 skip="\\\\\|\\+" end="/" contains=hogEscapeBrace,hogPatSep,hogNotPatSep -"syn match hogString contained +"[^"]*\\$+ skipnl nextgroup=hogStringCont -"syn match hogStringCont contained +\(\\\\\|.\)\{-}[^\\]"+ - - -" Beginners - Patterns that involve ^ -" -syn match hogLineComment +^[ \t]*#.*$+ contains=hogTodo,hogCommentString,hogCommentTitle -syn match hogCommentTitle '#\s*\u\a*\(\s\+\u\a*\)*:'ms=s+1 contained -syn keyword hogTodo contained TODO - -" Rule keywords -syn match hogARPCOpt contained "\d\+,\*,\*" -syn match hogARPCOpt contained "\d\+,\d\+,\*" -syn match hogARPCOpt contained "\d\+,\*,\d\+" -syn match hogARPCOpt contained "\d\+,\d\+,\d" -syn match hogATAGOpt contained "session" -syn match hogATAGOpt contained "host" -syn match hogATAGOpt contained "dst" -syn match hogATAGOpt contained "src" -syn match hogATAGOpt contained "seconds" -syn match hogATAGOpt contained "packets" -syn match hogATAGOpt contained "bytes" -syn keyword hogARespOpt contained rst_snd rst_rcv rst_all skipwhite -syn keyword hogARespOpt contained icmp_net icmp_host icmp_port icmp_all skipwhite -syn keyword hogAReactOpt contained block warn msg skipwhite -syn match hogAReactOpt contained "proxy\d\+" skipwhite -syn keyword hogAFOpt contained logto content_list skipwhite -syn keyword hogAIPOptVal contained eol nop ts sec lsrr lsrre satid ssrr rr skipwhite -syn keyword hogARefGrps contained arachnids skipwhite -syn keyword hogARefGrps contained bugtraq skipwhite -syn keyword hogARefGrps contained cve skipwhite -syn keyword hogSessionVal contained printable all skipwhite -syn match hogAFlagOpt contained "[0FSRPAUfsrpau21]\+" skipwhite -syn match hogAFragOpt contained "[DRMdrm]\+" skipwhite -" -" Output syslog options -" Facilities -syn keyword hogSysFac contained LOG_AUTH LOG_AUTHPRIV LOG_DAEMON LOG_LOCAL0 -syn keyword hogSysFac contained LOG_LOCAL1 LOG_LOCAL2 LOG_LOCAL3 LOG_LOCAL4 -syn keyword hogSysFac contained LOG_LOCAL5 LOG_LOCAL6 LOG_LOCAL7 LOG_USER -" Priorities -syn keyword hogSysPri contained LOG_EMERG ALERT LOG_CRIT LOG_ERR -syn keyword hogSysPri contained LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG -" Options -syn keyword hogSysOpt contained LOG_CONS LOG_NDELAY LOG_PERROR -syn keyword hogSysOpt contained LOG_PID -" RuleTypes -syn keyword hogRuleType contained log pass alert activate dynamic - -" Output log_database arguments and parameters -" Type of database followed by , -" syn keyword hogDBSQL contained mysql postgresql unixodbc -" Parameters param=constant -" are just various constants assigned to parameter names - -" Output log_database arguments and parameters -" Type of database followed by , -syn keyword hogDBType contained alert log -syn keyword hogDBSRV contained mysql postgresql unixodbc -" Parameters param=constant -" are just various constants assigned to parameter names -syn keyword hogDBParam contained dbname host port user password sensor_name - -" Output xml arguments and parameters -" xml args -syn keyword hogXMLArg contained log alert -syn keyword hogXMLParam contained file protocol host port cert key ca server sanitize encoding detail -" -" hog rule handler '(.*)' -syn region hogAOpt contained oneline start="rpc" end=":"me=e-1 nextgroup=hogARPCOptGrp skipwhite -syn region hogARPCOptGrp contained oneline start="."hs=s+1 end=";"me=e-1 contains=hogARPCOpt skipwhite - -syn region hogAOpt contained oneline start="tag" end=":"me=e-1 nextgroup=hogATAGOptGrp skipwhite -syn region hogATAGOptGrp contained oneline start="."hs=s+1 skip="," end=";"me=e-1 contains=hogATAGOpt,hogNumber skipwhite -" -syn region hogAOpt contained oneline start="nocase\|sameip" end=";"me=e-1 skipwhite oneline keepend -" -syn region hogAOpt contained start="resp" end=":"me=e-1 nextgroup=hogARespOpts skipwhite -syn region hogARespOpts contained oneline start="." end="[,;]" contains=hogARespOpt skipwhite nextgroup=hogARespOpts -" -syn region hogAOpt contained start="react" end=":"me=e-1 nextgroup=hogAReactOpts skipwhite -syn region hogAReactOpts contained oneline start="." end="[,;]" contains=hogAReactOpt skipwhite nextgroup=hogAReactOpts - -syn region hogAOpt contained oneline start="depth\|seq\|ttl\|ack\|icmp_seq\|activates\|activated_by\|dsize\|icode\|icmp_id\|count\|itype\|tos\|id\|offset" end=":"me=e-1 nextgroup=hogANOptGrp skipwhite -syn region hogANOptGrp contained oneline start="."hs=s+1 end=";"me=e-1 contains=hogNumber skipwhite oneline keepend - -syn region hogAOpt contained oneline start="classtype" end=":"me=e-1 nextgroup=hogAFileGrp skipwhite - -syn region hogAOpt contained oneline start="regex\|msg\|content" end=":"me=e-1 nextgroup=hogAStrGrp skipwhite -"syn region hogAStrGrp contained oneline start=+:\s*"+hs=s+1 skip="\\;" end=+"\s*;+he=s-1 contains=hogString skipwhite oneline keepend -syn region hogAStrGrp contained oneline start=+:\s*"\|:"+hs=s+1 skip="\\;" end=+"\s*;+he=s-1 contains=hogString skipwhite oneline keepend - -syn region hogAOpt contained oneline start="logto\|content-list" end=":"me=e-1 nextgroup=hogAFileGrp skipwhite -syn region hogAFileGrp contained oneline start="."hs=s+1 end=";"me=e-1 contains=hogFileName skipwhite - -syn region hogAOpt contained oneline start="reference" end=":"me=e-1 nextgroup=hogARefGrp skipwhite -syn region hogARefGrp contained oneline start="."hs=s+1 end=","me=e-1 contains=hogARefGrps nextgroup=hogARefName skipwhite -syn region hogARefName contained oneline start="."hs=s+1 end=";"me=e-1 contains=hogString,hogFileName,hogNumber skipwhite - -syn region hogAOpt contained oneline start="flags" end=":"he=s-1 nextgroup=hogAFlagOpt skipwhite oneline keepend - -syn region hogAOpt contained oneline start="fragbits" end=":"he=s-1 nextgroup=hogAFlagOpt skipwhite oneline keepend - -syn region hogAOpt contained oneline start="ipopts" end=":"he=s-1 nextgroup=hogAIPOptVal skipwhite oneline keepend - -"syn region hogAOpt contained oneline start="." end=":"he=s-1 contains=hogAFOpt nextgroup=hogFileName skipwhite - -syn region hogAOpt contained oneline start="session" end=":"he=s-1 nextgroup=hogSessionVal skipwhite - -syn match nothing "$" -syn region hogRules oneline contains=nothing start='$' end="$" -syn region hogRules oneline contains=hogRule start='('ms=s+1 end=")\s*$" skipwhite -syn region hogRule contained oneline start="." skip="\\;" end=";"he=s-1 contains=hogAOpts, skipwhite keepend -"syn region hogAOpts contained oneline start="." end="[;]"he=s-1 contains=hogAOpt skipwhite -syn region hogAOpts contained oneline start="." end="[;]"me=e-1 contains=hogAOpt skipwhite - - -" ruletype command -syn keyword hogRTypeStart skipwhite ruletype nextgroup=hogRuleName skipwhite -syn region hogRuleName contained start="." end="\s" contains=hogFileName nextgroup=hogRTypeRegion -" type ruletype sub type -syn region hogRtypeRegion contained start="{" end="}" nextgroup=hogRTypeStart -syn keyword hogRTypeStart skipwhite type nextgroup=hogRuleTypes skipwhite -syn region hogRuleTypes contained start="." end="\s" contains=hogRuleType nextgroup=hogOutStart - - -" var command -syn keyword hogVarStart skipwhite var nextgroup=hogVarIdent skipwhite -syn region hogVarIdent contained start="."hs=e+1 end="\s\+"he=s-1 contains=hogEnvvar nextgroup=hogVarRegion skipwhite -syn region hogVarRegion contained oneline start="." contains=hogIPaddr,hogEnvvar,hogNumber,hogString,hogFileName end="$"he=s-1 keepend skipwhite - -" config command -syn keyword hogConfigStart config skipwhite nextgroup=hogConfigType -syn match hogConfigType contained "\<classification\>" nextgroup=hogConfigTypeRegion skipwhite -syn region hogConfigTypeRegion contained oneline start=":"ms=s+1 end="$" contains=hogNumber,hogText keepend skipwhite - - -" include command -syn keyword hogIncStart include skipwhite nextgroup=hogIncRegion -syn region hogIncRegion contained oneline start="\>" contains=hogFileName,hogEnvvar end="$" keepend - -" preprocessor command -" http_decode, minfrag, portscan[-ignorehosts] -syn keyword hogPPrStart preprocessor skipwhite nextgroup=hogPPr -syn match hogPPr contained "\<spade\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<spade-homenet\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<spade-threshlearn\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<spade-adapt\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<spade-adapt2\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<spade-adapt3\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<spade-survey\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<defrag\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<telnet_decode\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<rpc_decode\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<bo\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<stream\>" nextgroup=hogStreamRegion skipwhite -syn match hogPPr contained "\<stream2\>" nextgroup=hogStreamRegion skipwhite -syn match hogPPr contained "\<stream3\>" nextgroup=hogStreamRegion skipwhite -syn match hogPPr contained "\<http_decode\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<minfrag\>" nextgroup=hogPPrRegion skipwhite -syn match hogPPr contained "\<portscan[-ignorehosts]*\>" nextgroup=hogPPrRegion skipwhite -syn region hogPPrRegion contained oneline start="$" end="$" keepend -syn region hogPPrRegion contained oneline start=":" end="$" contains=hogNumber,hogIPaddr,hogEnvvar,hogFileName keepend -syn keyword hogStreamArgs contained timeout ports maxbytes -syn region hogStreamRegion contained oneline start=":" end="$" contains=hogStreamArgs,hogNumber - -" output command -syn keyword hogOutStart output nextgroup=hogOut skipwhite -" -" alert_syslog -syn match hogOut contained "\<alert_syslog\>" nextgroup=hogSyslogRegion skipwhite -syn region hogSyslogRegion contained start=":" end="$" contains=hogSysFac,hogSysPri,hogSysOpt,hogEnvvar oneline skipwhite keepend -" -" alert_fast (full,smb,unixsock, and tcpdump) -syn match hogOut contained "\<alert_fast\|alert_full\|alert_smb\|alert_unixsock\|log_tcpdump\>" nextgroup=hogLogFileRegion skipwhite -syn region hogLogFileRegion contained start=":" end="$" contains=hogFileName,hogEnvvar oneline skipwhite keepend -" -" database -syn match hogOut contained "\<database\>" nextgroup=hogDBTypes skipwhite -syn region hogDBTypes contained start=":" end="," contains=hogDBType,hogEnvvar nextgroup=hogDBSRVs skipwhite -syn region hogDBSRVs contained start="\s\+" end="," contains=hogDBSRV nextgroup=hogDBParams skipwhite -syn region hogDBParams contained start="." end="="me=e-1 contains=hogDBParam nextgroup=hogDBValues -syn region hogDBValues contained start="." end="\>" contains=hogNumber,hogEnvvar,hogAscii nextgroup=hogDBParams oneline skipwhite -syn match hogAscii contained "\<\a\+" -" -" log_tcpdump -syn match hogOut contained "\<log_tcpdump\>" nextgroup=hogLogRegion skipwhite -syn region hogLogRegion oneline start=":" skipwhite end="$" contains=hogEnvvar,hogFileName keepend -" -" xml -syn keyword hogXMLTrans contained http https tcp iap -syn match hogOut contained "\<xml\>" nextgroup=hogXMLRegion skipwhite -syn region hogXMLRegion contained start=":" end="," contains=hogXMLArg,hogEnvvar nextgroup=hogXMLParams skipwhite -"syn region hogXMLParams contained start="." end="="me=e-1 contains=hogXMLProto nextgroup=hogXMLProtos -"syn region hogXMLProtos contained start="." end="\>" contains=hogXMLTrans nextgroup=hogXMLParams -syn region hogXMLParams contained start="." end="="me=e-1 contains=hogXMLParam nextgroup=hogXMLValue -syn region hogXMLValue contained start="." end="\>" contains=hogNumber,hogIPaddr,hogEnvvar,hogAscii,hogFileName nextgroup=hogXMLParams oneline skipwhite keepend -" -" Filename -syn match hogFileName contained "[-./[:alnum:]_~]\+" -syn match hogFileName contained "[-./[:alnum:]_~]\+" -" IP address -syn match hogIPaddr "\<\d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}\>" -syn match hogIPaddr "\<\d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}/\d\{1,2}\>" - -syn keyword hogProto tcp TCP ICMP icmp udp UDP - -" hog alert address port pairs -" hog IPaddresses -syn match hogIPaddrAndPort contained "\<\d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}\>" skipwhite nextgroup=hogPort -syn match hogIPaddrAndPort contained "\<\d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}/\d\{1,2}\>" skipwhite nextgroup=hogPort -syn match hogIPaddrAndPort contained "\<any\>" skipwhite nextgroup=hogPort -syn match hogIPaddrAndPort contained "\$\I\i*" nextgroup=hogPort skipwhite -syn match hogIPaddrAndPort contained "\${\I\i*}" nextgroup=hogPort skipwhite -"syn match hogPort contained "[\!]\=[\:]\=\d\+L\=\>" skipwhite -syn match hogPort contained "[\:]\=\d\+\>" -syn match hogPort contained "[\!]\=\<any\>" skipwhite -syn match hogPort contained "[\!]\=\d\+L\=:\d\+L\=\>" skipwhite - -" action commands -syn keyword hog7Functions activate skipwhite nextgroup=hogActRegion -syn keyword hog7Functions dynamic skipwhite nextgroup=hogActRegion -syn keyword hogActStart alert skipwhite nextgroup=hogActRegion -syn keyword hogActStart log skipwhite nextgroup=hogActRegion -syn keyword hogActStart pass skipwhite nextgroup=hogActRegion - -syn region hogActRegion contained oneline start="tcp\|TCP\|udp\|UDP\|icmp\|ICMP" end="\s\+"me=s-1 nextgroup=hogActSource oneline keepend skipwhite -syn region hogActSource contained oneline contains=hogIPaddrAndPort start="\s\+"ms=e+1 end="->\|<>"me=e-2 oneline keepend skipwhite nextgroup=hogActDest -syn region hogActDest contained oneline contains=hogIPaddrAndPort start="->\|<>" end="$" oneline keepend -syn region hogActDest contained oneline contains=hogIPaddrAndPort start="->\|<>" end="("me=e-1 oneline keepend skipwhite nextgroup=hogRules - - -" ==================== -if version >= 508 || !exists("did_hog_syn_inits") - if version < 508 - let did_hog_syn_inits = 1 - command -nargs=+ HiLink hi link <args> - else - command -nargs=+ HiLink hi def link <args> - endif -" The default methods for highlighting. Can be overridden later - HiLink hogComment Comment - HiLink hogLineComment Comment - HiLink hogAscii Constant - HiLink hogCommentString Constant - HiLink hogFileName Constant - HiLink hogIPaddr Constant - HiLink hogNotPatSep Constant - HiLink hogNumber Constant - HiLink hogText Constant - HiLink hogString Constant - HiLink hogSysFac Constant - HiLink hogSysOpt Constant - HiLink hogSysPri Constant -" HiLink hogAStrGrp Error - HiLink hogJunk Error - HiLink hogEnvvar Identifier - HiLink hogIPaddrAndPort Identifier - HiLink hogVarIdent Identifier - HiLink hogATAGOpt PreProc - HiLink hogAIPOptVal PreProc - HiLink hogARespOpt PreProc - HiLink hogAReactOpt PreProc - HiLink hogAFlagOpt PreProc - HiLink hogAFragOpt PreProc - HiLink hogCommentTitle PreProc - HiLink hogDBType PreProc - HiLink hogDBSRV PreProc - HiLink hogPort PreProc - HiLink hogARefGrps PreProc - HiLink hogSessionVal PreProc - HiLink hogXMLArg PreProc - HiLink hogARPCOpt PreProc - HiLink hogPatSep Special - HiLink hog7Functions Statement - HiLink hogActStart Statement - HiLink hogIncStart Statement - HiLink hogConfigStart Statement - HiLink hogOutStart Statement - HiLink hogPPrStart Statement - HiLink hogVarStart Statement - HiLink hogRTypeStart Statement - HiLink hogTodo Todo - HiLink hogRuleType Type - HiLink hogAFOpt Type - HiLink hogANoVal Type - HiLink hogAStrOpt Type - HiLink hogANOpt Type - HiLink hogAOpt Type - HiLink hogDBParam Type - HiLink hogStreamArgs Type - HiLink hogOut Type - HiLink hogPPr Type - HiLink hogConfigType Type - HiLink hogActRegion Type - HiLink hogProto Type - HiLink hogXMLParam Type - HiLink resp Todo - HiLink cLabel Label - delcommand HiLink +setlocal iskeyword-=: +setlocal iskeyword+=- +syn case ignore + +" Hog ruletype crap +syn keyword HogRuleType ruletype nextgroup=HogRuleTypeName skipwhite +syn match HogRuleTypeName "[[:alnum:]_]\+" contained nextgroup=HogRuleTypeBody skipwhite +syn region HogRuleTypeBody start="{" end="}" contained contains=HogRuleTypeType,HogOutput fold +syn keyword HogRuleTypeType type contained + +" Hog Configurables +syn keyword HogPreproc preprocessor nextgroup=HogConfigName skipwhite +syn keyword HogConfig config nextgroup=HogConfigName skipwhite +syn keyword HogOutput output nextgroup=HogConfigName skipwhite +syn match HogConfigName "[[:alnum:]_-]\+" contained nextgroup=HogConfigOpts skipwhite +syn region HogConfigOpts start=":" skip="\\.\{-}$\|^\s*#.\{-}$\|^\s*$" end="$" fold keepend contained contains=HogSpecial,HogNumber,HogIPAddr,HogVar,HogComment + +" Event filter's and threshold's +syn region HogEvFilter start="event_filter\|threshold" skip="\\.\{-}$\|^\s*#.\{-}$\|^\s*$" end="$" fold transparent keepend contains=HogEvFilterKeyword,HogEvFilterOptions,HogComment +syn keyword HogEvFilterKeyword skipwhite event_filter threshold +syn keyword HogEvFilterOptions skipwhite type nextgroup=HogEvFilterTypes +syn keyword HogEvFilterTypes skipwhite limit threshold both contained +syn keyword HogEvFilterOptions skipwhite track nextgroup=HogEvFilterTrack +syn keyword HogEvFilterTrack skipwhite by_src by_dst contained +syn keyword HogEvFilterOptions skipwhite gen_id sig_id count seconds nextgroup=HogNumber + +" Suppressions +syn region HogEvFilter start="suppress" skip="\\.\{-}$\|^\s*#.\{-}$\|^\s*$" end="$" fold transparent keepend contains=HogSuppressKeyword,HogComment +syn keyword HogSuppressKeyword skipwhite suppress +syn keyword HogSuppressOptions skipwhite gen_id sig_id nextgroup=HogNumber +syn keyword HogSuppressOptions skipwhite track nextgroup=HogEvFilterTrack +syn keyword HogSuppressOptions skipwhite ip nextgroup=HogIPAddr + +" Attribute table +syn keyword HogAttribute attribute_table nextgroup=HogAttributeFile +syn match HogAttributeFile contained ".*$" contains=HogVar,HogAttributeType,HogComment +syn keyword HogAttributeType filename + +" Hog includes +syn keyword HogInclude include nextgroup=HogIncludeFile skipwhite +syn match HogIncludeFile ".*$" contained contains=HogVar,HogComment + +" Hog dynamic libraries +syn keyword HogDylib dynamicpreprocessor dynamicengine dynamicdetection nextgroup=HogDylibFile skipwhite +syn match HogDylibFile "\s.*$" contained contains=HogVar,HogDylibType,HogComment +syn keyword HogDylibType directory file contained + +" Variable dereferenced with '$' +syn match HogVar "\$[[:alnum:]_]\+" + +", Variables declared with 'var' +syn keyword HogVarType var nextgroup=HogVarSet skipwhite +syn match HogVarSet "[[:alnum:]_]\+" display contained nextgroup=HogVarValue skipwhite +syn match HogVarValue ".*$" contained contains=HogString,HogNumber,HogVar,HogComment + +" Variables declared with 'ipvar' +syn keyword HogIPVarType ipvar nextgroup=HogIPVarSet skipwhite +syn match HogIPVarSet "[[:alnum:]_]\+" display contained nextgroup=HogIPVarList,HogSpecial skipwhite +syn region HogIPVarList start="\[" end="]" contains=HogIPVarList,HogIPAddr,HogVar,HogOpNot + +" Variables declared with 'portvar' +syn keyword HogPortVarType portvar nextgroup=HogPortVarSet skipwhite +syn match HogPortVarSet "[[:alnum:]_]\+" display contained nextgroup=HogPortVarList,HogPort,HogOpRange,HogOpNot,HogSpecial skipwhite +syn region HogPortVarList start="\[" end="]" contains=HogPortVarList,HogVar,HogOpNot,HogPort,HogOpRange,HogOpNot +syn match HogPort "\<\%(\d\+\|any\)\>" display contains=HogOpRange nextgroup=HogOpRange + +" Generic stuff +syn match HogIPAddr contained "\<\%(\d\{1,3}\(\.\d\{1,3}\)\{3}\|any\)\>" nextgroup=HogIPCidr +syn match HogIPAddr contained "\<\d\{1,3}\(\.\d\{1,3}\)\{3}\>" nextgroup=HogIPCidr +syn match HogIPCidr contained "\/\([0-2][0-9]\=\|3[0-2]\=\)" +syn region HogHexEsc contained start='|' end='|' oneline +syn region HogString contained start='"' end='"' extend oneline contains=HogHexEsc +syn match HogNumber contained display "\<\d\+\>" +syn match HogNumber contained display "\<\d\+\>" +syn match HogNumber contained display "0x\x\+\>" +syn keyword HogSpecial contained true false yes no default all any +syn keyword HogSpecialAny contained any +syn match HogOpNot "!" contained +syn match HogOpRange ":" contained + +" Rules +syn keyword HogRuleAction activate alert drop block dynamic log pass reject sdrop sblock skipwhite nextgroup=HogRuleProto,HogRuleBlock +syn keyword HogRuleProto ip tcp udp icmp skipwhite contained nextgroup=HogRuleSrcIP +syn match HogRuleSrcIP "\S\+" transparent skipwhite contained contains=HogIPVarList,HogIPAddr,HogVar,HogOpNot nextgroup=HogRuleSrcPort +syn match HogRuleSrcPort "\S\+" transparent skipwhite contained contains=HogPortVarList,HogVar,HogPort,HogOpRange,HogOpNot nextgroup=HogRuleDir +syn match HogRuleDir "->\|<>" skipwhite contained nextgroup=HogRuleDstIP +syn match HogRuleDstIP "\S\+" transparent skipwhite contained contains=HogIPVarList,HogIPAddr,HogVar,HogOpNot nextgroup=HogRuleDstPort +syn match HogRuleDstPort "\S\+" transparent skipwhite contained contains=HogPortVarList,HogVar,HogPort,HogOpRange,HogOpNot nextgroup=HogRuleBlock +syn region HogRuleBlock start="(" end=")" transparent skipwhite contained contains=HogRuleOption,HogComment fold +",HogString,HogComment,HogVar,HogOptNot +"syn region HogRuleOption start="\<gid\|sid\|rev\|depth\|offset\|distance\|within\>" end="\ze;" skipwhite contained contains=HogNumber +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP msg gid sid rev classtype priority metadata content nocase rawbytes +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP depth offset distance within http_client_body http_cookie http_raw_cookie http_header +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP http_raw_header http_method http_uri http_raw_uri http_stat_code http_stat_msg +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP fast_pattern uricontent urilen isdataat pcre pkt_data file_data base64_decode base64_data +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP byte_test byte_jump byte_extract ftpbounce asn1 cvs dce_iface dce_opnum dce_stub_data +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP sip_method sip_stat_code sip_header sip_body gtp_type gtp_info gtp_version ssl_version +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP ssl_state fragoffset ttl tos id ipopts fragbits dsize flags flow flowbits seq ack window +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP itype icode icmp_id icmp_seq rpc ip_proto sameip stream_reassemble stream_size +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP logto session resp react tag activates activated_by count replace detection_filter +syn keyword HogRuleOption skipwhite contained nextgroup=HogRuleSROP threshold reference sd_pattern file_type file_group + +syn region HogRuleSROP start=':' end=";" transparent keepend contained contains=HogRuleChars,HogString,HogNumber +syn match HogRuleChars "\%(\k\|\.\|?\|=\|/\|%\|&\)\+" contained +syn match HogURLChars "\%(\.\|?\|=\)\+" contained + +" Hog File Type Rules +syn match HogFileType /^\s*file.*$/ transparent contains=HogFileTypeOpt,HogFileFROP +syn keyword HogFileTypeOpt skipwhite contained nextgroup=HogRuleFROP file type ver category id rev content offset msg group +syn region HogFileFROP start=':' end=";" transparent keepend contained contains=NotASemicoln +syn match NotASemiColn ".*$" contained + + +" Comments +syn keyword HogTodo XXX TODO NOTE contained +syn match HogTodo "Step\s\+#\=\d\+" contained +syn region HogComment start="#" end="$" contains=HogTodo,@Spell + +syn case match + +if !exists("hog_minlines") + let hog_minlines = 100 endif +exec "syn sync minlines=" . hog_minlines + +hi link HogRuleType Statement +hi link HogRuleTypeName Type +hi link HogRuleTypeType Keyword + +hi link HogPreproc Statement +hi link HogConfig Statement +hi link HogOutput Statement +hi link HogConfigName Type + +"hi link HogEvFilter +hi link HogEvFilterKeyword Statement +hi link HogSuppressKeyword Statement +hi link HogEvFilterTypes Constant +hi link HogEvFilterTrack Constant + +hi link HogAttribute Statement +hi link HogAttributeFile String +hi link HogAttributeType Statement + +hi link HogInclude Statement +hi link HogIncludeFile String + +hi link HogDylib Statement +hi link HogDylibType Statement +hi link HogDylibFile String + +" Variables +" var +hi link HogVar Identifier +hi link HogVarType Keyword +hi link HogVarSet Identifier +hi link HogVarValue String +" ipvar +hi link HogIPVarType Keyword +hi link HogIPVarSet Identifier +" portvar +hi link HogPortVarType Keyword +hi link HogPortVarSet Identifier +hi link HogPort Constant + +hi link HogTodo Todo +hi link HogComment Comment +hi link HogString String +hi link HogHexEsc PreProc +hi link HogNumber Number +hi link HogSpecial Constant +hi link HogSpecialAny Constant +hi link HogIPAddr Constant +hi link HogIPCidr Constant +hi link HogOpNot Operator +hi link HogOpRange Operator + +hi link HogRuleAction Statement +hi link HogRuleProto Identifier +hi link HogRuleDir Operator +hi link HogRuleOption Keyword +hi link HogRuleChars String + +hi link HogFileType HogRuleAction +hi link HogFileTypeOpt HogRuleOption +hi link NotASemiColn HogRuleChars let b:current_syntax = "hog" - -" hog: cpw=59 diff --git a/runtime/syntax/j.vim b/runtime/syntax/j.vim index 0cdd59b887..4912942e8b 100644 --- a/runtime/syntax/j.vim +++ b/runtime/syntax/j.vim @@ -2,7 +2,7 @@ " Language: J " Maintainer: David Bürgin <676c7473@gmail.com> " URL: https://github.com/glts/vim-j -" Last Change: 2014-10-05 +" Last Change: 2015-01-11 if exists('b:current_syntax') finish @@ -23,12 +23,12 @@ syntax match jControl /\<\%(for\|goto\|label\)_\a\k*\./ " Standard library names. A few names need to be defined with ":syntax match" " because they would otherwise take precedence over the corresponding jControl " and jDefineExpression items. -syntax keyword jStdlibNoun ARGV BINPATH CR CRLF DEL Debug EAV EMPTY FF FHS IF64 IFIOS IFJCDROID IFJHS IFQT IFRASPI IFUNIX IFWIN IFWINCE IFWINE IFWOW64 JB01 JBOXED JCHAR JCMPX JFL JINT JPTR JSIZES JSTR JTYPES JVERSION LF LF2 TAB UNAME UNXLIB andurl dbhelp libjqt -syntax keyword jStdlibAdverb define each every fapplylines inv inverse items leaf rows table +syntax keyword jStdlibNoun ARGV BINPATH CR CRLF DEL Debug EAV EMPTY FF FHS IF64 IFIOS IFJCDROID IFJHS IFQT IFRASPI IFUNIX IFWIN IFWINCE IFWINE IFWOW64 JB01 JBOXED JCHAR JCMPX JFL JINT JPTR JSIZES JSTR JTYPES JVERSION LF LF2 TAB UNAME UNXLIB dbhelp libjqt +syntax keyword jStdlibAdverb define each every fapplylines inv inverse items leaf rows rxapply rxmerge table syntax keyword jStdlibConjunction bind cuts def on -syntax keyword jStdlibVerb AND Endian IFDEF Note OR XOR alpha17 alpha27 anddf android_exec_host andunzip apply boxopen boxxopen bx calendar cd cdcb cder cderx cdf charsub chopstring clear coclass cocreate cocurrent codestroy coerase cofind cofindv cofullname coinfo coinsert coname conames conew conl conouns conounsx copath copathnl copathnlx coreset costate cut cutLF cutopen cutpara datatype dbctx dberm dberr dbg dbjmp dblocals dblxq dblxs dbnxt dbq dbr dbret dbrr dbrrx dbrun dbs dbsig dbsq dbss dbst dbstack dbstk dbstop dbstopme dbstopnext dbstops dbtrace dbview deb debc delstring detab dfh dir dircompare dircompares dirfind dirpath dirss dirssrplc dirtree dirused dlb dltb dltbs dquote drop dropafter dropto dtb dtbs echo empty endian erase evtloop exit expand f2utf8 fappend fappends fboxname fc fcopynew fdir ferase fetch fexist fexists fgets file2url fixdotdot fliprgb fmakex foldpara foldtext fpathcreate fpathname fputs fread freadblock freadr freads frename freplace fsize fss fssrplc fstamp fstringreplace ftype fview fwrite fwritenew fwrites getargs getdate getenv getqtbin hfd hostpathsep ic install iospath isatty isotimestamp isutf8 jcwdpath joinstring jpathsep jsystemdefs list ljust load loadd mema memf memr memw nameclass namelist names nc nl pick quote require rjust rplc script scriptd setbreak show sign sminfo smoutput sort split splitnostring splitstring ss startupandroid startupconsole startupide stderr stdin stdout stringreplace symdat symget symset take takeafter taketo timespacex timestamp timex tmoutput toCRLF toHOST toJ todate todayno tolower topara toupper tsdiff tsrep tstamp type ucp ucpcount unxlib usleep utf8 uucp valdate wcsize weekday weeknumber weeksinyear winpathsep +syntax keyword jStdlibVerb AND Endian IFDEF OR XOR anddf android_exec_am android_exec_host andunzip apply boxopen boxxopen bx calendar cd cdcb cder cderx cdf charsub chopstring cleartags clear coclass cocreate cocurrent codestroy coerase cofind cofindv cofullname coinfo coinsert compare coname conames conew conl conouns conounsx copath copathnl copathnlx coreset costate cut cutLF cutopen cutpara datatype dbctx dberm dberr dbg dbjmp dblocals dblxq dblxs dbnxt dbq dbr dbret dbrr dbrrx dbrun dbs dbsig dbsq dbss dbst dbstack dbstk dbstop dbstopme dbstopnext dbstops dbtrace dbview deb debc delstring detab dfh dir dircompare dircompares dirfind dirpath dirss dirssrplc dirtree dirused dlb dltb dltbs dquote drop dropafter dropto dtb dtbs echo empty endian erase evtloop exit expand f2utf8 fappend fappends fboxname fc fcompare fcompares fcopynew fdir ferase fetch fexist fexists fgets file2url fixdotdot fliprgb fmakex foldpara foldtext fpathcreate fpathname fputs fread freadblock freadr freads frename freplace fsize fss fssrplc fstamp fstringreplace ftype fview fwrite fwritenew fwrites getalpha getargs getdate getenv getqtbin hfd hostpathsep ic install iospath isatty isotimestamp isutf8 jcwdpath joinstring jpath jpathsep jsystemdefs launch list ljust load loadd loadtags mema memf memr memw nameclass namelist names nc nl pick quote require rjust rplc rxE rxall rxcomp rxcut rxeq rxerror rxfirst rxfree rxfrom rxhandles rxin rxindex rxinfo rxmatch rxmatches rxrplc rxutf8 script scriptd scripts setalpha setbreak shell show sign sminfo smoutput sort split splitnostring splitstring ss startupandroid startupconsole startupide stderr stdin stdout stringreplace symdat symget symset ta tagcp tagopen tagselect take takeafter taketo timespacex timestamp timex tmoutput toCRLF toHOST toJ todate todayno tolower topara toupper tsdiff tsrep tstamp type ucp ucpcount unxlib usleep utf8 uucp valdate wcsize weekday weeknumber weeksinyear winpathsep xedit syntax match jStdlibNoun /\<\%(adverb\|conjunction\|dyad\|monad\|noun\|verb\)\>/ -syntax match jStdlibVerb /\<\%(assert\|break\|do\)\>\.\@!/ +syntax match jStdlibVerb /\<\%(Note\|\%(assert\|break\|do\)\.\@!\)\>/ " Numbers. Matching J numbers is difficult. In fact, the job cannot be done " with regular expressions alone. Below is a sketch of the pattern used. It @@ -75,12 +75,12 @@ syntax match jConjunction /;\.\|\^:\|![.:]/ " the next line. The trick is to split the problem into two regions and link " them with "nextgroup=". The fold wrapper provides syntax folding. syntax region jNounDefineFold - \ matchgroup=NONE start=/\<\%(\%(0\|noun\)\s\+\%(\:\s*0\|def\s\+0\|define\)\>\)\@=/ + \ matchgroup=NONE start=/\%(\%(\%(^\s*Note\)\|\<\%(0\|noun\)\s\+\%(\:\s*0\|def\s\+0\|define\)\)\>\)\@=/ \ keepend matchgroup=NONE end=/^\s*)\s*$/ \ contains=jNounDefineStart \ fold syntax region jNounDefineStart - \ matchgroup=jDefineExpression start=/\<\%(0\|noun\)\s\+\%(\:\s*0\|def\s\+0\|define\)\>/ + \ matchgroup=jDefineExpression start=/\%(\%(^\s*Note\)\|\<\%(0\|noun\)\s\+\%(\:\s*0\|def\s\+0\|define\)\)\>/ \ keepend matchgroup=NONE end=/$/ \ contains=@jStdlibItems,@jPrimitiveItems,jNumber,jString,jParenGroup,jParen,jComment \ contained oneline skipempty nextgroup=jDefineEnd,jNounDefine @@ -112,7 +112,7 @@ syntax region jParenGroup \ oneline transparent syntax keyword jTodo contained TODO FIXME XXX -syntax match jComment /NB\..*$/ contains=jTodo,@Spell +syntax match jComment /\<NB\..*$/ contains=jTodo,@Spell syntax match jSharpBang /\%^#!.*$/ diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim index e5491b2612..e113a904c3 100644 --- a/runtime/syntax/java.vim +++ b/runtime/syntax/java.vim @@ -2,7 +2,7 @@ " Language: Java " Maintainer: Claudio Fleiner <claudio@fleiner.com> " URL: http://www.fleiner.com/vim/syntax/java.vim -" Last Change: 2012 Oct 05 +" Last Change: 2015 March 01 " Please check :help java.vim for comments on some of the options available. @@ -30,7 +30,7 @@ endif " some characters that cannot be in a java program (outside a string) syn match javaError "[\\@`]" -syn match javaError "<<<\|\.\.\|=>\|||=\|&&=\|[^-]->\|\*\/" +syn match javaError "<<<\|\.\.\|=>\|||=\|&&=\|\*\/" syn match javaOK "\.\.\." @@ -63,7 +63,7 @@ syn match javaTypedef "\.\s*\<class\>"ms=s+1 syn keyword javaClassDecl enum syn match javaClassDecl "^class\>" syn match javaClassDecl "[^.]\s*\<class\>"ms=s+1 -syn match javaAnnotation "@\([_$a-zA-Z][_$a-zA-Z0-9]*\.\)*[_$a-zA-Z][_$a-zA-Z0-9]*\>" +syn match javaAnnotation "@\([_$a-zA-Z][_$a-zA-Z0-9]*\.\)*[_$a-zA-Z][_$a-zA-Z0-9]*\>\(([^)]*)\)\=" contains=javaString syn match javaClassDecl "@interface\>" syn keyword javaBranch break continue nextgroup=javaUserLabelRef skipwhite syn match javaUserLabelRef "\k\+" contained @@ -121,7 +121,7 @@ if exists("java_space_errors") endif endif -syn region javaLabelRegion transparent matchgroup=javaLabel start="\<case\>" matchgroup=NONE end=":" contains=javaNumber,javaCharacter +syn region javaLabelRegion transparent matchgroup=javaLabel start="\<case\>" matchgroup=NONE end=":" contains=javaNumber,javaCharacter,javaString syn match javaUserLabel "^\s*[_$a-zA-Z][_$a-zA-Z0-9_]*\s*:"he=e-1 contains=javaLabel syn keyword javaLabel default @@ -188,10 +188,10 @@ syn region javaString start=+"+ end=+"+ end=+$+ contains=javaSpecialChar,javaS syn match javaCharacter "'[^']*'" contains=javaSpecialChar,javaSpecialCharError syn match javaCharacter "'\\''" contains=javaSpecialChar syn match javaCharacter "'[^\\]'" -syn match javaNumber "\<\(0[0-7]*\|0[xX]\x\+\|\d\+\)[lL]\=\>" -syn match javaNumber "\(\<\d\+\.\d*\|\.\d\+\)\([eE][-+]\=\d\+\)\=[fFdD]\=" -syn match javaNumber "\<\d\+[eE][-+]\=\d\+[fFdD]\=\>" -syn match javaNumber "\<\d\+\([eE][-+]\=\d\+\)\=[fFdD]\>" +syn match javaNumber "\<\(0[bB][0-1]\+\|0[0-7]*\|0[xX]\x\+\|\d\(\d\|_\d\)*\)[lL]\=\>" +syn match javaNumber "\(\<\d\(\d\|_\d\)*\.\(\d\(\d\|_\d\)*\)\=\|\.\d\(\d\|_\d\)*\)\([eE][-+]\=\d\(\d\|_\d\)*\)\=[fFdD]\=" +syn match javaNumber "\<\d\(\d\|_\d\)*[eE][-+]\=\d\(\d\|_\d\)*[fFdD]\=\>" +syn match javaNumber "\<\d\(\d\|_\d\)*\([eE][-+]\=\d\(\d\|_\d\)*\)\=[fFdD]\>" " unicode characters syn match javaSpecial "\\u\d\{4\}" @@ -200,19 +200,21 @@ syn cluster javaTop add=javaString,javaCharacter,javaNumber,javaSpecial,javaStri if exists("java_highlight_functions") if java_highlight_functions == "indent" - syn match javaFuncDef "^\(\t\| \{8\}\)[_$a-zA-Z][_$a-zA-Z0-9_. \[\]]*([^-+*/()]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses - syn region javaFuncDef start=+^\(\t\| \{8\}\)[$_a-zA-Z][$_a-zA-Z0-9_. \[\]]*([^-+*/()]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses - syn match javaFuncDef "^ [$_a-zA-Z][$_a-zA-Z0-9_. \[\]]*([^-+*/()]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses - syn region javaFuncDef start=+^ [$_a-zA-Z][$_a-zA-Z0-9_. \[\]]*([^-+*/()]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses + syn match javaFuncDef "^\(\t\| \{8\}\)[_$a-zA-Z][_$a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation + syn region javaFuncDef start=+^\(\t\| \{8\}\)[$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation + syn match javaFuncDef "^ [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation + syn region javaFuncDef start=+^ [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation else " This line catches method declarations at any indentation>0, but it assumes " two things: " 1. class names are always capitalized (ie: Button) " 2. method names are never capitalized (except constructors, of course) - syn region javaFuncDef start=+^\s\+\(\(public\|protected\|private\|static\|abstract\|final\|native\|synchronized\)\s\+\)*\(\(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\(<[^>]*>\)\=\(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*([^0-9]+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses + "syn region javaFuncDef start=+^\s\+\(\(public\|protected\|private\|static\|abstract\|final\|native\|synchronized\)\s\+\)*\(\(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\(<[^>]*>\)\=\(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*([^0-9]+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses + syn region javaFuncDef start=+^\s\+\(\(public\|protected\|private\|static\|abstract\|final\|native\|synchronized\)\s\+\)*\(<.*>\s\+\)\?\(\(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\(<[^(){}]*>\)\=\(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*(+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses,javaAnnotation endif + syn match javaLambdaDef "[a-zA-Z_][a-zA-Z0-9_]*\s*->" syn match javaBraces "[{}]" - syn cluster javaTop add=javaFuncDef,javaBraces + syn cluster javaTop add=javaFuncDef,javaBraces,javaLambdaDef endif if exists("java_highlight_debug") @@ -266,18 +268,23 @@ if exists("java_mark_braces_in_parens_as_errors") endif " catch errors caused by wrong parenthesis -syn region javaParenT transparent matchgroup=javaParen start="(" end=")" contains=@javaTop,javaParenT1 +syn region javaParenT transparent matchgroup=javaParen start="(" end=")" contains=@javaTop,javaParenT1 syn region javaParenT1 transparent matchgroup=javaParen1 start="(" end=")" contains=@javaTop,javaParenT2 contained syn region javaParenT2 transparent matchgroup=javaParen2 start="(" end=")" contains=@javaTop,javaParenT contained syn match javaParenError ")" " catch errors caused by wrong square parenthesis -syn region javaParenT transparent matchgroup=javaParen start="\[" end="\]" contains=@javaTop,javaParenT1 +syn region javaParenT transparent matchgroup=javaParen start="\[" end="\]" contains=@javaTop,javaParenT1 syn region javaParenT1 transparent matchgroup=javaParen1 start="\[" end="\]" contains=@javaTop,javaParenT2 contained syn region javaParenT2 transparent matchgroup=javaParen2 start="\[" end="\]" contains=@javaTop,javaParenT contained syn match javaParenError "\]" JavaHiLink javaParenError javaError +if exists("java_highlight_functions") + syn match javaLambdaDef "([a-zA-Z0-9_<>\[\], \t]*)\s*->" + " needs to be defined after the parenthesis error catcher to work +endif + if !exists("java_minlines") let java_minlines = 10 endif @@ -288,6 +295,7 @@ if version >= 508 || !exists("did_java_syn_inits") if version < 508 let did_java_syn_inits = 1 endif + JavaHiLink javaLambdaDef Function JavaHiLink javaFuncDef Function JavaHiLink javaVarArg Function JavaHiLink javaBraces Function diff --git a/runtime/syntax/lisp.vim b/runtime/syntax/lisp.vim index 74ec20bb4e..2528f4f9b1 100644 --- a/runtime/syntax/lisp.vim +++ b/runtime/syntax/lisp.vim @@ -1,28 +1,23 @@ " Vim syntax file " Language: Lisp -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Nov 16, 2010 -" Version: 22 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 06, 2014 +" Version: 23 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_LISP " -" Thanks to F Xavier Noria for a list of 978 Common Lisp symbols -" taken from the HyperSpec +" Thanks to F Xavier Noria for a list of 978 Common Lisp symbols taken from HyperSpec " Clisp additions courtesy of http://clisp.cvs.sourceforge.net/*checkout*/clisp/clisp/emacs/lisp.vim " --------------------------------------------------------------------- " Load Once: {{{1 -" For vim-version 5.x: Clear all syntax items -" For vim-version 6.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") - finish +if exists("b:current_syntax") + finish endif -if version >= 600 - setlocal iskeyword=38,42,43,45,47-58,60-62,64-90,97-122,_ +if exists("g:lisp_isk") + exe "setl isk=".g:lisp_isk else - set iskeyword=38,42,43,45,47-58,60-62,64-90,97-122,_ + setl isk=38,42,43,45,47-58,60-62,64-90,97-122,_ endif if exists("g:lispsyntax_ignorecase") || exists("g:lispsyntax_clisp") diff --git a/runtime/syntax/make.vim b/runtime/syntax/make.vim index 6fd46aaa54..35b09341a6 100644 --- a/runtime/syntax/make.vim +++ b/runtime/syntax/make.vim @@ -2,7 +2,7 @@ " Language: Makefile " Maintainer: Claudio Fleiner <claudio@fleiner.com> " URL: http://www.fleiner.com/vim/syntax/make.vim -" Last Change: 2012 Oct 05 +" Last Change: 2015 Feb 28 " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded @@ -43,8 +43,8 @@ syn region makeIdent start="\$(" skip="\\)\|\\\\" end=")" contains=makeStatement syn region makeIdent start="\${" skip="\\}\|\\\\" end="}" contains=makeStatement,makeIdent,makeSString,makeDString syn match makeIdent "\$\$\w*" syn match makeIdent "\$[^({]" -syn match makeIdent "^ *\a\w*\s*[:+?!*]="me=e-2 -syn match makeIdent "^ *\a\w*\s*="me=e-1 +syn match makeIdent "^ *[^:#= \t]*\s*[:+?!*]="me=e-2 +syn match makeIdent "^ *[^:#= \t]*\s*="me=e-1 syn match makeIdent "%" " Makefile.in variables @@ -55,11 +55,11 @@ syn match makeConfig "@[A-Za-z0-9_]\+@" syn match makeImplicit "^\.[A-Za-z0-9_./\t -]\+\s*:$"me=e-1 nextgroup=makeSource syn match makeImplicit "^\.[A-Za-z0-9_./\t -]\+\s*:[^=]"me=e-2 nextgroup=makeSource -syn region makeTarget transparent matchgroup=makeTarget start="^[A-Za-z0-9_./$()%-][A-Za-z0-9_./\t $()%-]*:\{1,2}[^:=]"rs=e-1 end=";"re=e-1,me=e-1 end="[^\\]$" keepend contains=makeIdent,makeSpecTarget,makeNextLine skipnl nextGroup=makeCommands -syn match makeTarget "^[A-Za-z0-9_./$()%*@-][A-Za-z0-9_./\t $()%*@-]*::\=\s*$" contains=makeIdent,makeSpecTarget skipnl nextgroup=makeCommands,makeCommandError +syn region makeTarget transparent matchgroup=makeTarget start="^[~A-Za-z0-9_./$()%-][A-Za-z0-9_./\t $()%-]*:\{1,2}[^:=]"rs=e-1 end=";"re=e-1,me=e-1 end="[^\\]$" keepend contains=makeIdent,makeSpecTarget,makeNextLine,makeComment skipnl nextGroup=makeCommands +syn match makeTarget "^[~A-Za-z0-9_./$()%*@-][A-Za-z0-9_./\t $()%*@-]*::\=\s*$" contains=makeIdent,makeSpecTarget,makeComment skipnl nextgroup=makeCommands,makeCommandError -syn region makeSpecTarget transparent matchgroup=makeSpecTarget start="^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\)\>\s*:\{1,2}[^:=]"rs=e-1 end="[^\\]$" keepend contains=makeIdent,makeSpecTarget,makeNextLine skipnl nextGroup=makeCommands -syn match makeSpecTarget "^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\)\>\s*::\=\s*$" contains=makeIdent skipnl nextgroup=makeCommands,makeCommandError +syn region makeSpecTarget transparent matchgroup=makeSpecTarget start="^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\)\>\s*:\{1,2}[^:=]"rs=e-1 end="[^\\]$" keepend contains=makeIdent,makeSpecTarget,makeNextLine,makeComment skipnl nextGroup=makeCommands +syn match makeSpecTarget "^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\)\>\s*::\=\s*$" contains=makeIdent,makeComment skipnl nextgroup=makeCommands,makeCommandError syn match makeCommandError "^\s\+\S.*" contained syn region makeCommands start=";"hs=s+1 start="^\t" end="^[^\t#]"me=e-1,re=e-1 end="^$" contained contains=makeCmdNextLine,makeSpecial,makeComment,makeIdent,makePreCondit,makeDefine,makeDString,makeSString nextgroup=makeCommandError diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim index 4172a02fe1..fbc1847e6e 100644 --- a/runtime/syntax/man.vim +++ b/runtime/syntax/man.vim @@ -3,7 +3,7 @@ " Maintainer: SungHyun Nam <goweol@gmail.com> " Previous Maintainer: Gautam H. Mudunuri <gmudunur@informatica.com> " Version Info: -" Last Change: 2008 Sep 17 +" Last Change: 2015 Nov 24 " Additional highlighting by Johannes Tanzler <johannes.tanzler@aon.at>: " * manSubHeading @@ -27,8 +27,8 @@ endif syn case ignore syn match manReference "\f\+([1-9][a-z]\=)" syn match manTitle "^\f\+([0-9]\+[a-z]\=).*" -syn match manSectionHeading "^[a-z][a-z ]*[a-z]$" -syn match manSubHeading "^\s\{3\}[a-z][a-z ]*[a-z]$" +syn match manSectionHeading "^[a-z][a-z -]*[a-z]$" +syn match manSubHeading "^\s\{3\}[a-z][a-z -]*[a-z]$" syn match manOptionDesc "^\s*[+-][a-z0-9]\S*" syn match manLongOptionDesc "^\s*--[a-z0-9-]\S*" " syn match manHistory "^[a-z].*last change.*$" diff --git a/runtime/syntax/manual.vim b/runtime/syntax/manual.vim index 5ea373180a..c0e53fa7b4 100644 --- a/runtime/syntax/manual.vim +++ b/runtime/syntax/manual.vim @@ -1,6 +1,6 @@ " Vim syntax support file " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2008 Jan 26 +" Last Change: 2016 Feb 01 " This file is used for ":syntax manual". " It installs the Syntax autocommands, but no the FileType autocommands. @@ -16,10 +16,11 @@ endif let syntax_manual = 1 -" Remove the connection between FileType and Syntax autocommands. -if exists('#syntaxset') - au! syntaxset FileType -endif +" Overrule the connection between FileType and Syntax autocommands. This sets +" the syntax when the file type is detected, without changing the value. +augroup syntaxset + au! FileType * exe "set syntax=" . &syntax +augroup END " If the GUI is already running, may still need to install the FileType menu. " Don't do it when the 'M' flag is included in 'guioptions'. diff --git a/runtime/syntax/maple.vim b/runtime/syntax/maple.vim index b6e4ae9243..9c94643836 100644 --- a/runtime/syntax/maple.vim +++ b/runtime/syntax/maple.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: Maple V (based on release 4) -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Jan 05, 2010 -" Version: 10 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 11 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_MAPLE " " Package Function Selection: {{{1 " Because there are a lot of packages, and because of the potential for namespace diff --git a/runtime/syntax/mplayerconf.vim b/runtime/syntax/mplayerconf.vim index b348327f90..84ad2daf13 100644 --- a/runtime/syntax/mplayerconf.vim +++ b/runtime/syntax/mplayerconf.vim @@ -1,7 +1,8 @@ " Vim syntax file -" Language: mplayer(1) configuration file -" Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2007-06-17 +" Language: mplayer(1) configuration file +" Maintainer: Dmitri Vereshchagin <dmitri.vereshchagin@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2015-01-24 if exists("b:current_syntax") finish @@ -19,7 +20,7 @@ syn region mplayerconfComment display oneline start='#' end='$' syn keyword mplayerconfPreProc include -syn keyword mplayerconfBoolean yes no +syn keyword mplayerconfBoolean yes no true false syn match mplayerconfNumber '\<\d\+\>' @@ -68,7 +69,49 @@ syn keyword mplayerconfOption hardframedrop nomouseinput bandwidth dumpstream \ audio-delay audio-preload endpos ffourcc \ include info noautoexpand noskip o oac of \ ofps ovc skiplimit v vobsubout vobsuboutid - \ lameopts lavcopts nuvopts xvidencopts + \ lameopts lavcopts nuvopts xvidencopts a52drc + \ adapter af-add af-clr af-del af-pre + \ allow-dangerous-playlist-parsing ass + \ ass-border-color ass-bottom-margin ass-color + \ ass-font-scale ass-force-style ass-hinting + \ ass-line-spacing ass-styles ass-top-margin + \ ass-use-margins ausid bluray-angle + \ bluray-device border border-pos-x border-pos-y + \ cache-min cache-seek-min capture codecpath + \ codecs-file correct-pts crash-debug + \ doubleclick-time dvd-speed edl-backward-delay + \ edl-start-pts embeddedfonts fafmttag + \ field-dominance fontconfig force-avi-aspect + \ force-key-frames frameno-file fullscreen gamma + \ gui gui-include gui-wid heartbeat-cmd + \ heartbeat-interval hr-edl-seek + \ http-header-fields idle ignore-start + \ key-fifo-size list-properties menu-chroot + \ menu-keepdir menu-startup mixer-channel + \ monitor-orientation monitorpixelaspect + \ mouse-movements msgcharset msgcolor msglevel + \ msgmodule name noar nocache noconfig + \ noconsolecontrols nocorrect-pts nodouble + \ noedl-start-pts noencodedups + \ noflip-hebrew-commas nogui noidx noodml + \ nostop-xscreensaver nosub noterm-osd + \ osd-duration osd-fractions panscanrange + \ pausing playing-msg priority profile + \ progbar-align psprobe pvr radio referrer + \ refreshrate reuse-socket rtc rtc-device + \ rtsp-destination rtsp-port + \ rtsp-stream-over-http screenh show-profile + \ softvol softvol-max sub-paths subfont + \ term-osd-esc title tvscan udp-ip udp-master + \ udp-port udp-seek-threshold udp-slave + \ unrarexec use-filedir-conf use-filename-title + \ vf-add vf-clr vf-del vf-pre volstep volume + \ zrhdec zrydoff + +syn region mplayerconfString display oneline start=+"+ end=+"+ +syn region mplayerconfString display oneline start=+'+ end=+'+ + +syn region mplayerconfProfile display oneline start='^\s*\[' end='\]' hi def link mplayerconfTodo Todo hi def link mplayerconfComment Comment @@ -76,6 +119,8 @@ hi def link mplayerconfPreProc PreProc hi def link mplayerconfBoolean Boolean hi def link mplayerconfNumber Number hi def link mplayerconfOption Keyword +hi def link mplayerconfString String +hi def link mplayerconfProfile Special let b:current_syntax = "mplayerconf" diff --git a/runtime/syntax/netrw.vim b/runtime/syntax/netrw.vim index 980fe5dde5..718cee1429 100644 --- a/runtime/syntax/netrw.vim +++ b/runtime/syntax/netrw.vim @@ -19,7 +19,6 @@ syn cluster NetrwTreeGroup contains=netrwDir,netrwSymLink,netrwExe syn match netrwPlain "\(\S\+ \)*\S\+" contains=netrwLink,@NoSpell syn match netrwSpecial "\%(\S\+ \)*\S\+[*|=]\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell syn match netrwDir "\.\{1,2}/" contains=netrwClassify,@NoSpell -"syn match netrwDir "\%(\S\+ \)*\S\+/" contains=netrwClassify,@NoSpell syn match netrwDir "\%(\S\+ \)*\S\+/\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell syn match netrwSizeDate "\<\d\+\s\d\{1,2}/\d\{1,2}/\d\{4}\s" skipwhite contains=netrwDateSep,@NoSpell nextgroup=netrwTime syn match netrwSymLink "\%(\S\+ \)*\S\+@\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell diff --git a/runtime/syntax/objc.vim b/runtime/syntax/objc.vim index 1f61e50b8d..9d7b20ecd0 100644 --- a/runtime/syntax/objc.vim +++ b/runtime/syntax/objc.vim @@ -1,8 +1,7 @@ " Vim syntax file " Language: Objective-C -" Maintainer: Kazunobu Kuriyama <kazunobu.kuriyama@nifty.com> -" Last Change: 2013 Jun 13 -" Remark: Modern Objective-C Edition +" Maintainer: Kazunobu Kuriyama <kazunobu.kuriyama@gmail.com> +" Last Change: 2015 Dec 14 """ Preparation for loading ObjC stuff if exists("b:current_syntax") @@ -25,14 +24,14 @@ syn keyword objcUsefulTerm nil Nil NO YES " Preprocessor Directives syn region objcImported display contained start=+"+ skip=+\\\\\|\\"+ end=+"+ -syn match objcImported display contained "<[^>]*>" +syn match objcImported display contained "\(<\h[-a-zA-Z0-9_/]*\.h>\|<[a-z0-9]\+>\)" syn match objcImport display "^\s*\(%:\|#\)\s*import\>\s*["<]" contains=objcImported " ObjC Compiler Directives syn match objcObjDef display /@interface\>\|@implementation\>\|@end\>\|@class\>/ syn match objcProtocol display /@protocol\>\|@optional\>\|@required\>/ syn match objcProperty display /@property\>\|@synthesize\>\|@dynamic\>/ -syn match objcIvarScope display /@private\>\|@protected\>\|@public\>/ +syn match objcIvarScope display /@private\>\|@protected\>\|@public\>\|@package\>/ syn match objcInternalRep display /@selector\>\|@encode\>/ syn match objcException display /@try\>\|@throw\>\|@catch\|@finally\>/ syn match objcThread display /@synchronized\>/ @@ -56,6 +55,17 @@ syn keyword objcTollFreeBridgeQualifier __bridge __bridge_retained __bridge_tran " ObjC Type Qualifiers for Remote Messaging syn match objcRemoteMessagingQualifier display contained /\((\s*oneway\s\+\|(\s*in\s\+\|(\s*out\s\+\|(\s*inout\s\+\|(\s*bycopy\s\+\(in\(out\)\?\|out\)\?\|(\s*byref\s\+\(in\(out\)\?\|out\)\?\)/hs=s+1 +" ObjC Storage Classes +syn keyword objcStorageClass _Nullable _Nonnull _Null_unspecified +syn keyword objcStorageClass __nullable __nonnull __null_unspecified +syn keyword objcStorageClass nullable nonnull null_unspecified + +" ObjC type specifier +syn keyword objcTypeSpecifier __kindof __covariant + +" ObjC Type Infomation Parameters +syn keyword objcTypeInfoParams ObjectType KeyType + " shorthand syn cluster objcTypeQualifier contains=objcBlocksQualifier,objcObjectLifetimeQualifier,objcTollFreeBridgeQualifier,objcRemoteMessagingQualifier @@ -75,17 +85,24 @@ syn keyword objcDeclPropAccessorType readonly readwrite contained syn keyword objcDeclPropAssignSemantics assign retain copy contained syn keyword objcDeclPropAtomicity nonatomic contained syn keyword objcDeclPropARC strong weak contained -syn region objcDeclProp display transparent keepend start=/@property\s*(/ end=/)/ contains=objcProperty,objcDeclPropAccessorName,objcDeclPropAccessorType,objcDeclPropAssignSemantics,objcDeclPropAtomicity,objcDeclPropARC +syn match objcDeclPropNullable /\((\|\s\)nullable\(,\|)\)/ms=s+1,hs=s+1,me=e-1,he=e-1 contained +syn match objcDeclPropNonnull /\((\|\s\)nonnull\(,\|)\)/ms=s+1,hs=s+1,me=e-1,he=e-1 contained +syn match objcDeclPropNullUnspecified /\((\|\s\)null_unspecified\(,\|)\)/ms=s+1,hs=s+1,me=e-1,he=e-1 contained +syn keyword objcDeclProcNullResettable null_resettable contained +syn region objcDeclProp display transparent keepend start=/@property\s*(/ end=/)/ contains=objcProperty,objcDeclPropAccessorName,objcDeclPropAccessorType,objcDeclPropAssignSemantics,objcDeclPropAtomicity,objcDeclPropARC,objcDeclPropNullable,objcDeclPropNonnull,objcDeclPropNullUnspecified,objcDeclProcNullResettable " To distinguish colons in methods and dictionaries from those in C's labels. syn match objcColon display /^\s*\h\w*\s*\:\(\s\|.\)/me=e-1,he=e-1 " To distinguish a protocol list from system header files -syn match objcProtocolList display /<\h\w*\(\s*,\s*\h\w*\)*>/ contains=objcPrincipalType,cType,Type +syn match objcProtocolList display /<\h\w*\(\s*,\s*\h\w*\)*>/ contains=objcPrincipalType,cType,Type,objcType,objcTypeInfoParams + +" Type info for collection classes +syn match objcTypeInfo display /<\h\w*\s*<\(\h\w*\s*\**\|\h\w*\)>>/ contains=objcPrincipalType,cType,Type,objcType,objcTypeInfoParams " shorthand syn cluster objcCEntities contains=cType,cStructure,cStorageClass,cString,cCharacter,cSpecialCharacter,cNumbers,cConstant,cOperator,cComment,cCommentL,cStatement,cLabel,cConditional,cRepeat -syn cluster objcObjCEntities contains=objcHiddenArgument,objcPrincipalType,objcString,objcUsefulTerm,objcProtocol,objcInternalRep,objcException,objcThread,objcPool,objcModuleImport,@objcTypeQualifier,objcLiteralSyntaxNumber,objcLiteralSyntaxOp,objcLiteralSyntaxChar,objcLiteralSyntaxSpecialChar,objcProtocolList,objcColon,objcFastEnumKeyword,objcType,objcClass,objcMacro,objcEnum,objcEnumValue,objcExceptionValue,objcNotificationValue,objcConstVar,objcPreProcMacro +syn cluster objcObjCEntities contains=objcHiddenArgument,objcPrincipalType,objcString,objcUsefulTerm,objcProtocol,objcInternalRep,objcException,objcThread,objcPool,objcModuleImport,@objcTypeQualifier,objcLiteralSyntaxNumber,objcLiteralSyntaxOp,objcLiteralSyntaxChar,objcLiteralSyntaxSpecialChar,objcProtocolList,objcColon,objcFastEnumKeyword,objcType,objcClass,objcMacro,objcEnum,objcEnumValue,objcExceptionValue,objcNotificationValue,objcConstVar,objcPreProcMacro,objcTypeInfo " Objective-C Message Expressions syn region objcMethodCall start=/\[/ end=/\]/ contains=objcMethodCall,objcBlocks,@objcObjCEntities,@objcCEntities @@ -114,13 +131,17 @@ syn keyword objcEnum NSSortOptions syn keyword objcEnumValue NSSortConcurrent NSSortStable syn keyword objcEnumValue NSNotFound syn keyword objcMacro NSIntegerMax NSIntegerMin NSUIntegerMax +syn keyword objcMacro NS_INLINE NS_BLOCKS_AVAILABLE NS_NONATOMIC_IOSONLY NS_FORMAT_FUNCTION NS_FORMAT_ARGUMENT NS_RETURNS_RETAINED NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER NS_AUTOMATED_REFCOUNT_UNAVAILABLE NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE NS_REQUIRES_PROPERTY_DEFINITIONS NS_REPLACES_RECEIVER NS_RELEASES_ARGUMENT NS_VALID_UNTIL_END_OF_SCOPE NS_ROOT_CLASS NS_REQUIRES_SUPER NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION NS_DESIGNATED_INITIALIZER NS_REQUIRES_NIL_TERMINATION +syn keyword objcEnum NSQualityOfService +syn keyword objcEnumValue NSQualityOfServiceUserInteractive NSQualityOfServiceUserInitiated NSQualityOfServiceUtility NSQualityOfServiceBackground NSQualityOfServiceDefault " NSRange.h syn keyword objcType NSRange NSRangePointer " NSGeometry.h -syn keyword objcType NSPoint NSPointPointer NSPointArray NSSize NSSizePointer NSSizeArray NSRect NSRectPointer NSRectArray +syn keyword objcType NSPoint NSPointPointer NSPointArray NSSize NSSizePointer NSSizeArray NSRect NSRectPointer NSRectArray NSEdgeInsets syn keyword objcEnum NSRectEdge syn keyword objcEnumValue NSMinXEdge NSMinYEdge NSMaxXEdge NSMaxYEdge -syn keyword objcConstVar NSZeroPoint NSZeroSize NSZeroRect +syn keyword objcEnumValue NSRectEdgeMinX NSRectEdgeMinY NSRectEdgeMaxX NSRectEdgeMaxY +syn keyword objcConstVar NSZeroPoint NSZeroSize NSZeroRect NSEdgeInsetsZero syn keyword cType CGFloat CGPoint CGSize CGRect syn keyword objcEnum NSAlignmentOptions syn keyword objcEnumValue NSAlignMinXInward NSAlignMinYInward NSAlignMaxXInward NSAlignMaxYInward NSAlignWidthInward NSAlignHeightInward NSAlignMinXOutward NSAlignMinYOutward NSAlignMaxXOutward NSAlignMaxYOutward NSAlignWidthOutward NSAlignHeightOutward NSAlignMinXNearest NSAlignMinYNearest NSAlignMaxXNearest NSAlignMaxYNearest NSAlignWidthNearest NSAlignHeightNearest NSAlignRectFlipped NSAlignAllEdgesInward NSAlignAllEdgesOutward NSAlignAllEdgesNearest @@ -130,6 +151,7 @@ syn keyword objcEnum NSRoundingMode syn keyword objcEnumValue NSRoundPlain NSRoundDown NSRoundUp NSRoundBankers syn keyword objcEnum NSCalculationError syn keyword objcEnumValue NSCalculationNoError NSCalculationLossOfPrecision NSCalculationUnderflow NSCalculationOverflow NSCalculationDivideByZero +syn keyword objcConstVar NSDecimalMaxSize NSDecimalNoScale " NSDate.h syn match objcClass /NSDate\s*\*/me=s+6,he=s+6 syn keyword objcType NSTimeInterval @@ -137,11 +159,13 @@ syn keyword objcNotificationValue NSSystemClockDidChangeNotification syn keyword objcMacro NSTimeIntervalSince1970 " NSZone.h syn match objcType /NSZone\s*\*/me=s+6,he=s+6 +syn keyword objcEnumValue NSScannedOption NSCollectorDisabledOption " NSError.h syn match objcClass /NSError\s*\*/me=s+7,he=s+7 syn keyword objcConstVar NSCocoaErrorDomain NSPOSIXErrorDomain NSOSStatusErrorDomain NSMachErrorDomain NSUnderlyingErrorKey NSLocalizedDescriptionKey NSLocalizedFailureReasonErrorKey NSLocalizedRecoverySuggestionErrorKey NSLocalizedRecoveryOptionsErrorKey NSRecoveryAttempterErrorKey NSHelpAnchorErrorKey NSStringEncodingErrorKey NSURLErrorKey NSFilePathErrorKey " NSException.h syn match objcClass /NSException\s*\*/me=s+11,he=s+11 +syn match objcClass /NSAssertionHandler\s*\*/me=s+18,he=s+18 syn keyword objcType NSUncaughtExceptionHandler syn keyword objcConstVar NSGenericException NSRangeException NSInvalidArgumentException NSInternalInconsistencyException NSMallocException NSObjectInaccessibleException NSObjectNotAvailableException NSDestinationInvalidException NSPortTimeoutException NSInvalidSendPortException NSInvalidReceivePortException NSPortSendException NSPortReceiveException NSOldStyleException " NSNotification.h @@ -153,6 +177,8 @@ syn keyword objcConstVar NSLocalNotificationCenterType syn keyword objcEnum NSNotificationSuspensionBehavior syn keyword objcEnumValue NSNotificationSuspensionBehaviorDrop NSNotificationSuspensionBehaviorCoalesce NSNotificationSuspensionBehaviorHold NSNotificationSuspensionBehaviorHold NSNotificationSuspensionBehaviorDeliverImmediately syn keyword objcEnumValue NSNotificationDeliverImmediately NSNotificationPostToAllSessions +syn keyword objcEnum NSDistributedNotificationOptions +syn keyword objcEnumValue NSDistributedNotificationDeliverImmediately NSDistributedNotificationPostToAllSessions " NSNotificationQueue.h syn match objcClass /NSNotificationQueue\s*\*/me=s+19,he=s+19 syn keyword objcEnum NSPostingStyle @@ -161,11 +187,15 @@ syn keyword objcEnum NSNotificationCoalescing syn keyword objcEnumValue NSNotificationNoCoalescing NSNotificationCoalescingOnName NSNotificationCoalescingOnSender " NSEnumerator.h syn match objcClass /NSEnumerator\s*\*/me=s+12,he=s+12 +syn match objcClass /NSEnumerator<.*>\s*\*/me=s+12,he=s+12 contains=objcTypeInfoParams +syn keyword objcType NSFastEnumerationState " NSIndexSet.h syn match objcClass /NSIndexSet\s*\*/me=s+10,he=s+10 syn match objcClass /NSMutableIndexSet\s*\*/me=s+17,he=s+17 " NSCharecterSet.h syn match objcClass /NSCharacterSet\s*\*/me=s+14,he=s+14 +syn match objcClass /NSMutableCharacterSet\s*\*/me=s+21,he=s+21 +syn keyword objcConstVar NSOpenStepUnicodeReservedBase " NSURL.h syn match objcClass /NSURL\s*\*/me=s+5,he=s+5 syn keyword objcEnum NSURLBookmarkCreationOptions @@ -174,11 +204,11 @@ syn keyword objcEnum NSURLBookmarkResolutionOptions syn keyword objcEnumValue NSURLBookmarkResolutionWithoutUI NSURLBookmarkResolutionWithoutMounting NSURLBookmarkResolutionWithSecurityScope syn keyword objcType NSURLBookmarkFileCreationOptions syn keyword objcConstVar NSURLFileScheme NSURLKeysOfUnsetValuesKey -syn keyword objcConstVar NSURLNameKey NSURLLocalizedNameKey NSURLIsRegularFileKey NSURLIsDirectoryKey NSURLIsSymbolicLinkKey NSURLIsVolumeKey NSURLIsPackageKey NSURLIsSystemImmutableKey NSURLIsUserImmutableKey NSURLIsHiddenKey NSURLHasHiddenExtensionKey NSURLCreationDateKey NSURLContentAccessDateKey NSURLContentModificationDateKey NSURLAttributeModificationDateKey NSURLLinkCountKey NSURLParentDirectoryURLKey NSURLVolumeURLKey NSURLTypeIdentifierKey NSURLLocalizedTypeDescriptionKey NSURLLabelNumberKey NSURLLabelColorKey NSURLLocalizedLabelKey NSURLEffectiveIconKey NSURLCustomIconKey NSURLFileResourceIdentifierKey NSURLVolumeIdentifierKey NSURLPreferredIOBlockSizeKey NSURLIsReadableKey NSURLIsWritableKey NSURLIsExecutableKey NSURLFileSecurityKey NSURLIsExcludedFromBackupKey NSURLPathKey NSURLIsMountTriggerKey NSURLFileResourceTypeKey -syn keyword objcConstVar NSURLFileResourceTypeNamedPipe NSURLFileResourceTypeCharacterSpecial NSURLFileResourceTypeDirectory NSURLFileResourceTypeBlockSpecial NSURLFileResourceTypeRegular NSURLFileResourceTypeSymbolicLink NSURLFileResourceTypeSocket NSURLFileResourceTypeUnknown -syn keyword objcConstVar NSURLFileSizeKey NSURLFileAllocatedSizeKey NSURLTotalFileSizeKey NSURLTotalFileAllocatedSizeKey NSURLIsAliasFileKey +syn keyword objcConstVar NSURLNameKey NSURLLocalizedNameKey NSURLIsRegularFileKey NSURLIsDirectoryKey NSURLIsSymbolicLinkKey NSURLIsVolumeKey NSURLIsPackageKey NSURLIsApplicationKey NSURLApplicationIsScriptableKey NSURLIsSystemImmutableKey NSURLIsUserImmutableKey NSURLIsHiddenKey NSURLHasHiddenExtensionKey NSURLCreationDateKey NSURLContentAccessDateKey NSURLContentModificationDateKey NSURLAttributeModificationDateKey NSURLLinkCountKey NSURLParentDirectoryURLKey NSURLVolumeURLKey NSURLTypeIdentifierKey NSURLLocalizedTypeDescriptionKey NSURLLabelNumberKey NSURLLabelColorKey NSURLLocalizedLabelKey NSURLEffectiveIconKey NSURLCustomIconKey NSURLFileResourceIdentifierKey NSURLVolumeIdentifierKey NSURLPreferredIOBlockSizeKey NSURLIsReadableKey NSURLIsWritableKey NSURLIsExecutableKey NSURLFileSecurityKey NSURLIsExcludedFromBackupKey NSURLTagNamesKey NSURLPathKey NSURLIsMountTriggerKey NSURLGenerationIdentifierKey NSURLDocumentIdentifierKey NSURLAddedToDirectoryDateKey NSURLQuarantinePropertiesKey NSURLFileResourceTypeKey +syn keyword objcConstVar NSURLFileResourceTypeNamedPipe NSURLFileResourceTypeCharacterSpecial NSURLFileResourceTypeDirectory NSURLFileResourceTypeBlockSpecial NSURLFileResourceTypeRegular NSURLFileResourceTypeSymbolicLink NSURLFileResourceTypeSocket NSURLFileResourceTypeUnknown NSURLThumbnailDictionaryKey NSURLThumbnailKey NSThumbnail1024x1024SizeKey +syn keyword objcConstVar NSURLFileSizeKey NSURLFileAllocatedSizeKey NSURLTotalFileSizeKey NSURLTotalFileAllocatedSizeKey NSURLIsAliasFileKey NSURLFileProtectionKey NSURLFileProtectionNone NSURLFileProtectionComplete NSURLFileProtectionCompleteUnlessOpen NSURLFileProtectionCompleteUntilFirstUserAuthentication syn keyword objcConstVar NSURLVolumeLocalizedFormatDescriptionKey NSURLVolumeTotalCapacityKey NSURLVolumeAvailableCapacityKey NSURLVolumeResourceCountKey NSURLVolumeSupportsPersistentIDsKey NSURLVolumeSupportsSymbolicLinksKey NSURLVolumeSupportsHardLinksKey NSURLVolumeSupportsJournalingKey NSURLVolumeIsJournalingKey NSURLVolumeSupportsSparseFilesKey NSURLVolumeSupportsZeroRunsKey NSURLVolumeSupportsCaseSensitiveNamesKey NSURLVolumeSupportsCasePreservedNamesKey NSURLVolumeSupportsRootDirectoryDatesKey NSURLVolumeSupportsVolumeSizesKey NSURLVolumeSupportsRenamingKey NSURLVolumeSupportsAdvisoryFileLockingKey NSURLVolumeSupportsExtendedSecurityKey NSURLVolumeIsBrowsableKey NSURLVolumeMaximumFileSizeKey NSURLVolumeIsEjectableKey NSURLVolumeIsRemovableKey NSURLVolumeIsInternalKey NSURLVolumeIsAutomountedKey NSURLVolumeIsLocalKey NSURLVolumeIsReadOnlyKey NSURLVolumeCreationDateKey NSURLVolumeURLForRemountingKey NSURLVolumeUUIDStringKey NSURLVolumeNameKey NSURLVolumeLocalizedNameKey -syn keyword objcConstVar NSURLIsUbiquitousItemKey NSURLUbiquitousItemHasUnresolvedConflictsKey NSURLUbiquitousItemIsDownloadedKey NSURLUbiquitousItemIsDownloadingKey NSURLUbiquitousItemIsUploadedKey NSURLUbiquitousItemIsUploadingKey NSURLUbiquitousItemPercentDownloadedKey NSURLUbiquitousItemPercentUploadedKey +syn keyword objcConstVar NSURLIsUbiquitousItemKey NSURLUbiquitousItemHasUnresolvedConflictsKey NSURLUbiquitousItemIsDownloadedKey NSURLUbiquitousItemIsDownloadingKey NSURLUbiquitousItemIsUploadedKey NSURLUbiquitousItemIsUploadingKey NSURLUbiquitousItemPercentDownloadedKey NSURLUbiquitousItemPercentUploadedKey NSURLUbiquitousItemDownloadingStatusKey NSURLUbiquitousItemDownloadingErrorKey NSURLUbiquitousItemUploadingErrorKey NSURLUbiquitousItemDownloadRequestedKey NSURLUbiquitousItemContainerDisplayNameKey NSURLUbiquitousItemDownloadingStatusNotDownloaded NSURLUbiquitousItemDownloadingStatusDownloaded NSURLUbiquitousItemDownloadingStatusCurrent """""""""""" " NSString.h syn match objcClass /NSString\s*\*/me=s+8,he=s+8 @@ -189,11 +219,14 @@ syn keyword objcMacro NSMaximumStringLength syn keyword objcEnum NSStringCompareOptions syn keyword objcEnumValue NSCaseInsensitiveSearch NSLiteralSearch NSBackwardsSearch NSAnchoredSearch NSNumericSearch NSDiacriticInsensitiveSearch NSWidthInsensitiveSearch NSForcedOrderingSearch NSRegularExpressionSearch syn keyword objcEnum NSStringEncoding +syn keyword objcEnumValue NSProprietaryStringEncoding syn keyword objcEnumValue NSASCIIStringEncoding NSNEXTSTEPStringEncoding NSJapaneseEUCStringEncoding NSUTF8StringEncoding NSISOLatin1StringEncoding NSSymbolStringEncoding NSNonLossyASCIIStringEncoding NSShiftJISStringEncoding NSISOLatin2StringEncoding NSUnicodeStringEncoding NSWindowsCP1251StringEncoding NSWindowsCP1252StringEncoding NSWindowsCP1253StringEncoding NSWindowsCP1254StringEncoding NSWindowsCP1250StringEncoding NSISO2022JPStringEncoding NSMacOSRomanStringEncoding NSUTF16StringEncoding NSUTF16BigEndianStringEncoding NSUTF16LittleEndianStringEncoding NSUTF32StringEncoding NSUTF32BigEndianStringEncoding NSUTF32LittleEndianStringEncoding syn keyword objcEnum NSStringEncodingConversionOptions syn keyword objcEnumValue NSStringEncodingConversionAllowLossy NSStringEncodingConversionExternalRepresentation syn keyword objcEnum NSStringEnumerationOptions syn keyword objcEnumValue NSStringEnumerationByLines NSStringEnumerationByParagraphs NSStringEnumerationByComposedCharacterSequences NSStringEnumerationByWords NSStringEnumerationBySentences NSStringEnumerationReverse NSStringEnumerationSubstringNotRequired NSStringEnumerationLocalized +syn keyword objcConstVar NSStringTransformLatinToKatakana NSStringTransformLatinToHiragana NSStringTransformLatinToHangul NSStringTransformLatinToArabic NSStringTransformLatinToHebrew NSStringTransformLatinToThai NSStringTransformLatinToCyrillic NSStringTransformLatinToGreek NSStringTransformToLatin NSStringTransformMandarinToLatin NSStringTransformHiraganaToKatakana NSStringTransformFullwidthToHalfwidth NSStringTransformToXMLHex NSStringTransformToUnicodeName NSStringTransformStripCombiningMarks NSStringTransformStripDiacritics +syn keyword objcConstVar NSStringEncodingDetectionSuggestedEncodingsKey NSStringEncodingDetectionDisallowedEncodingsKey NSStringEncodingDetectionUseOnlySuggestedEncodingsKey NSStringEncodingDetectionAllowLossyKey NSStringEncodingDetectionFromWindowsKey NSStringEncodingDetectionLossySubstitutionKey NSStringEncodingDetectionLikelyLanguageKey " NSAttributedString.h syn match objcClass /NSAttributedString\s*\*/me=s+18,he=s+18 syn match objcClass /NSMutableAttributedString\s*\*/me=s+25,he=s+25 @@ -215,21 +248,32 @@ syn keyword objcEnum NSDataWritingOptions syn keyword objcEnumValue NSDataWritingAtomic NSDataWritingWithoutOverwriting NSDataWritingFileProtectionNone NSDataWritingFileProtectionComplete NSDataWritingFileProtectionCompleteUnlessOpen NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication NSDataWritingFileProtectionMask NSAtomicWrite syn keyword objcEnum NSDataSearchOptions syn keyword objcEnumValue NSDataSearchBackwards NSDataSearchAnchored +syn keyword objcEnum NSDataBase64EncodingOptions NSDataBase64DecodingOptions +syn keyword objcEnumValue NSDataBase64Encoding64CharacterLineLength NSDataBase64Encoding76CharacterLineLength NSDataBase64EncodingEndLineWithCarriageReturn NSDataBase64EncodingEndLineWithLineFeed NSDataBase64DecodingIgnoreUnknownCharacters " NSArray.h syn match objcClass /NSArray\s*\*/me=s+7,he=s+7 +syn match objcClass /NSArray<.*>\s*\*/me=s+7,he=s+7 contains=objcTypeInfoParams syn match objcClass /NSMutableArray\s*\*/me=s+14,he=s+14 +syn match objcClass /NSMutableArray<.*>\s*\*/me=s+14,he=s+14 contains=objcTypeInfoParams syn keyword objcEnum NSBinarySearchingOptions syn keyword objcEnumValue NSBinarySearchingFirstEqual NSBinarySearchingLastEqual NSBinarySearchingInsertionIndex " NSDictionary.h syn match objcClass /NSDictionary\s*\*/me=s+12,he=s+12 +syn match objcClass /NSDictionary<.*>\s*\*/me=s+12,he=s+12 contains=objcTypeInfoParams syn match objcClass /NSMutableDictionary\s*\*/me=s+19,he=s+19 +syn match objcClass /NSMutableDictionary<.*>\s*\*/me=s+19,he=s+19 contains=objcTypeInfoParams " NSSet.h syn match objcClass /NSSet\s*\*/me=s+5,me=s+5 +syn match objcClass /NSSet<.*>\s*\*/me=s+5,me=s+5 contains=objcTypeInfoParams syn match objcClass /NSMutableSet\s*\*/me=s+12,me=s+12 +syn match objcClass /NSMutableSet<.*>\s*\*/me=s+12,me=s+12 contains=objcTypeInfoParams syn match objcClass /NSCountedSet\s*\*/me=s+12,me=s+12 +syn match objcClass /NSCountedSet<.*>\s*\*/me=s+12,me=s+12 contains=objcTypeInfoParams " NSOrderedSet.h syn match objcClass /NSOrderedSet\s*\*/me=s+12,me=s+12 +syn match objcClass /NSOrderedSet<.*>\s*\*/me=s+12,me=s+12 contains=objcTypeInfoParams syn match objcClass /NSMutableOrderedSet\s*\*/me=s+19,me=s+19 +syn match objcClass /NSMutableOrderedSet<.*>\s*\*/me=s+19,me=s+19 """"""""""""""""""" " NSPathUtilities.h syn keyword objcEnum NSSearchPathDirectory @@ -238,9 +282,15 @@ syn keyword objcEnum NSSearchPathDomainMask syn keyword objcEnumValue NSUserDomainMask NSLocalDomainMask NSNetworkDomainMask NSSystemDomainMask NSAllDomainsMask " NSFileManger.h syn match objcClass /NSFileManager\s*\*/me=s+13,he=s+13 -syn match objcClass /NSDirectoryEnumerator\s*\*/me=s+21,he=s+21 +syn match objcClass /NSDirectoryEnumerator\s*\*/me=s+21,he=s+21 contains=objcTypeInfoParams +syn match objcClass /NSDirectoryEnumerator<.*>\s*\*/me=s+21,he=s+21 syn keyword objcEnum NSVolumeEnumerationOptions syn keyword objcEnumValue NSVolumeEnumerationSkipHiddenVolumes NSVolumeEnumerationProduceFileReferenceURLs +syn keyword objcEnum NSURLRelationship +syn keyword objcEnumValue NSURLRelationshipContains NSURLRelationshipSame NSURLRelationshipOther +syn keyword objcEnum NSFileManagerUnmountOptions +syn keyword objcEnumValue NSFileManagerUnmountAllPartitionsAndEjectDisk NSFileManagerUnmountWithoutUI +syn keyword objcConstVar NSFileManagerUnmountDissentingProcessIdentifierErrorKey syn keyword objcEnum NSDirectoryEnumerationOptions syn keyword objcEnumValue NSDirectoryEnumerationSkipsSubdirectoryDescendants NSDirectoryEnumerationSkipsPackageDescendants NSDirectoryEnumerationSkipsHiddenFiles syn keyword objcEnum NSFileManagerItemReplacementOptions @@ -261,10 +311,12 @@ syn keyword objcNotificationValue NSCurrentLocaleDidChangeNotification syn keyword objcConstVar NSLocaleIdentifier NSLocaleLanguageCode NSLocaleCountryCode NSLocaleScriptCode NSLocaleVariantCode NSLocaleExemplarCharacterSet NSLocaleCalendar NSLocaleCollationIdentifier NSLocaleUsesMetricSystem NSLocaleMeasurementSystem NSLocaleDecimalSeparator NSLocaleGroupingSeparator NSLocaleCurrencySymbol NSLocaleCurrencyCode NSLocaleCollatorIdentifier NSLocaleQuotationBeginDelimiterKey NSLocaleQuotationEndDelimiterKey NSLocaleAlternateQuotationBeginDelimiterKey NSLocaleAlternateQuotationEndDelimiterKey NSGregorianCalendar NSBuddhistCalendar NSChineseCalendar NSHebrewCalendar NSIslamicCalendar NSIslamicCivilCalendar NSJapaneseCalendar NSRepublicOfChinaCalendar NSPersianCalendar NSIndianCalendar NSISO8601Calendar " NSFormatter.h syn match objcClass /NSFormatter\s*\*/me=s+11,he=s+11 +syn keyword objcEnum NSFormattingContext NSFormattingUnitStyle +syn keyword objcEnumValue NSFormattingContextUnknown NSFormattingContextDynamic NSFormattingContextStandalone NSFormattingContextListItem NSFormattingContextBeginningOfSentence NSFormattingContextMiddleOfSentence NSFormattingUnitStyleShort NSFormattingUnitStyleMedium NSFormattingUnitStyleLong " NSNumberFormatter.h syn match objcClass /NSNumberFormatter\s*\*/me=s+17,he=s+17 syn keyword objcEnum NSNumberFormatterStyle -syn keyword objcEnumValue NSNumberFormatterNoStyle NSNumberFormatterDecimalStyle NSNumberFormatterCurrencyStyle NSNumberFormatterPercentStyle NSNumberFormatterScientificStyle NSNumberFormatterSpellOutStyle +syn keyword objcEnumValue NSNumberFormatterNoStyle NSNumberFormatterDecimalStyle NSNumberFormatterCurrencyStyle NSNumberFormatterPercentStyle NSNumberFormatterScientificStyle NSNumberFormatterSpellOutStyle NSNumberFormatterOrdinalStyle NSNumberFormatterCurrencyISOCodeStyle NSNumberFormatterCurrencyPluralStyle NSNumberFormatterCurrencyAccountingStyle syn keyword objcEnum NSNumberFormatterBehavior syn keyword objcEnumValue NSNumberFormatterBehaviorDefault NSNumberFormatterBehavior10_0 NSNumberFormatterBehavior10_4 syn keyword objcEnum NSNumberFormatterPadPosition @@ -279,10 +331,15 @@ syn keyword objcEnum NSDateFormatterBehavior syn keyword objcEnumValue NSDateFormatterBehaviorDefault NSDateFormatterBehavior10_0 NSDateFormatterBehavior10_4 " NSCalendar.h syn match objcClass /NSCalendar\s*\*/me=s+10,he=s+10 +syn keyword objcConstVar NSCalendarIdentifierGregorian NSCalendarIdentifierBuddhist NSCalendarIdentifierChinese NSCalendarIdentifierCoptic NSCalendarIdentifierEthiopicAmeteMihret NSCalendarIdentifierEthiopicAmeteAlem NSCalendarIdentifierHebrew NSCalendarIdentifierISO8601 NSCalendarIdentifierIndian NSCalendarIdentifierIslamic NSCalendarIdentifierIslamicCivil NSCalendarIdentifierJapanese NSCalendarIdentifierPersian NSCalendarIdentifierRepublicOfChina NSCalendarIdentifierIslamicTabular NSCalendarIdentifierIslamicUmmAlQura syn keyword objcEnum NSCalendarUnit +syn keyword objcEnumValue NSCalendarUnitEra NSCalendarUnitYear NSCalendarUnitMonth NSCalendarUnitDay NSCalendarUnitHour NSCalendarUnitMinute NSCalendarUnitSecond NSCalendarUnitWeekday NSCalendarUnitWeekdayOrdinal NSCalendarUnitQuarter NSCalendarUnitWeekOfMonth NSCalendarUnitWeekOfYear NSCalendarUnitYearForWeekOfYear NSCalendarUnitNanosecond NSCalendarUnitCalendar NSCalendarUnitTimeZone syn keyword objcEnumValue NSEraCalendarUnit NSYearCalendarUnit NSMonthCalendarUnit NSDayCalendarUnit NSHourCalendarUnit NSMinuteCalendarUnit NSSecondCalendarUnit NSWeekCalendarUnit NSWeekdayCalendarUnit NSWeekdayOrdinalCalendarUnit NSQuarterCalendarUnit NSWeekOfMonthCalendarUnit NSWeekOfYearCalendarUnit NSYearForWeekOfYearCalendarUnit NSCalendarCalendarUnit NSTimeZoneCalendarUnit -syn keyword objcEnumValue NSWrapCalendarComponents NSUndefinedDateComponent +syn keyword objcEnumValue NSWrapCalendarComponents NSUndefinedDateComponent NSDateComponentUndefined syn match objcClass /NSDateComponents\s*\*/me=s+16,he=s+16 +syn keyword objcEnum NSCalendarOptions +syn keyword objcEnumValue NSCalendarWrapComponents NSCalendarMatchStrictly NSCalendarSearchBackwards NSCalendarMatchPreviousTimePreservingSmallerUnits NSCalendarMatchNextTimePreservingSmallerUnits NSCalendarMatchNextTime NSCalendarMatchFirst NSCalendarMatchLast +syn keyword objcConstVar NSCalendarDayChangedNotification " NSTimeZone.h syn match objcClass /NSTimeZone\s*\*/me=s+10,he=s+10 syn keyword objcEnum NSTimeZoneNameStyle @@ -299,6 +356,7 @@ syn keyword objcExceptionValue NSInconsistentArchiveException syn match objcClass /NSKeyedArchiver\s*\*/me=s+15,he=s+15 syn match objcClass /NSKeyedUnarchiver\s*\*/me=s+17,he=s+17 syn keyword objcExceptionValue NSInvalidArchiveOperationException NSInvalidUnarchiveOperationException +syn keyword objcConstVar NSKeyedArchiveRootObjectKey """""""""""""""""" " NSPropertyList.h syn keyword objcEnum NSPropertyListMutabilityOptions @@ -313,11 +371,16 @@ syn keyword objcNotificationValue NSUserDefaultsDidChangeNotification " NSBundle.h syn match objcClass /NSBundle\s*\*/me=s+8,he=s+8 syn keyword objcEnumValue NSBundleExecutableArchitectureI386 NSBundleExecutableArchitecturePPC NSBundleExecutableArchitectureX86_64 NSBundleExecutableArchitecturePPC64 -syn keyword objcNotificationValue NSBundleDidLoadNotification NSLoadedClasses +syn keyword objcNotificationValue NSBundleDidLoadNotification NSLoadedClasses NSBundleResourceRequestLowDiskSpaceNotification +syn keyword objcConstVar NSBundleResourceRequestLoadingPriorityUrgent """"""""""""""""" " NSProcessInfo.h syn match objcClass /NSProcessInfo\s*\*/me=s+13,he=s+13 syn keyword objcEnumValue NSWindowsNTOperatingSystem NSWindows95OperatingSystem NSSolarisOperatingSystem NSHPUXOperatingSystem NSMACHOperatingSystem NSSunOSOperatingSystem NSOSF1OperatingSystem +syn keyword objcType NSOperatingSystemVersion +syn keyword objcEnum NSActivityOptions NSProcessInfoThermalState +syn keyword objcEnumValue NSActivityIdleDisplaySleepDisabled NSActivityIdleSystemSleepDisabled NSActivitySuddenTerminationDisabled NSActivityAutomaticTerminationDisabled NSActivityUserInitiated NSActivityUserInitiatedAllowingIdleSystemSleep NSActivityBackground NSActivityLatencyCritical NSProcessInfoThermalStateNominal NSProcessInfoThermalStateFair NSProcessInfoThermalStateSerious NSProcessInfoThermalStateCritical +syn keyword objcNotificationValue NSProcessInfoThermalStateDidChangeNotification NSProcessInfoPowerStateDidChangeNotification " NSTask.h syn match objcClass /NSTask\s*\*/me=s+6,he=s+6 syn keyword objcEnum NSTaskTerminationReason @@ -352,6 +415,7 @@ syn match objcClass /NSPort\s*\*/me=s+6,he=s+6 syn keyword objcType NSSocketNativeHandle syn keyword objcNotificationValue NSPortDidBecomeInvalidNotification syn match objcClass /NSMachPort\s*\*/me=s+10,he=s+10 +syn keyword objcEnum NSMachPortOptions syn keyword objcEnumValue NSMachPortDeallocateNone NSMachPortDeallocateSendRight NSMachPortDeallocateReceiveRight syn match objcClass /NSMessagePort\s*\*/me=s+13,he=s+13 syn match objcClass /NSSocketPort\s*\*/me=s+12,he=s+12 @@ -386,6 +450,31 @@ syn match objcClass /NSProxy\s*\*/me=s+7,he=s+7 " NSObject.h syn match objcClass /NSObject\s*\*/me=s+8,he=s+8 + +" NSCache.h +syn match objcClass /NSCache\s*\*/me=s+7,he=s+7 +syn match objcClass /NSCache<.*>\s*\*/me=s+7,he=s+7 contains=objcTypeInfoParams +" NSHashTable.h +syn match objcClass /NSHashTable\s*\*/me=s+11,he=s+11 +syn match objcClass /NSHashTable<.*>\s*\*/me=s+11,he=s+11 contains=objcTypeInfoParams +syn keyword objcConstVar NSHashTableStrongMemory NSHashTableZeroingWeakMemory NSHashTableCopyIn NSHashTableObjectPointerPersonality NSHashTableWeakMemory +syn keyword objcType NSHashTableOptions NSHashEnumerator NSHashTableCallBacks +syn keyword objcConstVar NSIntegerHashCallBacks NSNonOwnedPointerHashCallBacks NSNonRetainedObjectHashCallBacks NSObjectHashCallBacks NSOwnedObjectIdentityHashCallBacks NSOwnedPointerHashCallBacks NSPointerToStructHashCallBacks NSOwnedObjectIdentityHashCallBacks NSOwnedObjectIdentityHashCallBacks NSIntHashCallBacks +" NSMapTable.h +syn match objcClass /NSMapTable\s*\*/me=s+10,he=s+10 +syn match objcClass /NSMapTable<.*>\s*\*/me=s+10,he=s+10 contains=objcTypeInfoParams +syn keyword objcConstVar NSPointerToStructHashCallBacks NSPointerToStructHashCallBacks NSPointerToStructHashCallBacks NSPointerToStructHashCallBacks NSPointerToStructHashCallBacks +syn keyword objcConstVar NSMapTableStrongMemory NSMapTableZeroingWeakMemory NSMapTableCopyIn NSMapTableObjectPointerPersonality NSMapTableWeakMemory +syn keyword objcType NSMapTableOptions NSMapEnumerator NSMapTableKeyCallBacks NSMapTableValueCallBacks +syn keyword objcMacro NSNotAnIntMapKey NSNotAnIntegerMapKey NSNotAPointerMapKey +syn keyword objcConstVar NSIntegerMapKeyCallBacks NSNonOwnedPointerMapKeyCallBacks NSNonOwnedPointerOrNullMapKeyCallBacks NSNonRetainedObjectMapKeyCallBacks NSObjectMapKeyCallBacks NSOwnedPointerMapKeyCallBacks NSIntMapKeyCallBacks NSIntegerMapValueCallBacks NSNonOwnedPointerMapValueCallBacks NSObjectMapValueCallBacks NSNonRetainedObjectMapValueCallBacks NSOwnedPointerMapValueCallBacks NSIntMapValueCallBacks + +" NSPointerFunctions.h +syn match objcClass /NSPointerFunctions\s*\*/me=s+18,he=s+18 +syn keyword objcEnum NSPointerFunctionsOptions +syn keyword objcEnumValue NSPointerFunctionsStrongMemory NSPointerFunctionsZeroingWeakMemory NSPointerFunctionsOpaqueMemory NSPointerFunctionsMallocMemory NSPointerFunctionsMachVirtualMemory NSPointerFunctionsWeakMemory NSPointerFunctionsObjectPersonality NSPointerFunctionsOpaquePersonality NSPointerFunctionsObjectPointerPersonality NSPointerFunctionsCStringPersonality NSPointerFunctionsStructPersonality NSPointerFunctionsIntegerPersonality NSPointerFunctionsCopyIn + + """ Default Highlighting hi def link objcPreProcMacro cConstant hi def link objcPrincipalType cType @@ -408,6 +497,7 @@ hi def link objcBlocksQualifier cStorageClass hi def link objcObjectLifetimeQualifier cStorageClass hi def link objcTollFreeBridgeQualifier cStorageClass hi def link objcRemoteMessagingQualifier cStorageClass +hi def link objcStorageClass cStorageClass hi def link objcFastEnumKeyword cStatement hi def link objcLiteralSyntaxNumber cNumber hi def link objcLiteralSyntaxChar cCharacter @@ -418,16 +508,22 @@ hi def link objcDeclPropAccessorType cConstant hi def link objcDeclPropAssignSemantics cConstant hi def link objcDeclPropAtomicity cConstant hi def link objcDeclPropARC cConstant +hi def link objcDeclPropNullable cConstant +hi def link objcDeclPropNonnull cConstant +hi def link objcDeclPropNullUnspecified cConstant +hi def link objcDeclProcNullResettable cConstant hi def link objcInstanceMethod Function hi def link objcClassMethod Function hi def link objcType cType hi def link objcClass cType +hi def link objcTypeSpecifier cType hi def link objcMacro cConstant hi def link objcEnum cType hi def link objcEnumValue cConstant hi def link objcExceptionValue cConstant hi def link objcNotificationValue cConstant hi def link objcConstVar cConstant +hi def link objcTypeInfoParams Identifier """ Final step let b:current_syntax = "objc" diff --git a/runtime/syntax/pfmain.vim b/runtime/syntax/pfmain.vim index a0904457a2..32ed6975c0 100644 --- a/runtime/syntax/pfmain.vim +++ b/runtime/syntax/pfmain.vim @@ -1,11 +1,11 @@ " Vim syntax file " Language: Postfix main.cf configuration " Maintainer: KELEMEN Peter <Peter dot Kelemen at cern dot ch> -" Last Update: Hong Xu -" Last Change: 2011 May 14 +" Last Updates: Anton Shestakov, Hong Xu +" Last Change: 2015 Feb 10 " Version: 0.40 " URL: http://cern.ch/fuji/vim/syntax/pfmain.vim -" Comment: Based on Postfix 2.9 defaults. +" Comment: Based on Postfix 2.12/3.0 postconf.5.html. if version < 600 syntax clear @@ -41,6 +41,7 @@ syntax keyword pfmainConf address_verify_relayhost syntax keyword pfmainConf address_verify_sender syntax keyword pfmainConf address_verify_sender_dependent_default_transport_maps syntax keyword pfmainConf address_verify_sender_dependent_relayhost_maps +syntax keyword pfmainConf address_verify_sender_ttl syntax keyword pfmainConf address_verify_service_name syntax keyword pfmainConf address_verify_transport_maps syntax keyword pfmainConf address_verify_virtual_transport @@ -52,6 +53,7 @@ syntax keyword pfmainConf allow_min_user syntax keyword pfmainConf allow_percent_hack syntax keyword pfmainConf allow_untrusted_routing syntax keyword pfmainConf alternate_config_directories +syntax keyword pfmainConf always_add_missing_headers syntax keyword pfmainConf always_bcc syntax keyword pfmainConf anvil_rate_time_unit syntax keyword pfmainConf anvil_status_update_time @@ -61,6 +63,7 @@ syntax keyword pfmainConf application_event_drain_time syntax keyword pfmainConf authorized_flush_users syntax keyword pfmainConf authorized_mailq_users syntax keyword pfmainConf authorized_submit_users +syntax keyword pfmainConf authorized_verp_clients syntax keyword pfmainConf backwards_bounce_logfile_compatibility syntax keyword pfmainConf berkeley_db_create_buffer_size syntax keyword pfmainConf berkeley_db_read_buffer_size @@ -81,7 +84,9 @@ syntax keyword pfmainConf command_directory syntax keyword pfmainConf command_execution_directory syntax keyword pfmainConf command_expansion_filter syntax keyword pfmainConf command_time_limit +syntax keyword pfmainConf compatibility_level syntax keyword pfmainConf config_directory +syntax keyword pfmainConf confirm_delay_cleared syntax keyword pfmainConf connection_cache_protocol_timeout syntax keyword pfmainConf connection_cache_service_name syntax keyword pfmainConf connection_cache_status_update_time @@ -89,14 +94,17 @@ syntax keyword pfmainConf connection_cache_ttl_limit syntax keyword pfmainConf content_filter syntax keyword pfmainConf cyrus_sasl_config_path syntax keyword pfmainConf daemon_directory +syntax keyword pfmainConf daemon_table_open_error_is_fatal syntax keyword pfmainConf daemon_timeout syntax keyword pfmainConf data_directory syntax keyword pfmainConf debug_peer_level syntax keyword pfmainConf debug_peer_list +syntax keyword pfmainConf debugger_command syntax keyword pfmainConf default_database_type syntax keyword pfmainConf default_delivery_slot_cost syntax keyword pfmainConf default_delivery_slot_discount syntax keyword pfmainConf default_delivery_slot_loan +syntax keyword pfmainConf default_delivery_status_filter syntax keyword pfmainConf default_destination_concurrency_failed_cohort_limit syntax keyword pfmainConf default_destination_concurrency_limit syntax keyword pfmainConf default_destination_concurrency_negative_feedback @@ -137,6 +145,7 @@ syntax keyword pfmainConf duplicate_filter_limit syntax keyword pfmainConf empty_address_default_transport_maps_lookup_key syntax keyword pfmainConf empty_address_recipient syntax keyword pfmainConf empty_address_relayhost_maps_lookup_key +syntax keyword pfmainConf enable_errors_to syntax keyword pfmainConf enable_long_queue_ids syntax keyword pfmainConf enable_original_recipient syntax keyword pfmainConf error_notice_recipient @@ -144,6 +153,8 @@ syntax keyword pfmainConf error_service_name syntax keyword pfmainConf execution_directory_expansion_filter syntax keyword pfmainConf expand_owner_alias syntax keyword pfmainConf export_environment +syntax keyword pfmainConf extract_recipient_limit +syntax keyword pfmainConf fallback_relay syntax keyword pfmainConf fallback_transport syntax keyword pfmainConf fallback_transport_maps syntax keyword pfmainConf fast_flush_domains @@ -177,25 +188,33 @@ syntax keyword pfmainConf ipc_idle syntax keyword pfmainConf ipc_timeout syntax keyword pfmainConf ipc_ttl syntax keyword pfmainConf line_length_limit +syntax keyword pfmainConf lmdb_map_size syntax keyword pfmainConf lmtp_address_preference +syntax keyword pfmainConf lmtp_address_verify_target +syntax keyword pfmainConf lmtp_assume_final syntax keyword pfmainConf lmtp_bind_address syntax keyword pfmainConf lmtp_bind_address6 syntax keyword pfmainConf lmtp_body_checks +syntax keyword pfmainConf lmtp_cache_connection syntax keyword pfmainConf lmtp_cname_overrides_servername syntax keyword pfmainConf lmtp_connect_timeout syntax keyword pfmainConf lmtp_connection_cache_destinations syntax keyword pfmainConf lmtp_connection_cache_on_demand syntax keyword pfmainConf lmtp_connection_cache_time_limit +syntax keyword pfmainConf lmtp_connection_reuse_count_limit syntax keyword pfmainConf lmtp_connection_reuse_time_limit syntax keyword pfmainConf lmtp_data_done_timeout syntax keyword pfmainConf lmtp_data_init_timeout syntax keyword pfmainConf lmtp_data_xfer_timeout syntax keyword pfmainConf lmtp_defer_if_no_mx_address_found +syntax keyword pfmainConf lmtp_delivery_status_filter syntax keyword pfmainConf lmtp_destination_concurrency_limit syntax keyword pfmainConf lmtp_destination_recipient_limit syntax keyword pfmainConf lmtp_discard_lhlo_keyword_address_maps syntax keyword pfmainConf lmtp_discard_lhlo_keywords +syntax keyword pfmainConf lmtp_dns_reply_filter syntax keyword pfmainConf lmtp_dns_resolver_options +syntax keyword pfmainConf lmtp_dns_support_level syntax keyword pfmainConf lmtp_enforce_tls syntax keyword pfmainConf lmtp_generic_maps syntax keyword pfmainConf lmtp_header_checks @@ -230,9 +249,11 @@ syntax keyword pfmainConf lmtp_sasl_security_options syntax keyword pfmainConf lmtp_sasl_tls_security_options syntax keyword pfmainConf lmtp_sasl_tls_verified_security_options syntax keyword pfmainConf lmtp_sasl_type +syntax keyword pfmainConf lmtp_send_dummy_mail_auth syntax keyword pfmainConf lmtp_send_xforward_command syntax keyword pfmainConf lmtp_sender_dependent_authentication syntax keyword pfmainConf lmtp_skip_5xx_greeting +syntax keyword pfmainConf lmtp_skip_quit_response syntax keyword pfmainConf lmtp_starttls_timeout syntax keyword pfmainConf lmtp_tcp_port syntax keyword pfmainConf lmtp_tls_CAfile @@ -248,6 +269,7 @@ syntax keyword pfmainConf lmtp_tls_enforce_peername syntax keyword pfmainConf lmtp_tls_exclude_ciphers syntax keyword pfmainConf lmtp_tls_fingerprint_cert_match syntax keyword pfmainConf lmtp_tls_fingerprint_digest +syntax keyword pfmainConf lmtp_tls_force_insecure_host_tlsa_lookup syntax keyword pfmainConf lmtp_tls_key_file syntax keyword pfmainConf lmtp_tls_loglevel syntax keyword pfmainConf lmtp_tls_mandatory_ciphers @@ -262,10 +284,12 @@ syntax keyword pfmainConf lmtp_tls_secure_cert_match syntax keyword pfmainConf lmtp_tls_security_level syntax keyword pfmainConf lmtp_tls_session_cache_database syntax keyword pfmainConf lmtp_tls_session_cache_timeout +syntax keyword pfmainConf lmtp_tls_trust_anchor_file syntax keyword pfmainConf lmtp_tls_verify_cert_match syntax keyword pfmainConf lmtp_use_tls syntax keyword pfmainConf lmtp_xforward_timeout syntax keyword pfmainConf local_command_shell +syntax keyword pfmainConf local_delivery_status_filter syntax keyword pfmainConf local_destination_concurrency_limit syntax keyword pfmainConf local_destination_recipient_limit syntax keyword pfmainConf local_header_rewrite_clients @@ -287,17 +311,19 @@ syntax keyword pfmainConf mailq_path syntax keyword pfmainConf manpage_directory syntax keyword pfmainConf maps_rbl_domains syntax keyword pfmainConf maps_rbl_reject_code -syntax keyword pfmainConf master_service_disable syntax keyword pfmainConf masquerade_classes syntax keyword pfmainConf masquerade_domains syntax keyword pfmainConf masquerade_exceptions +syntax keyword pfmainConf master_service_disable syntax keyword pfmainConf max_idle syntax keyword pfmainConf max_use syntax keyword pfmainConf maximal_backoff_time syntax keyword pfmainConf maximal_queue_lifetime +syntax keyword pfmainConf message_drop_headers syntax keyword pfmainConf message_reject_characters syntax keyword pfmainConf message_size_limit syntax keyword pfmainConf message_strip_characters +syntax keyword pfmainConf meta_directory syntax keyword pfmainConf milter_command_timeout syntax keyword pfmainConf milter_connect_macros syntax keyword pfmainConf milter_connect_timeout @@ -335,10 +361,12 @@ syntax keyword pfmainConf newaliases_path syntax keyword pfmainConf non_fqdn_reject_code syntax keyword pfmainConf non_smtpd_milters syntax keyword pfmainConf notify_classes +syntax keyword pfmainConf nullmx_reject_code syntax keyword pfmainConf owner_request_special syntax keyword pfmainConf parent_domain_matches_subdomains syntax keyword pfmainConf permit_mx_backup_networks syntax keyword pfmainConf pickup_service_name +syntax keyword pfmainConf pipe_delivery_status_filter syntax keyword pfmainConf plaintext_reject_code syntax keyword pfmainConf postmulti_control_commands syntax keyword pfmainConf postmulti_start_commands @@ -362,7 +390,9 @@ syntax keyword pfmainConf postscreen_dnsbl_action syntax keyword pfmainConf postscreen_dnsbl_reply_map syntax keyword pfmainConf postscreen_dnsbl_sites syntax keyword pfmainConf postscreen_dnsbl_threshold +syntax keyword pfmainConf postscreen_dnsbl_timeout syntax keyword pfmainConf postscreen_dnsbl_ttl +syntax keyword pfmainConf postscreen_dnsbl_whitelist_threshold syntax keyword pfmainConf postscreen_enforce_tls syntax keyword pfmainConf postscreen_expansion_filter syntax keyword pfmainConf postscreen_forbidden_commands @@ -381,6 +411,8 @@ syntax keyword pfmainConf postscreen_post_queue_limit syntax keyword pfmainConf postscreen_pre_queue_limit syntax keyword pfmainConf postscreen_reject_footer syntax keyword pfmainConf postscreen_tls_security_level +syntax keyword pfmainConf postscreen_upstream_proxy_protocol +syntax keyword pfmainConf postscreen_upstream_proxy_timeout syntax keyword pfmainConf postscreen_use_tls syntax keyword pfmainConf postscreen_watchdog_timeout syntax keyword pfmainConf postscreen_whitelist_interfaces @@ -395,7 +427,9 @@ syntax keyword pfmainConf proxy_write_maps syntax keyword pfmainConf proxymap_service_name syntax keyword pfmainConf proxywrite_service_name syntax keyword pfmainConf qmgr_clog_warn_time +syntax keyword pfmainConf qmgr_daemon_timeout syntax keyword pfmainConf qmgr_fudge_factor +syntax keyword pfmainConf qmgr_ipc_timeout syntax keyword pfmainConf qmgr_message_active_limit syntax keyword pfmainConf qmgr_message_recipient_limit syntax keyword pfmainConf qmgr_message_recipient_minimum @@ -435,17 +469,21 @@ syntax keyword pfmainConf resolve_numeric_domain syntax keyword pfmainConf rewrite_service_name syntax keyword pfmainConf sample_directory syntax keyword pfmainConf send_cyrus_sasl_authzid +syntax keyword pfmainConf sender_based_routing syntax keyword pfmainConf sender_bcc_maps syntax keyword pfmainConf sender_canonical_classes syntax keyword pfmainConf sender_canonical_maps syntax keyword pfmainConf sender_dependent_default_transport_maps syntax keyword pfmainConf sender_dependent_relayhost_maps +syntax keyword pfmainConf sendmail_fix_line_endings syntax keyword pfmainConf sendmail_path syntax keyword pfmainConf service_throttle_time syntax keyword pfmainConf setgid_group +syntax keyword pfmainConf shlib_directory syntax keyword pfmainConf show_user_unknown_table_name syntax keyword pfmainConf showq_service_name syntax keyword pfmainConf smtp_address_preference +syntax keyword pfmainConf smtp_address_verify_target syntax keyword pfmainConf smtp_always_send_ehlo syntax keyword pfmainConf smtp_bind_address syntax keyword pfmainConf smtp_bind_address6 @@ -455,16 +493,20 @@ syntax keyword pfmainConf smtp_connect_timeout syntax keyword pfmainConf smtp_connection_cache_destinations syntax keyword pfmainConf smtp_connection_cache_on_demand syntax keyword pfmainConf smtp_connection_cache_time_limit +syntax keyword pfmainConf smtp_connection_reuse_count_limit syntax keyword pfmainConf smtp_connection_reuse_time_limit syntax keyword pfmainConf smtp_data_done_timeout syntax keyword pfmainConf smtp_data_init_timeout syntax keyword pfmainConf smtp_data_xfer_timeout syntax keyword pfmainConf smtp_defer_if_no_mx_address_found +syntax keyword pfmainConf smtp_delivery_status_filter syntax keyword pfmainConf smtp_destination_concurrency_limit syntax keyword pfmainConf smtp_destination_recipient_limit syntax keyword pfmainConf smtp_discard_ehlo_keyword_address_maps syntax keyword pfmainConf smtp_discard_ehlo_keywords +syntax keyword pfmainConf smtp_dns_reply_filter syntax keyword pfmainConf smtp_dns_resolver_options +syntax keyword pfmainConf smtp_dns_support_level syntax keyword pfmainConf smtp_enforce_tls syntax keyword pfmainConf smtp_fallback_relay syntax keyword pfmainConf smtp_generic_maps @@ -479,6 +521,7 @@ syntax keyword pfmainConf smtp_mx_address_limit syntax keyword pfmainConf smtp_mx_session_limit syntax keyword pfmainConf smtp_nested_header_checks syntax keyword pfmainConf smtp_never_send_ehlo +syntax keyword pfmainConf smtp_per_record_deadline syntax keyword pfmainConf smtp_pix_workaround_delay_time syntax keyword pfmainConf smtp_pix_workaround_maps syntax keyword pfmainConf smtp_pix_workaround_threshold_time @@ -500,8 +543,10 @@ syntax keyword pfmainConf smtp_sasl_security_options syntax keyword pfmainConf smtp_sasl_tls_security_options syntax keyword pfmainConf smtp_sasl_tls_verified_security_options syntax keyword pfmainConf smtp_sasl_type +syntax keyword pfmainConf smtp_send_dummy_mail_auth syntax keyword pfmainConf smtp_send_xforward_command syntax keyword pfmainConf smtp_sender_dependent_authentication +syntax keyword pfmainConf smtp_skip_4xx_greeting syntax keyword pfmainConf smtp_skip_5xx_greeting syntax keyword pfmainConf smtp_skip_quit_response syntax keyword pfmainConf smtp_starttls_timeout @@ -519,6 +564,7 @@ syntax keyword pfmainConf smtp_tls_enforce_peername syntax keyword pfmainConf smtp_tls_exclude_ciphers syntax keyword pfmainConf smtp_tls_fingerprint_cert_match syntax keyword pfmainConf smtp_tls_fingerprint_digest +syntax keyword pfmainConf smtp_tls_force_insecure_host_tlsa_lookup syntax keyword pfmainConf smtp_tls_key_file syntax keyword pfmainConf smtp_tls_loglevel syntax keyword pfmainConf smtp_tls_mandatory_ciphers @@ -533,7 +579,9 @@ syntax keyword pfmainConf smtp_tls_secure_cert_match syntax keyword pfmainConf smtp_tls_security_level syntax keyword pfmainConf smtp_tls_session_cache_database syntax keyword pfmainConf smtp_tls_session_cache_timeout +syntax keyword pfmainConf smtp_tls_trust_anchor_file syntax keyword pfmainConf smtp_tls_verify_cert_match +syntax keyword pfmainConf smtp_tls_wrappermode syntax keyword pfmainConf smtp_use_tls syntax keyword pfmainConf smtp_xforward_timeout syntax keyword pfmainConf smtpd_authorized_verp_clients @@ -554,6 +602,7 @@ syntax keyword pfmainConf smtpd_delay_open_until_valid_rcpt syntax keyword pfmainConf smtpd_delay_reject syntax keyword pfmainConf smtpd_discard_ehlo_keyword_address_maps syntax keyword pfmainConf smtpd_discard_ehlo_keywords +syntax keyword pfmainConf smtpd_dns_reply_filter syntax keyword pfmainConf smtpd_end_of_data_restrictions syntax keyword pfmainConf smtpd_enforce_tls syntax keyword pfmainConf smtpd_error_sleep_time @@ -565,14 +614,19 @@ syntax keyword pfmainConf smtpd_helo_required syntax keyword pfmainConf smtpd_helo_restrictions syntax keyword pfmainConf smtpd_history_flush_threshold syntax keyword pfmainConf smtpd_junk_command_limit +syntax keyword pfmainConf smtpd_log_access_permit_actions syntax keyword pfmainConf smtpd_milters syntax keyword pfmainConf smtpd_noop_commands syntax keyword pfmainConf smtpd_null_access_lookup_key syntax keyword pfmainConf smtpd_peername_lookup syntax keyword pfmainConf smtpd_per_record_deadline +syntax keyword pfmainConf smtpd_policy_service_default_action syntax keyword pfmainConf smtpd_policy_service_max_idle syntax keyword pfmainConf smtpd_policy_service_max_ttl +syntax keyword pfmainConf smtpd_policy_service_request_limit +syntax keyword pfmainConf smtpd_policy_service_retry_delay syntax keyword pfmainConf smtpd_policy_service_timeout +syntax keyword pfmainConf smtpd_policy_service_try_limit syntax keyword pfmainConf smtpd_proxy_ehlo syntax keyword pfmainConf smtpd_proxy_filter syntax keyword pfmainConf smtpd_proxy_options @@ -583,13 +637,16 @@ syntax keyword pfmainConf smtpd_recipient_restrictions syntax keyword pfmainConf smtpd_reject_footer syntax keyword pfmainConf smtpd_reject_unlisted_recipient syntax keyword pfmainConf smtpd_reject_unlisted_sender +syntax keyword pfmainConf smtpd_relay_restrictions syntax keyword pfmainConf smtpd_restriction_classes +syntax keyword pfmainConf smtpd_sasl_application_name syntax keyword pfmainConf smtpd_sasl_auth_enable syntax keyword pfmainConf smtpd_sasl_authenticated_header syntax keyword pfmainConf smtpd_sasl_exceptions_networks syntax keyword pfmainConf smtpd_sasl_local_domain syntax keyword pfmainConf smtpd_sasl_path syntax keyword pfmainConf smtpd_sasl_security_options +syntax keyword pfmainConf smtpd_sasl_service syntax keyword pfmainConf smtpd_sasl_tls_security_options syntax keyword pfmainConf smtpd_sasl_type syntax keyword pfmainConf smtpd_sender_login_maps @@ -628,21 +685,21 @@ syntax keyword pfmainConf smtpd_tls_security_level syntax keyword pfmainConf smtpd_tls_session_cache_database syntax keyword pfmainConf smtpd_tls_session_cache_timeout syntax keyword pfmainConf smtpd_tls_wrappermode +syntax keyword pfmainConf smtpd_upstream_proxy_protocol +syntax keyword pfmainConf smtpd_upstream_proxy_timeout syntax keyword pfmainConf smtpd_use_tls +syntax keyword pfmainConf smtputf8_autodetect_classes +syntax keyword pfmainConf smtputf8_enable syntax keyword pfmainConf soft_bounce syntax keyword pfmainConf stale_lock_time syntax keyword pfmainConf stress syntax keyword pfmainConf strict_7bit_headers syntax keyword pfmainConf strict_8bitmime syntax keyword pfmainConf strict_8bitmime_body -syntax keyword pfmainConf strict_mime_encoding_domain -syntax keyword pfmainConf strict_rfc821_envelopes -syntax keyword pfmainConf strict_7bit_headers -syntax keyword pfmainConf strict_8bitmime -syntax keyword pfmainConf strict_8bitmime_body syntax keyword pfmainConf strict_mailbox_ownership syntax keyword pfmainConf strict_mime_encoding_domain syntax keyword pfmainConf strict_rfc821_envelopes +syntax keyword pfmainConf strict_smtputf8 syntax keyword pfmainConf sun_mailtool_compatibility syntax keyword pfmainConf swap_bangpath syntax keyword pfmainConf syslog_facility @@ -650,11 +707,15 @@ syntax keyword pfmainConf syslog_name syntax keyword pfmainConf tcp_windowsize syntax keyword pfmainConf tls_append_default_CA syntax keyword pfmainConf tls_daemon_random_bytes +syntax keyword pfmainConf tls_dane_digest_agility +syntax keyword pfmainConf tls_dane_digests +syntax keyword pfmainConf tls_dane_trust_anchor_digest_enable syntax keyword pfmainConf tls_disable_workarounds syntax keyword pfmainConf tls_eecdh_strong_curve syntax keyword pfmainConf tls_eecdh_ultra_curve syntax keyword pfmainConf tls_export_cipherlist syntax keyword pfmainConf tls_high_cipherlist +syntax keyword pfmainConf tls_legacy_public_key_fingerprints syntax keyword pfmainConf tls_low_cipherlist syntax keyword pfmainConf tls_medium_cipherlist syntax keyword pfmainConf tls_null_cipherlist @@ -664,6 +725,10 @@ syntax keyword pfmainConf tls_random_exchange_name syntax keyword pfmainConf tls_random_prng_update_period syntax keyword pfmainConf tls_random_reseed_period syntax keyword pfmainConf tls_random_source +syntax keyword pfmainConf tls_session_ticket_cipher +syntax keyword pfmainConf tls_ssl_options +syntax keyword pfmainConf tls_wildcard_matches_multiple_labels +syntax keyword pfmainConf tlsmgr_service_name syntax keyword pfmainConf tlsproxy_enforce_tls syntax keyword pfmainConf tlsproxy_service_name syntax keyword pfmainConf tlsproxy_tls_CAfile @@ -711,6 +776,7 @@ syntax keyword pfmainConf transport_recipient_limit syntax keyword pfmainConf transport_recipient_refill_delay syntax keyword pfmainConf transport_recipient_refill_limit syntax keyword pfmainConf transport_retry_time +syntax keyword pfmainConf transport_time_limit syntax keyword pfmainConf trigger_timeout syntax keyword pfmainConf undisclosed_recipients_header syntax keyword pfmainConf unknown_address_reject_code @@ -731,10 +797,12 @@ syntax keyword pfmainConf unverified_sender_reject_code syntax keyword pfmainConf unverified_sender_reject_reason syntax keyword pfmainConf unverified_sender_tempfail_action syntax keyword pfmainConf verp_delimiter_filter +syntax keyword pfmainConf virtual_alias_address_length_limit syntax keyword pfmainConf virtual_alias_domains syntax keyword pfmainConf virtual_alias_expansion_limit syntax keyword pfmainConf virtual_alias_maps syntax keyword pfmainConf virtual_alias_recursion_limit +syntax keyword pfmainConf virtual_delivery_status_filter syntax keyword pfmainConf virtual_destination_concurrency_limit syntax keyword pfmainConf virtual_destination_recipient_limit syntax keyword pfmainConf virtual_gid_maps @@ -743,6 +811,7 @@ syntax keyword pfmainConf virtual_mailbox_domains syntax keyword pfmainConf virtual_mailbox_limit syntax keyword pfmainConf virtual_mailbox_lock syntax keyword pfmainConf virtual_mailbox_maps +syntax keyword pfmainConf virtual_maps syntax keyword pfmainConf virtual_minimum_uid syntax keyword pfmainConf virtual_transport syntax keyword pfmainConf virtual_uid_maps @@ -765,6 +834,7 @@ syntax match pfmainRef "$\<address_verify_relayhost\>" syntax match pfmainRef "$\<address_verify_sender\>" syntax match pfmainRef "$\<address_verify_sender_dependent_default_transport_maps\>" syntax match pfmainRef "$\<address_verify_sender_dependent_relayhost_maps\>" +syntax match pfmainRef "$\<address_verify_sender_ttl\>" syntax match pfmainRef "$\<address_verify_service_name\>" syntax match pfmainRef "$\<address_verify_transport_maps\>" syntax match pfmainRef "$\<address_verify_virtual_transport\>" @@ -776,6 +846,7 @@ syntax match pfmainRef "$\<allow_min_user\>" syntax match pfmainRef "$\<allow_percent_hack\>" syntax match pfmainRef "$\<allow_untrusted_routing\>" syntax match pfmainRef "$\<alternate_config_directories\>" +syntax match pfmainRef "$\<always_add_missing_headers\>" syntax match pfmainRef "$\<always_bcc\>" syntax match pfmainRef "$\<anvil_rate_time_unit\>" syntax match pfmainRef "$\<anvil_status_update_time\>" @@ -785,6 +856,7 @@ syntax match pfmainRef "$\<application_event_drain_time\>" syntax match pfmainRef "$\<authorized_flush_users\>" syntax match pfmainRef "$\<authorized_mailq_users\>" syntax match pfmainRef "$\<authorized_submit_users\>" +syntax match pfmainRef "$\<authorized_verp_clients\>" syntax match pfmainRef "$\<backwards_bounce_logfile_compatibility\>" syntax match pfmainRef "$\<berkeley_db_create_buffer_size\>" syntax match pfmainRef "$\<berkeley_db_read_buffer_size\>" @@ -805,7 +877,9 @@ syntax match pfmainRef "$\<command_directory\>" syntax match pfmainRef "$\<command_execution_directory\>" syntax match pfmainRef "$\<command_expansion_filter\>" syntax match pfmainRef "$\<command_time_limit\>" +syntax match pfmainRef "$\<compatibility_level\>" syntax match pfmainRef "$\<config_directory\>" +syntax match pfmainRef "$\<confirm_delay_cleared\>" syntax match pfmainRef "$\<connection_cache_protocol_timeout\>" syntax match pfmainRef "$\<connection_cache_service_name\>" syntax match pfmainRef "$\<connection_cache_status_update_time\>" @@ -813,14 +887,17 @@ syntax match pfmainRef "$\<connection_cache_ttl_limit\>" syntax match pfmainRef "$\<content_filter\>" syntax match pfmainRef "$\<cyrus_sasl_config_path\>" syntax match pfmainRef "$\<daemon_directory\>" +syntax match pfmainRef "$\<daemon_table_open_error_is_fatal\>" syntax match pfmainRef "$\<daemon_timeout\>" syntax match pfmainRef "$\<data_directory\>" syntax match pfmainRef "$\<debug_peer_level\>" syntax match pfmainRef "$\<debug_peer_list\>" +syntax match pfmainRef "$\<debugger_command\>" syntax match pfmainRef "$\<default_database_type\>" syntax match pfmainRef "$\<default_delivery_slot_cost\>" syntax match pfmainRef "$\<default_delivery_slot_discount\>" syntax match pfmainRef "$\<default_delivery_slot_loan\>" +syntax match pfmainRef "$\<default_delivery_status_filter\>" syntax match pfmainRef "$\<default_destination_concurrency_failed_cohort_limit\>" syntax match pfmainRef "$\<default_destination_concurrency_limit\>" syntax match pfmainRef "$\<default_destination_concurrency_negative_feedback\>" @@ -861,6 +938,7 @@ syntax match pfmainRef "$\<duplicate_filter_limit\>" syntax match pfmainRef "$\<empty_address_default_transport_maps_lookup_key\>" syntax match pfmainRef "$\<empty_address_recipient\>" syntax match pfmainRef "$\<empty_address_relayhost_maps_lookup_key\>" +syntax match pfmainRef "$\<enable_errors_to\>" syntax match pfmainRef "$\<enable_long_queue_ids\>" syntax match pfmainRef "$\<enable_original_recipient\>" syntax match pfmainRef "$\<error_notice_recipient\>" @@ -868,6 +946,8 @@ syntax match pfmainRef "$\<error_service_name\>" syntax match pfmainRef "$\<execution_directory_expansion_filter\>" syntax match pfmainRef "$\<expand_owner_alias\>" syntax match pfmainRef "$\<export_environment\>" +syntax match pfmainRef "$\<extract_recipient_limit\>" +syntax match pfmainRef "$\<fallback_relay\>" syntax match pfmainRef "$\<fallback_transport\>" syntax match pfmainRef "$\<fallback_transport_maps\>" syntax match pfmainRef "$\<fast_flush_domains\>" @@ -901,25 +981,33 @@ syntax match pfmainRef "$\<ipc_idle\>" syntax match pfmainRef "$\<ipc_timeout\>" syntax match pfmainRef "$\<ipc_ttl\>" syntax match pfmainRef "$\<line_length_limit\>" +syntax match pfmainRef "$\<lmdb_map_size\>" syntax match pfmainRef "$\<lmtp_address_preference\>" +syntax match pfmainRef "$\<lmtp_address_verify_target\>" +syntax match pfmainRef "$\<lmtp_assume_final\>" syntax match pfmainRef "$\<lmtp_bind_address\>" syntax match pfmainRef "$\<lmtp_bind_address6\>" syntax match pfmainRef "$\<lmtp_body_checks\>" +syntax match pfmainRef "$\<lmtp_cache_connection\>" syntax match pfmainRef "$\<lmtp_cname_overrides_servername\>" syntax match pfmainRef "$\<lmtp_connect_timeout\>" syntax match pfmainRef "$\<lmtp_connection_cache_destinations\>" syntax match pfmainRef "$\<lmtp_connection_cache_on_demand\>" syntax match pfmainRef "$\<lmtp_connection_cache_time_limit\>" +syntax match pfmainRef "$\<lmtp_connection_reuse_count_limit\>" syntax match pfmainRef "$\<lmtp_connection_reuse_time_limit\>" syntax match pfmainRef "$\<lmtp_data_done_timeout\>" syntax match pfmainRef "$\<lmtp_data_init_timeout\>" syntax match pfmainRef "$\<lmtp_data_xfer_timeout\>" syntax match pfmainRef "$\<lmtp_defer_if_no_mx_address_found\>" +syntax match pfmainRef "$\<lmtp_delivery_status_filter\>" syntax match pfmainRef "$\<lmtp_destination_concurrency_limit\>" syntax match pfmainRef "$\<lmtp_destination_recipient_limit\>" syntax match pfmainRef "$\<lmtp_discard_lhlo_keyword_address_maps\>" syntax match pfmainRef "$\<lmtp_discard_lhlo_keywords\>" +syntax match pfmainRef "$\<lmtp_dns_reply_filter\>" syntax match pfmainRef "$\<lmtp_dns_resolver_options\>" +syntax match pfmainRef "$\<lmtp_dns_support_level\>" syntax match pfmainRef "$\<lmtp_enforce_tls\>" syntax match pfmainRef "$\<lmtp_generic_maps\>" syntax match pfmainRef "$\<lmtp_header_checks\>" @@ -954,9 +1042,11 @@ syntax match pfmainRef "$\<lmtp_sasl_security_options\>" syntax match pfmainRef "$\<lmtp_sasl_tls_security_options\>" syntax match pfmainRef "$\<lmtp_sasl_tls_verified_security_options\>" syntax match pfmainRef "$\<lmtp_sasl_type\>" +syntax match pfmainRef "$\<lmtp_send_dummy_mail_auth\>" syntax match pfmainRef "$\<lmtp_send_xforward_command\>" syntax match pfmainRef "$\<lmtp_sender_dependent_authentication\>" syntax match pfmainRef "$\<lmtp_skip_5xx_greeting\>" +syntax match pfmainRef "$\<lmtp_skip_quit_response\>" syntax match pfmainRef "$\<lmtp_starttls_timeout\>" syntax match pfmainRef "$\<lmtp_tcp_port\>" syntax match pfmainRef "$\<lmtp_tls_CAfile\>" @@ -972,6 +1062,7 @@ syntax match pfmainRef "$\<lmtp_tls_enforce_peername\>" syntax match pfmainRef "$\<lmtp_tls_exclude_ciphers\>" syntax match pfmainRef "$\<lmtp_tls_fingerprint_cert_match\>" syntax match pfmainRef "$\<lmtp_tls_fingerprint_digest\>" +syntax match pfmainRef "$\<lmtp_tls_force_insecure_host_tlsa_lookup\>" syntax match pfmainRef "$\<lmtp_tls_key_file\>" syntax match pfmainRef "$\<lmtp_tls_loglevel\>" syntax match pfmainRef "$\<lmtp_tls_mandatory_ciphers\>" @@ -982,11 +1073,16 @@ syntax match pfmainRef "$\<lmtp_tls_per_site\>" syntax match pfmainRef "$\<lmtp_tls_policy_maps\>" syntax match pfmainRef "$\<lmtp_tls_protocols\>" syntax match pfmainRef "$\<lmtp_tls_scert_verifydepth\>" +syntax match pfmainRef "$\<lmtp_tls_secure_cert_match\>" +syntax match pfmainRef "$\<lmtp_tls_security_level\>" +syntax match pfmainRef "$\<lmtp_tls_session_cache_database\>" syntax match pfmainRef "$\<lmtp_tls_session_cache_timeout\>" +syntax match pfmainRef "$\<lmtp_tls_trust_anchor_file\>" syntax match pfmainRef "$\<lmtp_tls_verify_cert_match\>" syntax match pfmainRef "$\<lmtp_use_tls\>" syntax match pfmainRef "$\<lmtp_xforward_timeout\>" syntax match pfmainRef "$\<local_command_shell\>" +syntax match pfmainRef "$\<local_delivery_status_filter\>" syntax match pfmainRef "$\<local_destination_concurrency_limit\>" syntax match pfmainRef "$\<local_destination_recipient_limit\>" syntax match pfmainRef "$\<local_header_rewrite_clients\>" @@ -1008,17 +1104,19 @@ syntax match pfmainRef "$\<mailq_path\>" syntax match pfmainRef "$\<manpage_directory\>" syntax match pfmainRef "$\<maps_rbl_domains\>" syntax match pfmainRef "$\<maps_rbl_reject_code\>" -syntax match pfmainRef "$\<master_service_disable\>" syntax match pfmainRef "$\<masquerade_classes\>" syntax match pfmainRef "$\<masquerade_domains\>" syntax match pfmainRef "$\<masquerade_exceptions\>" +syntax match pfmainRef "$\<master_service_disable\>" syntax match pfmainRef "$\<max_idle\>" syntax match pfmainRef "$\<max_use\>" syntax match pfmainRef "$\<maximal_backoff_time\>" syntax match pfmainRef "$\<maximal_queue_lifetime\>" +syntax match pfmainRef "$\<message_drop_headers\>" syntax match pfmainRef "$\<message_reject_characters\>" syntax match pfmainRef "$\<message_size_limit\>" syntax match pfmainRef "$\<message_strip_characters\>" +syntax match pfmainRef "$\<meta_directory\>" syntax match pfmainRef "$\<milter_command_timeout\>" syntax match pfmainRef "$\<milter_connect_macros\>" syntax match pfmainRef "$\<milter_connect_timeout\>" @@ -1056,10 +1154,12 @@ syntax match pfmainRef "$\<newaliases_path\>" syntax match pfmainRef "$\<non_fqdn_reject_code\>" syntax match pfmainRef "$\<non_smtpd_milters\>" syntax match pfmainRef "$\<notify_classes\>" +syntax match pfmainRef "$\<nullmx_reject_code\>" syntax match pfmainRef "$\<owner_request_special\>" syntax match pfmainRef "$\<parent_domain_matches_subdomains\>" syntax match pfmainRef "$\<permit_mx_backup_networks\>" syntax match pfmainRef "$\<pickup_service_name\>" +syntax match pfmainRef "$\<pipe_delivery_status_filter\>" syntax match pfmainRef "$\<plaintext_reject_code\>" syntax match pfmainRef "$\<postmulti_control_commands\>" syntax match pfmainRef "$\<postmulti_start_commands\>" @@ -1083,7 +1183,9 @@ syntax match pfmainRef "$\<postscreen_dnsbl_action\>" syntax match pfmainRef "$\<postscreen_dnsbl_reply_map\>" syntax match pfmainRef "$\<postscreen_dnsbl_sites\>" syntax match pfmainRef "$\<postscreen_dnsbl_threshold\>" +syntax match pfmainRef "$\<postscreen_dnsbl_timeout\>" syntax match pfmainRef "$\<postscreen_dnsbl_ttl\>" +syntax match pfmainRef "$\<postscreen_dnsbl_whitelist_threshold\>" syntax match pfmainRef "$\<postscreen_enforce_tls\>" syntax match pfmainRef "$\<postscreen_expansion_filter\>" syntax match pfmainRef "$\<postscreen_forbidden_commands\>" @@ -1102,9 +1204,8 @@ syntax match pfmainRef "$\<postscreen_post_queue_limit\>" syntax match pfmainRef "$\<postscreen_pre_queue_limit\>" syntax match pfmainRef "$\<postscreen_reject_footer\>" syntax match pfmainRef "$\<postscreen_tls_security_level\>" -syntax match pfmainRef "$\<lmtp_tls_secure_cert_match\>" -syntax match pfmainRef "$\<lmtp_tls_security_level\>" -syntax match pfmainRef "$\<lmtp_tls_session_cache_database\>" +syntax match pfmainRef "$\<postscreen_upstream_proxy_protocol\>" +syntax match pfmainRef "$\<postscreen_upstream_proxy_timeout\>" syntax match pfmainRef "$\<postscreen_use_tls\>" syntax match pfmainRef "$\<postscreen_watchdog_timeout\>" syntax match pfmainRef "$\<postscreen_whitelist_interfaces\>" @@ -1119,7 +1220,9 @@ syntax match pfmainRef "$\<proxy_write_maps\>" syntax match pfmainRef "$\<proxymap_service_name\>" syntax match pfmainRef "$\<proxywrite_service_name\>" syntax match pfmainRef "$\<qmgr_clog_warn_time\>" +syntax match pfmainRef "$\<qmgr_daemon_timeout\>" syntax match pfmainRef "$\<qmgr_fudge_factor\>" +syntax match pfmainRef "$\<qmgr_ipc_timeout\>" syntax match pfmainRef "$\<qmgr_message_active_limit\>" syntax match pfmainRef "$\<qmgr_message_recipient_limit\>" syntax match pfmainRef "$\<qmgr_message_recipient_minimum\>" @@ -1159,17 +1262,21 @@ syntax match pfmainRef "$\<resolve_numeric_domain\>" syntax match pfmainRef "$\<rewrite_service_name\>" syntax match pfmainRef "$\<sample_directory\>" syntax match pfmainRef "$\<send_cyrus_sasl_authzid\>" +syntax match pfmainRef "$\<sender_based_routing\>" syntax match pfmainRef "$\<sender_bcc_maps\>" syntax match pfmainRef "$\<sender_canonical_classes\>" syntax match pfmainRef "$\<sender_canonical_maps\>" syntax match pfmainRef "$\<sender_dependent_default_transport_maps\>" syntax match pfmainRef "$\<sender_dependent_relayhost_maps\>" +syntax match pfmainRef "$\<sendmail_fix_line_endings\>" syntax match pfmainRef "$\<sendmail_path\>" syntax match pfmainRef "$\<service_throttle_time\>" syntax match pfmainRef "$\<setgid_group\>" +syntax match pfmainRef "$\<shlib_directory\>" syntax match pfmainRef "$\<show_user_unknown_table_name\>" syntax match pfmainRef "$\<showq_service_name\>" syntax match pfmainRef "$\<smtp_address_preference\>" +syntax match pfmainRef "$\<smtp_address_verify_target\>" syntax match pfmainRef "$\<smtp_always_send_ehlo\>" syntax match pfmainRef "$\<smtp_bind_address\>" syntax match pfmainRef "$\<smtp_bind_address6\>" @@ -1179,16 +1286,20 @@ syntax match pfmainRef "$\<smtp_connect_timeout\>" syntax match pfmainRef "$\<smtp_connection_cache_destinations\>" syntax match pfmainRef "$\<smtp_connection_cache_on_demand\>" syntax match pfmainRef "$\<smtp_connection_cache_time_limit\>" +syntax match pfmainRef "$\<smtp_connection_reuse_count_limit\>" syntax match pfmainRef "$\<smtp_connection_reuse_time_limit\>" syntax match pfmainRef "$\<smtp_data_done_timeout\>" syntax match pfmainRef "$\<smtp_data_init_timeout\>" syntax match pfmainRef "$\<smtp_data_xfer_timeout\>" syntax match pfmainRef "$\<smtp_defer_if_no_mx_address_found\>" +syntax match pfmainRef "$\<smtp_delivery_status_filter\>" syntax match pfmainRef "$\<smtp_destination_concurrency_limit\>" syntax match pfmainRef "$\<smtp_destination_recipient_limit\>" syntax match pfmainRef "$\<smtp_discard_ehlo_keyword_address_maps\>" syntax match pfmainRef "$\<smtp_discard_ehlo_keywords\>" +syntax match pfmainRef "$\<smtp_dns_reply_filter\>" syntax match pfmainRef "$\<smtp_dns_resolver_options\>" +syntax match pfmainRef "$\<smtp_dns_support_level\>" syntax match pfmainRef "$\<smtp_enforce_tls\>" syntax match pfmainRef "$\<smtp_fallback_relay\>" syntax match pfmainRef "$\<smtp_generic_maps\>" @@ -1203,6 +1314,7 @@ syntax match pfmainRef "$\<smtp_mx_address_limit\>" syntax match pfmainRef "$\<smtp_mx_session_limit\>" syntax match pfmainRef "$\<smtp_nested_header_checks\>" syntax match pfmainRef "$\<smtp_never_send_ehlo\>" +syntax match pfmainRef "$\<smtp_per_record_deadline\>" syntax match pfmainRef "$\<smtp_pix_workaround_delay_time\>" syntax match pfmainRef "$\<smtp_pix_workaround_maps\>" syntax match pfmainRef "$\<smtp_pix_workaround_threshold_time\>" @@ -1224,8 +1336,10 @@ syntax match pfmainRef "$\<smtp_sasl_security_options\>" syntax match pfmainRef "$\<smtp_sasl_tls_security_options\>" syntax match pfmainRef "$\<smtp_sasl_tls_verified_security_options\>" syntax match pfmainRef "$\<smtp_sasl_type\>" +syntax match pfmainRef "$\<smtp_send_dummy_mail_auth\>" syntax match pfmainRef "$\<smtp_send_xforward_command\>" syntax match pfmainRef "$\<smtp_sender_dependent_authentication\>" +syntax match pfmainRef "$\<smtp_skip_4xx_greeting\>" syntax match pfmainRef "$\<smtp_skip_5xx_greeting\>" syntax match pfmainRef "$\<smtp_skip_quit_response\>" syntax match pfmainRef "$\<smtp_starttls_timeout\>" @@ -1243,6 +1357,7 @@ syntax match pfmainRef "$\<smtp_tls_enforce_peername\>" syntax match pfmainRef "$\<smtp_tls_exclude_ciphers\>" syntax match pfmainRef "$\<smtp_tls_fingerprint_cert_match\>" syntax match pfmainRef "$\<smtp_tls_fingerprint_digest\>" +syntax match pfmainRef "$\<smtp_tls_force_insecure_host_tlsa_lookup\>" syntax match pfmainRef "$\<smtp_tls_key_file\>" syntax match pfmainRef "$\<smtp_tls_loglevel\>" syntax match pfmainRef "$\<smtp_tls_mandatory_ciphers\>" @@ -1257,7 +1372,9 @@ syntax match pfmainRef "$\<smtp_tls_secure_cert_match\>" syntax match pfmainRef "$\<smtp_tls_security_level\>" syntax match pfmainRef "$\<smtp_tls_session_cache_database\>" syntax match pfmainRef "$\<smtp_tls_session_cache_timeout\>" +syntax match pfmainRef "$\<smtp_tls_trust_anchor_file\>" syntax match pfmainRef "$\<smtp_tls_verify_cert_match\>" +syntax match pfmainRef "$\<smtp_tls_wrappermode\>" syntax match pfmainRef "$\<smtp_use_tls\>" syntax match pfmainRef "$\<smtp_xforward_timeout\>" syntax match pfmainRef "$\<smtpd_authorized_verp_clients\>" @@ -1278,6 +1395,7 @@ syntax match pfmainRef "$\<smtpd_delay_open_until_valid_rcpt\>" syntax match pfmainRef "$\<smtpd_delay_reject\>" syntax match pfmainRef "$\<smtpd_discard_ehlo_keyword_address_maps\>" syntax match pfmainRef "$\<smtpd_discard_ehlo_keywords\>" +syntax match pfmainRef "$\<smtpd_dns_reply_filter\>" syntax match pfmainRef "$\<smtpd_end_of_data_restrictions\>" syntax match pfmainRef "$\<smtpd_enforce_tls\>" syntax match pfmainRef "$\<smtpd_error_sleep_time\>" @@ -1289,14 +1407,19 @@ syntax match pfmainRef "$\<smtpd_helo_required\>" syntax match pfmainRef "$\<smtpd_helo_restrictions\>" syntax match pfmainRef "$\<smtpd_history_flush_threshold\>" syntax match pfmainRef "$\<smtpd_junk_command_limit\>" +syntax match pfmainRef "$\<smtpd_log_access_permit_actions\>" syntax match pfmainRef "$\<smtpd_milters\>" syntax match pfmainRef "$\<smtpd_noop_commands\>" syntax match pfmainRef "$\<smtpd_null_access_lookup_key\>" syntax match pfmainRef "$\<smtpd_peername_lookup\>" syntax match pfmainRef "$\<smtpd_per_record_deadline\>" +syntax match pfmainRef "$\<smtpd_policy_service_default_action\>" syntax match pfmainRef "$\<smtpd_policy_service_max_idle\>" syntax match pfmainRef "$\<smtpd_policy_service_max_ttl\>" +syntax match pfmainRef "$\<smtpd_policy_service_request_limit\>" +syntax match pfmainRef "$\<smtpd_policy_service_retry_delay\>" syntax match pfmainRef "$\<smtpd_policy_service_timeout\>" +syntax match pfmainRef "$\<smtpd_policy_service_try_limit\>" syntax match pfmainRef "$\<smtpd_proxy_ehlo\>" syntax match pfmainRef "$\<smtpd_proxy_filter\>" syntax match pfmainRef "$\<smtpd_proxy_options\>" @@ -1307,13 +1430,16 @@ syntax match pfmainRef "$\<smtpd_recipient_restrictions\>" syntax match pfmainRef "$\<smtpd_reject_footer\>" syntax match pfmainRef "$\<smtpd_reject_unlisted_recipient\>" syntax match pfmainRef "$\<smtpd_reject_unlisted_sender\>" +syntax match pfmainRef "$\<smtpd_relay_restrictions\>" syntax match pfmainRef "$\<smtpd_restriction_classes\>" +syntax match pfmainRef "$\<smtpd_sasl_application_name\>" syntax match pfmainRef "$\<smtpd_sasl_auth_enable\>" syntax match pfmainRef "$\<smtpd_sasl_authenticated_header\>" syntax match pfmainRef "$\<smtpd_sasl_exceptions_networks\>" syntax match pfmainRef "$\<smtpd_sasl_local_domain\>" syntax match pfmainRef "$\<smtpd_sasl_path\>" syntax match pfmainRef "$\<smtpd_sasl_security_options\>" +syntax match pfmainRef "$\<smtpd_sasl_service\>" syntax match pfmainRef "$\<smtpd_sasl_tls_security_options\>" syntax match pfmainRef "$\<smtpd_sasl_type\>" syntax match pfmainRef "$\<smtpd_sender_login_maps\>" @@ -1352,21 +1478,21 @@ syntax match pfmainRef "$\<smtpd_tls_security_level\>" syntax match pfmainRef "$\<smtpd_tls_session_cache_database\>" syntax match pfmainRef "$\<smtpd_tls_session_cache_timeout\>" syntax match pfmainRef "$\<smtpd_tls_wrappermode\>" +syntax match pfmainRef "$\<smtpd_upstream_proxy_protocol\>" +syntax match pfmainRef "$\<smtpd_upstream_proxy_timeout\>" syntax match pfmainRef "$\<smtpd_use_tls\>" +syntax match pfmainRef "$\<smtputf8_autodetect_classes\>" +syntax match pfmainRef "$\<smtputf8_enable\>" syntax match pfmainRef "$\<soft_bounce\>" syntax match pfmainRef "$\<stale_lock_time\>" syntax match pfmainRef "$\<stress\>" syntax match pfmainRef "$\<strict_7bit_headers\>" syntax match pfmainRef "$\<strict_8bitmime\>" syntax match pfmainRef "$\<strict_8bitmime_body\>" -syntax match pfmainRef "$\<strict_mime_encoding_domain\>" -syntax match pfmainRef "$\<strict_rfc821_envelopes\>" -syntax match pfmainRef "$\<strict_7bit_headers\>" -syntax match pfmainRef "$\<strict_8bitmime\>" -syntax match pfmainRef "$\<strict_8bitmime_body\>" syntax match pfmainRef "$\<strict_mailbox_ownership\>" syntax match pfmainRef "$\<strict_mime_encoding_domain\>" syntax match pfmainRef "$\<strict_rfc821_envelopes\>" +syntax match pfmainRef "$\<strict_smtputf8\>" syntax match pfmainRef "$\<sun_mailtool_compatibility\>" syntax match pfmainRef "$\<swap_bangpath\>" syntax match pfmainRef "$\<syslog_facility\>" @@ -1374,11 +1500,15 @@ syntax match pfmainRef "$\<syslog_name\>" syntax match pfmainRef "$\<tcp_windowsize\>" syntax match pfmainRef "$\<tls_append_default_CA\>" syntax match pfmainRef "$\<tls_daemon_random_bytes\>" +syntax match pfmainRef "$\<tls_dane_digest_agility\>" +syntax match pfmainRef "$\<tls_dane_digests\>" +syntax match pfmainRef "$\<tls_dane_trust_anchor_digest_enable\>" syntax match pfmainRef "$\<tls_disable_workarounds\>" syntax match pfmainRef "$\<tls_eecdh_strong_curve\>" syntax match pfmainRef "$\<tls_eecdh_ultra_curve\>" syntax match pfmainRef "$\<tls_export_cipherlist\>" syntax match pfmainRef "$\<tls_high_cipherlist\>" +syntax match pfmainRef "$\<tls_legacy_public_key_fingerprints\>" syntax match pfmainRef "$\<tls_low_cipherlist\>" syntax match pfmainRef "$\<tls_medium_cipherlist\>" syntax match pfmainRef "$\<tls_null_cipherlist\>" @@ -1388,6 +1518,10 @@ syntax match pfmainRef "$\<tls_random_exchange_name\>" syntax match pfmainRef "$\<tls_random_prng_update_period\>" syntax match pfmainRef "$\<tls_random_reseed_period\>" syntax match pfmainRef "$\<tls_random_source\>" +syntax match pfmainRef "$\<tls_session_ticket_cipher\>" +syntax match pfmainRef "$\<tls_ssl_options\>" +syntax match pfmainRef "$\<tls_wildcard_matches_multiple_labels\>" +syntax match pfmainRef "$\<tlsmgr_service_name\>" syntax match pfmainRef "$\<tlsproxy_enforce_tls\>" syntax match pfmainRef "$\<tlsproxy_service_name\>" syntax match pfmainRef "$\<tlsproxy_tls_CAfile\>" @@ -1435,6 +1569,7 @@ syntax match pfmainRef "$\<transport_recipient_limit\>" syntax match pfmainRef "$\<transport_recipient_refill_delay\>" syntax match pfmainRef "$\<transport_recipient_refill_limit\>" syntax match pfmainRef "$\<transport_retry_time\>" +syntax match pfmainRef "$\<transport_time_limit\>" syntax match pfmainRef "$\<trigger_timeout\>" syntax match pfmainRef "$\<undisclosed_recipients_header\>" syntax match pfmainRef "$\<unknown_address_reject_code\>" @@ -1455,10 +1590,12 @@ syntax match pfmainRef "$\<unverified_sender_reject_code\>" syntax match pfmainRef "$\<unverified_sender_reject_reason\>" syntax match pfmainRef "$\<unverified_sender_tempfail_action\>" syntax match pfmainRef "$\<verp_delimiter_filter\>" +syntax match pfmainRef "$\<virtual_alias_address_length_limit\>" syntax match pfmainRef "$\<virtual_alias_domains\>" syntax match pfmainRef "$\<virtual_alias_expansion_limit\>" syntax match pfmainRef "$\<virtual_alias_maps\>" syntax match pfmainRef "$\<virtual_alias_recursion_limit\>" +syntax match pfmainRef "$\<virtual_delivery_status_filter\>" syntax match pfmainRef "$\<virtual_destination_concurrency_limit\>" syntax match pfmainRef "$\<virtual_destination_recipient_limit\>" syntax match pfmainRef "$\<virtual_gid_maps\>" @@ -1467,39 +1604,62 @@ syntax match pfmainRef "$\<virtual_mailbox_domains\>" syntax match pfmainRef "$\<virtual_mailbox_limit\>" syntax match pfmainRef "$\<virtual_mailbox_lock\>" syntax match pfmainRef "$\<virtual_mailbox_maps\>" +syntax match pfmainRef "$\<virtual_maps\>" syntax match pfmainRef "$\<virtual_minimum_uid\>" syntax match pfmainRef "$\<virtual_transport\>" syntax match pfmainRef "$\<virtual_uid_maps\>" +syntax keyword pfmainWord accept syntax keyword pfmainWord all +syntax keyword pfmainWord always syntax keyword pfmainWord check_address_map syntax keyword pfmainWord check_ccert_access +syntax keyword pfmainWord check_client_a_access syntax keyword pfmainWord check_client_access +syntax keyword pfmainWord check_client_mx_access +syntax keyword pfmainWord check_client_ns_access syntax keyword pfmainWord check_etrn_access +syntax keyword pfmainWord check_helo_a_access syntax keyword pfmainWord check_helo_access syntax keyword pfmainWord check_helo_mx_access syntax keyword pfmainWord check_helo_ns_access syntax keyword pfmainWord check_policy_service +syntax keyword pfmainWord check_recipient_a_access syntax keyword pfmainWord check_recipient_access syntax keyword pfmainWord check_recipient_maps syntax keyword pfmainWord check_recipient_mx_access syntax keyword pfmainWord check_recipient_ns_access syntax keyword pfmainWord check_relay_domains +syntax keyword pfmainWord check_reverse_client_hostname_a_access +syntax keyword pfmainWord check_reverse_client_hostname_access +syntax keyword pfmainWord check_reverse_client_hostname_mx_access +syntax keyword pfmainWord check_reverse_client_hostname_ns_access +syntax keyword pfmainWord check_sasl_access +syntax keyword pfmainWord check_sender_a_access syntax keyword pfmainWord check_sender_access syntax keyword pfmainWord check_sender_mx_access syntax keyword pfmainWord check_sender_ns_access syntax keyword pfmainWord class syntax keyword pfmainWord client_address syntax keyword pfmainWord client_port +syntax keyword pfmainWord dane +syntax keyword pfmainWord dane-only +syntax keyword pfmainWord defer syntax keyword pfmainWord defer_if_permit syntax keyword pfmainWord defer_if_reject +syntax keyword pfmainWord defer_unauth_destination +syntax keyword pfmainWord disabled syntax keyword pfmainWord dns +syntax keyword pfmainWord dnssec syntax keyword pfmainWord drop syntax keyword pfmainWord dunno +syntax keyword pfmainWord enabled +syntax keyword pfmainWord encrypt syntax keyword pfmainWord enforce syntax keyword pfmainWord envelope_recipient syntax keyword pfmainWord envelope_sender syntax keyword pfmainWord export +syntax keyword pfmainWord fingerprint syntax keyword pfmainWord header_recipient syntax keyword pfmainWord header_sender syntax keyword pfmainWord high @@ -1509,22 +1669,37 @@ syntax keyword pfmainWord ipv4 syntax keyword pfmainWord ipv6 syntax keyword pfmainWord localtime syntax keyword pfmainWord low +syntax keyword pfmainWord may +syntax keyword pfmainWord maybe syntax keyword pfmainWord medium syntax keyword pfmainWord native +syntax keyword pfmainWord never +syntax keyword pfmainWord no_address_mappings +syntax keyword pfmainWord no_header_body_checks +syntax keyword pfmainWord no_header_reply +syntax keyword pfmainWord no_milters +syntax keyword pfmainWord no_unknown_recipient_checks +syntax keyword pfmainWord none syntax keyword pfmainWord null +syntax keyword pfmainWord off +syntax keyword pfmainWord on syntax keyword pfmainWord permit syntax keyword pfmainWord permit_auth_destination +syntax keyword pfmainWord permit_dnswl_client syntax keyword pfmainWord permit_inet_interfaces syntax keyword pfmainWord permit_mx_backup syntax keyword pfmainWord permit_mynetworks syntax keyword pfmainWord permit_naked_ip_address +syntax keyword pfmainWord permit_rhswl_client syntax keyword pfmainWord permit_sasl_authenticated syntax keyword pfmainWord permit_tls_all_clientcerts syntax keyword pfmainWord permit_tls_clientcerts +syntax keyword pfmainWord quarantine syntax keyword pfmainWord reject syntax keyword pfmainWord reject_authenticated_sender_login_mismatch syntax keyword pfmainWord reject_invalid_helo_hostname syntax keyword pfmainWord reject_invalid_hostname +syntax keyword pfmainWord reject_known_sender_login_mismatch syntax keyword pfmainWord reject_maps_rbl syntax keyword pfmainWord reject_multi_recipient_bounce syntax keyword pfmainWord reject_non_fqdn_helo_hostname @@ -1537,10 +1712,12 @@ syntax keyword pfmainWord reject_rbl_client syntax keyword pfmainWord reject_rhsbl_client syntax keyword pfmainWord reject_rhsbl_helo syntax keyword pfmainWord reject_rhsbl_recipient +syntax keyword pfmainWord reject_rhsbl_reverse_client syntax keyword pfmainWord reject_rhsbl_sender syntax keyword pfmainWord reject_sender_login_mismatch syntax keyword pfmainWord reject_unauth_destination syntax keyword pfmainWord reject_unauth_pipelining +syntax keyword pfmainWord reject_unauthenticated_sender_login_mismatch syntax keyword pfmainWord reject_unknown_address syntax keyword pfmainWord reject_unknown_client syntax keyword pfmainWord reject_unknown_client_hostname @@ -1554,11 +1731,15 @@ syntax keyword pfmainWord reject_unlisted_recipient syntax keyword pfmainWord reject_unlisted_sender syntax keyword pfmainWord reject_unverified_recipient syntax keyword pfmainWord reject_unverified_sender +syntax keyword pfmainWord secure +syntax keyword pfmainWord server_name syntax keyword pfmainWord sleep syntax keyword pfmainWord smtpd_access_maps -syntax keyword pfmainWord server_name syntax keyword pfmainWord speed_adjust +syntax keyword pfmainWord strong syntax keyword pfmainWord subnet +syntax keyword pfmainWord tempfail +syntax keyword pfmainWord ultra syntax keyword pfmainWord warn_if_reject syntax keyword pfmainWord CRYPTOPRO_TLSEXT_BUG syntax keyword pfmainWord DONT_INSERT_EMPTY_FRAGMENTS @@ -1574,7 +1755,7 @@ syntax keyword pfmainWord TLS_BLOCK_PADDING_BUG syntax keyword pfmainWord TLS_D5_BUG syntax keyword pfmainWord TLS_ROLLBACK_BUG -syntax keyword pfmainDict btree cidr environ hash nis pcre proxy regexp sdbm sdbm static tcp unix +syntax keyword pfmainDict btree cidr environ hash nis pcre proxy regexp sdbm static tcp unix syntax keyword pfmainQueueDir incoming active deferred corrupt hold syntax keyword pfmainTransport smtp lmtp unix local relay uucp virtual syntax keyword pfmainLock fcntl flock dotlock @@ -1588,26 +1769,50 @@ syntax match pfmainVariable "\$\w\+" contains=pfmainRef syntax match pfmainSpecial "\<alias\>" syntax match pfmainSpecial "\<canonical\>" +syntax match pfmainSpecial "\<forward\>" +syntax match pfmainSpecial "\<generic\>" +syntax match pfmainSpecial "\<include\>" +syntax match pfmainSpecial "\<virtual\>" + +syntax match pfmainSpecial "\<delay_dotcrlf\>" +syntax match pfmainSpecial "\<disable_esmtp\>" + +syntax match pfmainSpecial "\<res_defnames\>" +syntax match pfmainSpecial "\<res_dnsrch\>" + syntax match pfmainSpecial "\<command\>" syntax match pfmainSpecial "\<file\>" -syntax match pfmainSpecial "\<forward\>" + +syntax match pfmainSpecial "\<forward_secrecy\>" +syntax match pfmainSpecial "\<mutual_auth\>" +syntax match pfmainSpecial "\<noactive\>" syntax match pfmainSpecial "\<noanonymous\>" +syntax match pfmainSpecial "\<nodictionary\>" syntax match pfmainSpecial "\<noplaintext\>" + +syntax match pfmainSpecial "\<bounce\>" +syntax match pfmainSpecial "\<2bounce\>" +syntax match pfmainSpecial "\<data\>" +syntax match pfmainSpecial "\<delay\>" +syntax match pfmainSpecial "\<policy\>" +syntax match pfmainSpecial "\<protocol\>" syntax match pfmainSpecial "\<resource\>" syntax match pfmainSpecial "\<software\>" -syntax match pfmainSpecial "\<bounce\>" syntax match pfmainSpecial "\<cleanup\>" syntax match pfmainSpecial "\<cyrus\>" -syntax match pfmainSpecial "\<defer\>" syntax match pfmainSpecial "\<error\>" syntax match pfmainSpecial "\<flush\>" +syntax match pfmainSpecial "\<notify\>" syntax match pfmainSpecial "\<pickup\>" syntax match pfmainSpecial "\<postdrop\>" syntax match pfmainSpecial "\<qmgr\>" +syntax match pfmainSpecial "\<qmqpd\>" syntax match pfmainSpecial "\<rewrite\>" syntax match pfmainSpecial "\<scache\>" +syntax match pfmainSpecial "\<sendmail\>" syntax match pfmainSpecial "\<showq\>" +syntax match pfmainSpecial "\<smtpd\>" syntax match pfmainSpecial "\<trace\>" syntax match pfmainSpecial "\<verify\>" diff --git a/runtime/syntax/php.vim b/runtime/syntax/php.vim index 860181e0e6..4e1a84651c 100644 --- a/runtime/syntax/php.vim +++ b/runtime/syntax/php.vim @@ -1,8 +1,8 @@ " Vim syntax file " Language: php PHP 3/4/5 " Maintainer: Jason Woofenden <jason@jasonwoof.com> -" Last Change: Sep 18, 2014 -" URL: https://gitorious.org/jasonwoof/vim-syntax/blobs/master/php.vim +" Last Change: Dec 26, 2015 +" URL: https://jasonwoof.com/gitweb/?p=vim-syntax.git;a=blob;f=php.vim;hb=HEAD " Former Maintainers: Peter Hodge <toomuchphp-vim@yahoo.com> " Debian VIM Maintainers <pkg-vim-maintainers@lists.alioth.debian.org> " @@ -280,7 +280,7 @@ syn keyword phpStatement return break continue exit goto contained syn keyword phpKeyword var const contained " Type -syn keyword phpType bool[ean] int[eger] real double float string array object NULL contained +syn keyword phpType bool boolean int integer real double float string array object NULL contained " Structure syn keyword phpStructure namespace extends implements instanceof parent self contained @@ -393,13 +393,13 @@ endif " String if exists("php_parent_error_open") - syn region phpStringDouble matchgroup=None start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble contained keepend - syn region phpBacktick matchgroup=None start=+`+ skip=+\\\\\|\\"+ end=+`+ contains=@phpAddStrings,phpIdentifier,phpBackslashSequences,phpIdentifierSimply,phpIdentifierComplex contained keepend - syn region phpStringSingle matchgroup=None start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote contained keepend + syn region phpStringDouble matchgroup=phpStringDouble start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble contained keepend + syn region phpBacktick matchgroup=phpBacktick start=+`+ skip=+\\\\\|\\"+ end=+`+ contains=@phpAddStrings,phpIdentifier,phpBackslashSequences,phpIdentifierSimply,phpIdentifierComplex contained keepend + syn region phpStringSingle matchgroup=phpStringSingle start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote contained keepend else - syn region phpStringDouble matchgroup=None start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble contained extend keepend - syn region phpBacktick matchgroup=None start=+`+ skip=+\\\\\|\\"+ end=+`+ contains=@phpAddStrings,phpIdentifier,phpBackslashSequences,phpIdentifierSimply,phpIdentifierComplex contained extend keepend - syn region phpStringSingle matchgroup=None start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote contained keepend extend + syn region phpStringDouble matchgroup=phpStringDouble start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@phpAddStrings,phpBackslashSequences,phpBackslashDoubleQuote,@phpInterpDouble contained extend keepend + syn region phpBacktick matchgroup=phpBacktick start=+`+ skip=+\\\\\|\\"+ end=+`+ contains=@phpAddStrings,phpIdentifier,phpBackslashSequences,phpIdentifierSimply,phpIdentifierComplex contained extend keepend + syn region phpStringSingle matchgroup=phpStringSingle start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@phpAddStrings,phpBackslashSingleQuote contained keepend extend endif " HereDoc and NowDoc @@ -515,7 +515,7 @@ syntax keyword phpStatement die contained " Highlighting for PHP5's user-definable magic class methods syntax keyword phpSpecialFunction containedin=ALLBUT,phpComment,phpStringDouble,phpStringSingle,phpIdentifier - \ __construct __destruct __call __toString __sleep __wakeup __set __get __unset __isset __clone __set_state + \ __construct __destruct __call __callStatic __get __set __isset __unset __sleep __wakeup __toString __invoke __set_state __clone __debugInfo " Highlighting for __autoload slightly different from line above syntax keyword phpSpecialFunction containedin=ALLBUT,phpComment,phpStringDouble,phpStringSingle,phpIdentifier,phpMethodsVar \ __autoload diff --git a/runtime/syntax/python.vim b/runtime/syntax/python.vim index d2162dee5a..78d35e4c15 100644 --- a/runtime/syntax/python.vim +++ b/runtime/syntax/python.vim @@ -1,9 +1,8 @@ " Vim syntax file " Language: Python -" Maintainer: Neil Schemenauer <nas@python.ca> -" Last Change: 2014 Jul 16 -" Credits: Zvezdan Petkovic <zpetkovic@acm.org> -" Neil Schemenauer <nas@python.ca> +" Maintainer: Zvezdan Petkovic <zpetkovic@acm.org> +" Last Change: 2015 Sep 15 +" Credits: Neil Schemenauer <nas@python.ca> " Dmitry Vasiliev " " This version is a major rewrite by Zvezdan Petkovic. @@ -52,24 +51,26 @@ set cpo&vim " Keep Python keywords in alphabetical order inside groups for easy " comparison with the table in the 'Python Language Reference' -" http://docs.python.org/reference/lexical_analysis.html#keywords. +" https://docs.python.org/2/reference/lexical_analysis.html#keywords, +" https://docs.python.org/3/reference/lexical_analysis.html#keywords. " Groups are in the order presented in NAMING CONVENTIONS in syntax.txt. " Exceptions come last at the end of each group (class and def below). " " Keywords 'with' and 'as' are new in Python 2.6 " (use 'from __future__ import with_statement' in Python 2.5). " -" Some compromises had to be made to support both Python 3.0 and 2.6. -" We include Python 3.0 features, but when a definition is duplicated, +" Some compromises had to be made to support both Python 3 and 2. +" We include Python 3 features, but when a definition is duplicated, " the last definition takes precedence. " -" - 'False', 'None', and 'True' are keywords in Python 3.0 but they are -" built-ins in 2.6 and will be highlighted as built-ins below. -" - 'exec' is a built-in in Python 3.0 and will be highlighted as +" - 'False', 'None', and 'True' are keywords in Python 3 but they are +" built-ins in 2 and will be highlighted as built-ins below. +" - 'exec' is a built-in in Python 3 and will be highlighted as " built-in below. -" - 'nonlocal' is a keyword in Python 3.0 and will be highlighted. -" - 'print' is a built-in in Python 3.0 and will be highlighted as -" built-in below (use 'from __future__ import print_function' in 2.6) +" - 'nonlocal' is a keyword in Python 3 and will be highlighted. +" - 'print' is a built-in in Python 3 and will be highlighted as +" built-in below (use 'from __future__ import print_function' in 2) +" - async and await were added in Python 3.5 and are soft keywords. " syn keyword pythonStatement False, None, True syn keyword pythonStatement as assert break continue del exec global @@ -80,6 +81,7 @@ syn keyword pythonRepeat for while syn keyword pythonOperator and in is not or syn keyword pythonException except finally raise try syn keyword pythonInclude from import +syn keyword pythonAsync async await " Decorators (new in Python 2.4) syn match pythonDecorator "@" display nextgroup=pythonFunction skipwhite @@ -95,16 +97,16 @@ syn match pythonComment "#.*$" contains=pythonTodo,@Spell syn keyword pythonTodo FIXME NOTE NOTES TODO XXX contained " Triple-quoted strings can contain doctests. -syn region pythonString +syn region pythonString matchgroup=pythonQuotes \ start=+[uU]\=\z(['"]\)+ end="\z1" skip="\\\\\|\\\z1" \ contains=pythonEscape,@Spell -syn region pythonString +syn region pythonString matchgroup=pythonTripleQuotes \ start=+[uU]\=\z('''\|"""\)+ skip=+\\["']+ end="\z1" keepend \ contains=pythonEscape,pythonSpaceError,pythonDoctest,@Spell -syn region pythonRawString +syn region pythonRawString matchgroup=pythonQuotes \ start=+[uU]\=[rR]\z(['"]\)+ end="\z1" skip="\\\\\|\\\z1" \ contains=@Spell -syn region pythonRawString +syn region pythonRawString matchgroup=pythonTripleQuotes \ start=+[uU]\=[rR]\z('''\|"""\)+ end="\z1" keepend \ contains=pythonSpaceError,pythonDoctest,@Spell @@ -113,7 +115,7 @@ syn match pythonEscape "\\\o\{1,3}" contained syn match pythonEscape "\\x\x\{2}" contained syn match pythonEscape "\%(\\u\x\{4}\|\\U\x\{8}\)" contained " Python allows case-insensitive Unicode IDs: http://www.unicode.org/charts/ -syn match pythonEscape "\\N{.\{-}}" contained +syn match pythonEscape "\\N{\a\+\%(\s\a\+\)*}" contained syn match pythonEscape "\\$" if exists("python_highlight_all") @@ -148,7 +150,8 @@ endif " - 08e0 or 08j are highlighted, " " and so on, as specified in the 'Python Language Reference'. -" http://docs.python.org/reference/lexical_analysis.html#numeric-literals +" https://docs.python.org/2/reference/lexical_analysis.html#numeric-literals +" https://docs.python.org/3/reference/lexical_analysis.html#numeric-literals if !exists("python_no_number_highlight") " numbers (including longs and complex) syn match pythonNumber "\<0[oO]\=\o\+[Ll]\=\>" @@ -160,54 +163,58 @@ if !exists("python_no_number_highlight") syn match pythonNumber \ "\<\d\+\.\%([eE][+-]\=\d\+\)\=[jJ]\=\%(\W\|$\)\@=" syn match pythonNumber - \ "\%(^\|\W\)\@<=\d*\.\d\+\%([eE][+-]\=\d\+\)\=[jJ]\=\>" + \ "\%(^\|\W\)\zs\d*\.\d\+\%([eE][+-]\=\d\+\)\=[jJ]\=\>" endif " Group the built-ins in the order in the 'Python Library Reference' for " easier comparison. -" http://docs.python.org/library/constants.html -" http://docs.python.org/library/functions.html -" http://docs.python.org/library/functions.html#non-essential-built-in-functions +" https://docs.python.org/2/library/constants.html +" https://docs.python.org/3/library/constants.html +" http://docs.python.org/2/library/functions.html +" http://docs.python.org/3/library/functions.html +" http://docs.python.org/2/library/functions.html#non-essential-built-in-functions +" http://docs.python.org/3/library/functions.html#non-essential-built-in-functions " Python built-in functions are in alphabetical order. if !exists("python_no_builtin_highlight") " built-in constants - " 'False', 'True', and 'None' are also reserved words in Python 3.0 + " 'False', 'True', and 'None' are also reserved words in Python 3 syn keyword pythonBuiltin False True None syn keyword pythonBuiltin NotImplemented Ellipsis __debug__ " built-in functions - syn keyword pythonBuiltin abs all any bin bool chr classmethod - syn keyword pythonBuiltin compile complex delattr dict dir divmod - syn keyword pythonBuiltin enumerate eval filter float format + syn keyword pythonBuiltin abs all any bin bool bytearray callable chr + syn keyword pythonBuiltin classmethod compile complex delattr dict dir + syn keyword pythonBuiltin divmod enumerate eval filter float format syn keyword pythonBuiltin frozenset getattr globals hasattr hash syn keyword pythonBuiltin help hex id input int isinstance syn keyword pythonBuiltin issubclass iter len list locals map max - syn keyword pythonBuiltin min next object oct open ord pow print - syn keyword pythonBuiltin property range repr reversed round set + syn keyword pythonBuiltin memoryview min next object oct open ord pow + syn keyword pythonBuiltin print property range repr reversed round set syn keyword pythonBuiltin setattr slice sorted staticmethod str syn keyword pythonBuiltin sum super tuple type vars zip __import__ - " Python 2.6 only - syn keyword pythonBuiltin basestring callable cmp execfile file + " Python 2 only + syn keyword pythonBuiltin basestring cmp execfile file syn keyword pythonBuiltin long raw_input reduce reload unichr syn keyword pythonBuiltin unicode xrange - " Python 3.0 only - syn keyword pythonBuiltin ascii bytearray bytes exec memoryview - " non-essential built-in functions; Python 2.6 only + " Python 3 only + syn keyword pythonBuiltin ascii bytes exec + " non-essential built-in functions; Python 2 only syn keyword pythonBuiltin apply buffer coerce intern endif " From the 'Python Library Reference' class hierarchy at the bottom. -" http://docs.python.org/library/exceptions.html +" http://docs.python.org/2/library/exceptions.html +" http://docs.python.org/3/library/exceptions.html if !exists("python_no_exception_highlight") - " builtin base exceptions (only used as base classes for other exceptions) + " builtin base exceptions (used mostly as base classes for other exceptions) syn keyword pythonExceptions BaseException Exception - syn keyword pythonExceptions ArithmeticError EnvironmentError + syn keyword pythonExceptions ArithmeticError BufferError syn keyword pythonExceptions LookupError - " builtin base exception removed in Python 3.0 - syn keyword pythonExceptions StandardError + " builtin base exceptions removed in Python 3 + syn keyword pythonExceptions EnvironmentError StandardError " builtin exceptions (actually raised) - syn keyword pythonExceptions AssertionError AttributeError BufferError + syn keyword pythonExceptions AssertionError AttributeError syn keyword pythonExceptions EOFError FloatingPointError GeneratorExit - syn keyword pythonExceptions IOError ImportError IndentationError + syn keyword pythonExceptions ImportError IndentationError syn keyword pythonExceptions IndexError KeyError KeyboardInterrupt syn keyword pythonExceptions MemoryError NameError NotImplementedError syn keyword pythonExceptions OSError OverflowError ReferenceError @@ -215,13 +222,27 @@ if !exists("python_no_exception_highlight") syn keyword pythonExceptions SystemError SystemExit TabError TypeError syn keyword pythonExceptions UnboundLocalError UnicodeError syn keyword pythonExceptions UnicodeDecodeError UnicodeEncodeError - syn keyword pythonExceptions UnicodeTranslateError ValueError VMSError - syn keyword pythonExceptions WindowsError ZeroDivisionError + syn keyword pythonExceptions UnicodeTranslateError ValueError + syn keyword pythonExceptions ZeroDivisionError + " builtin OS exceptions in Python 3 + syn keyword pythonExceptions BlockingIOError BrokenPipeError + syn keyword pythonExceptions ChildProcessError ConnectionAbortedError + syn keyword pythonExceptions ConnectionError ConnectionRefusedError + syn keyword pythonExceptions ConnectionResetError FileExistsError + syn keyword pythonExceptions FileNotFoundError InterruptedError + syn keyword pythonExceptions IsADirectoryError NotADirectoryError + syn keyword pythonExceptions PermissionError ProcessLookupError + syn keyword pythonExceptions RecursionError StopAsyncIteration + syn keyword pythonExceptions TimeoutError + " builtin exceptions deprecated/removed in Python 3 + syn keyword pythonExceptions IOError VMSError WindowsError " builtin warnings syn keyword pythonExceptions BytesWarning DeprecationWarning FutureWarning syn keyword pythonExceptions ImportWarning PendingDeprecationWarning syn keyword pythonExceptions RuntimeWarning SyntaxWarning UnicodeWarning syn keyword pythonExceptions UserWarning Warning + " builtin warnings in Python 3 + syn keyword pythonExceptions ResourceWarning endif if exists("python_space_error_highlight") @@ -268,12 +289,15 @@ if version >= 508 || !exists("did_python_syn_inits") HiLink pythonOperator Operator HiLink pythonException Exception HiLink pythonInclude Include + HiLink pythonAsync Statement HiLink pythonDecorator Define HiLink pythonFunction Function HiLink pythonComment Comment HiLink pythonTodo Todo HiLink pythonString String HiLink pythonRawString String + HiLink pythonQuotes String + HiLink pythonTripleQuotes pythonQuotes HiLink pythonEscape Special if !exists("python_no_number_highlight") HiLink pythonNumber Number diff --git a/runtime/syntax/r.vim b/runtime/syntax/r.vim index 9677823fb1..e48b6686cb 100644 --- a/runtime/syntax/r.vim +++ b/runtime/syntax/r.vim @@ -3,7 +3,9 @@ " Maintainer: Jakson Aquino <jalvesaq@gmail.com> " Former Maintainers: Vaidotas Zemlys <zemlys@gmail.com> " Tom Payne <tom@tompayne.org> -" Last Change: Wed Dec 31, 2014 12:36AM +" Contributor: Johannes Ranke <jranke@uni-bremen.de> +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Wed Oct 21, 2015 06:33AM " Filenames: *.R *.r *.Rhistory *.Rt " " NOTE: The highlighting of R functions is defined in @@ -30,16 +32,21 @@ syn case match " Comment syn match rCommentTodo contained "\(BUG\|FIXME\|NOTE\|TODO\):" -syn match rComment contains=@Spell,rCommentTodo "#.*" +syn match rComment contains=@Spell,rCommentTodo,rOBlock "#.*" " Roxygen -syn match rOKeyword contained "@\(param\|return\|name\|rdname\|examples\|include\|docType\)" +syn region rOBlock start="^\s*\n#\{1,2}' " start="\%^#\{1,2}' " end="^\(#\{1,2}'\)\@!" contains=rOTitle,rOKeyword,rOExamples,@Spell keepend +syn region rOTitle start="^\s*\n#\{1,2}' " start="\%^#\{1,2}' " end="^\(#\{1,2}'\s*$\)\@=" contained contains=rOCommentKey +syn match rOCommentKey "#\{1,2}'" containedin=rOTitle contained + +syn region rOExamples start="^#\{1,2}' @examples.*"rs=e+1,hs=e+1 end="^\(#\{1,2}' @.*\)\@=" end="^\(#\{1,2}'\)\@!" contained contains=rOKeyword + +syn match rOKeyword contained "@\(param\|return\|name\|rdname\|examples\|example\|include\|docType\)" syn match rOKeyword contained "@\(S3method\|TODO\|aliases\|alias\|assignee\|author\|callGraphDepth\|callGraph\)" syn match rOKeyword contained "@\(callGraphPrimitives\|concept\|exportClass\|exportMethod\|exportPattern\|export\|formals\)" syn match rOKeyword contained "@\(format\|importClassesFrom\|importFrom\|importMethodsFrom\|import\|keywords\|useDynLib\)" syn match rOKeyword contained "@\(method\|noRd\|note\|references\|seealso\|setClass\|slot\|source\|title\|usage\)" -syn match rOKeyword contained "@\(family\|template\|templateVar\|description\|details\|inheritParams\)" -syn match rOComment contains=@Spell,rOKeyword "#'.*" +syn match rOKeyword contained "@\(family\|template\|templateVar\|description\|details\|inheritParams\|field\)" if &filetype == "rhelp" @@ -202,7 +209,6 @@ hi def link rBoolean Boolean hi def link rBraceError Error hi def link rComment Comment hi def link rCommentTodo Todo -hi def link rOComment Comment hi def link rComplex Number hi def link rConditional Conditional hi def link rConstant Constant @@ -230,6 +236,11 @@ hi def link rString String hi def link rStrError Error hi def link rType Type hi def link rOKeyword Title +hi def link rOBlock Comment +hi def link rOTitle Title +hi def link rOCommentKey Comment +hi def link rOExamples SpecialComment + let b:current_syntax="r" diff --git a/runtime/syntax/remind.vim b/runtime/syntax/remind.vim index 93a7178479..98064e043a 100644 --- a/runtime/syntax/remind.vim +++ b/runtime/syntax/remind.vim @@ -1,13 +1,17 @@ " Vim syntax file " Language: Remind -" Maintainer: Davide Alberani <alberanid@libero.it> -" Last Change: 18 Sep 2009 -" Version: 0.5 -" URL: http://erlug.linux.it/~da/vim/syntax/remind.vim +" Maintainer: Davide Alberani <da@erlug.linux.it> +" Last Change: 02 Nov 2015 +" Version: 0.7 +" URL: http://ismito.it/vim/syntax/remind.vim " -" remind is a sophisticated reminder service -" you can download remind from: -" http://www.roaringpenguin.com/penguin/open_source_remind.php +" Remind is a sophisticated calendar and alarm program. +" You can download remind from: +" https://www.roaringpenguin.com/products/remind +" +" Changelog +" version 0.7: updated email and link +" version 0.6: added THROUGH keyword (courtesy of Ben Orchard) if version < 600 syntax clear @@ -19,7 +23,7 @@ endif syn case ignore syn keyword remindCommands REM OMIT SET FSET UNSET -syn keyword remindExpiry UNTIL FROM SCANFROM SCAN WARN SCHED +syn keyword remindExpiry UNTIL FROM SCANFROM SCAN WARN SCHED THROUGH syn keyword remindTag PRIORITY TAG syn keyword remindTimed AT DURATION syn keyword remindMove ONCE SKIP BEFORE AFTER diff --git a/runtime/syntax/rpcgen.vim b/runtime/syntax/rpcgen.vim index 548f8c807f..85036dc049 100644 --- a/runtime/syntax/rpcgen.vim +++ b/runtime/syntax/rpcgen.vim @@ -1,15 +1,11 @@ " Vim syntax file " Language: rpcgen -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Sep 06, 2005 -" Version: 8 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Jan 13, 2015 +" Version: 10 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_RPCGEN -" For version 5.x: Clear all syntax items -" For version 6.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") +if exists("b:current_syntax") finish endif diff --git a/runtime/syntax/rst.vim b/runtime/syntax/rst.vim index c1f25699e7..8b17104be4 100644 --- a/runtime/syntax/rst.vim +++ b/runtime/syntax/rst.vim @@ -2,7 +2,7 @@ " Language: reStructuredText documentation format " Maintainer: Marshall Ward <marshall.ward@gmail.com> " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2014-10-03 +" Latest Revision: 2015-09-07 if exists("b:current_syntax") finish @@ -81,7 +81,7 @@ syn region rstHyperlinkTarget matchgroup=rstDirective execute 'syn region rstExDirective contained matchgroup=rstDirective' . \ ' start=+' . s:ReferenceName . '::\_s+' . \ ' skip=+^$+' . - \ ' end=+^\s\@!+ contains=@rstCruft' + \ ' end=+^\s\@!+ contains=@rstCruft,rstLiteralBlock' execute 'syn match rstSubstitutionDefinition contained' . \ ' /|' . s:ReferenceName . '|\_s\+/ nextgroup=@rstDirectives' diff --git a/runtime/syntax/screen.vim b/runtime/syntax/screen.vim index 71b3d3efba..d576d29b7a 100644 --- a/runtime/syntax/screen.vim +++ b/runtime/syntax/screen.vim @@ -1,7 +1,8 @@ " Vim syntax file -" Language: screen(1) configuration file -" Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2010-01-03 +" Language: screen(1) configuration file +" Maintainer: Dmitri Vereshchagin <dmitri.vereshchagin@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2015-09-24 if exists("b:current_syntax") finish @@ -76,12 +77,16 @@ syn keyword screenCommands \ break \ breaktype \ bufferfile + \ bumpleft + \ bumpright \ c1 \ caption \ chacl \ charset \ chdir + \ cjkwidth \ clear + \ collapse \ colon \ command \ compacthist @@ -104,6 +109,7 @@ syn keyword screenCommands \ deflogin \ defmode \ defmonitor + \ defmousetrack \ defnonblock \ defobuflimit \ defscrollback @@ -113,6 +119,7 @@ syn keyword screenCommands \ defutf8 \ defwrap \ defwritelock + \ defzombie \ detach \ digraph \ dinfo @@ -126,7 +133,9 @@ syn keyword screenCommands \ fit \ flow \ focus + \ focusminsize \ gr + \ group \ hardcopy \ hardcopy_append \ hardcopydir @@ -155,6 +164,7 @@ syn keyword screenCommands \ maxwin \ meta \ monitor + \ mousetrack \ msgminwait \ msgwait \ multiuser @@ -182,6 +192,7 @@ syn keyword screenCommands \ register \ remove \ removebuf + \ rendition \ reset \ resize \ screen @@ -197,6 +208,7 @@ syn keyword screenCommands \ sleep \ slowpaste \ sorendition + \ sort \ source \ split \ startup_message @@ -210,6 +222,7 @@ syn keyword screenCommands \ time \ title \ umask + \ unbindall \ unsetenv \ utf8 \ vbell @@ -228,6 +241,7 @@ syn keyword screenCommands \ xon \ zmodem \ zombie + \ zombie_timeout hi def link screenEscape Special hi def link screenComment Comment diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index 5ca5bc641a..efe0bcb461 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -2,11 +2,11 @@ " Language: shell (sh) Korn shell (ksh) bash (sh) " Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> " Previous Maintainer: Lennart Schultz <Lennart.Schultz@ecmwf.int> -" Last Change: Mar 20, 2014 -" Version: 132 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax -" For options and settings, please use: :help ft-sh-syntax -" This file includes many ideas from ?ric Brunet (eric.brunet@ens.fr) +" Last Change: Nov 09, 2015 +" Version: 142 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH +" For options and settings, please use: :help ft-sh-syntax +" This file includes many ideas from Eric Brunet (eric.brunet@ens.fr) " For version 5.x: Clear all syntax items {{{1 " For version 6.x: Quit when a syntax file was already loaded @@ -16,17 +16,6 @@ elseif exists("b:current_syntax") finish endif -" AFAICT "." should be considered part of the iskeyword. Using iskeywords in -" syntax is dicey, so the following code permits the user to -" g:sh_isk set to a string : specify iskeyword. -" g:sh_noisk exists : don't change iskeyword -" g:sh_noisk does not exist : (default) append "." to iskeyword -if exists("g:sh_isk") && type(g:sh_isk) == 1 " user specifying iskeyword - exe "setl isk=".g:sh_isk -elseif !exists("g:sh_noisk") " optionally prevent appending '.' to iskeyword - setl isk+=. -endif - " trying to answer the question: which shell is /bin/sh, really? " If the user has not specified any of g:is_kornshell, g:is_bash, g:is_posix, g:is_sh, then guess. if !exists("g:is_kornshell") && !exists("g:is_bash") && !exists("g:is_posix") && !exists("g:is_sh") @@ -73,6 +62,7 @@ if !exists("b:is_kornshell") && !exists("b:is_bash") endif " set up default g:sh_fold_enabled {{{1 +" ================================ if !exists("g:sh_fold_enabled") let g:sh_fold_enabled= 0 elseif g:sh_fold_enabled != 0 && !has("folding") @@ -95,6 +85,24 @@ if g:sh_fold_enabled && &fdm == "manual" setl fdm=syntax endif +" Set up folding commands for shell {{{1 +" ================================= +if s:sh_fold_functions + com! -nargs=* ShFoldFunctions <args> fold +else + com! -nargs=* ShFoldFunctions <args> +endif +if s:sh_fold_heredoc + com! -nargs=* ShFoldHereDoc <args> fold +else + com! -nargs=* ShFoldHereDoc <args> +endif +if s:sh_fold_ifdofor + com! -nargs=* ShFoldIfDoFor <args> fold +else + com! -nargs=* ShFoldIfDoFor <args> +endif + " sh syntax is case sensitive {{{1 syn case match @@ -104,12 +112,11 @@ syn cluster shErrorList contains=shDoError,shIfError,shInError,shCaseError,shEsa if exists("b:is_kornshell") syn cluster ErrorList add=shDTestError endif -syn cluster shArithParenList contains=shArithmetic,shCaseEsac,shDeref,shDerefSimple,shEcho,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement +syn cluster shArithParenList contains=shArithmetic,shCaseEsac,shComment,shDeref,shDo,shDerefSimple,shEcho,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement,shIf,shFor syn cluster shArithList contains=@shArithParenList,shParenError syn cluster shCaseEsacList contains=shCaseStart,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote,shCtrlSeq,@shErrorList,shStringSpecial,shCaseRange syn cluster shCaseList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq -"syn cluster shColonList contains=@shCaseList -syn cluster shCommandSubList contains=shArithmetic,shDeref,shDerefSimple,shEcho,shEscape,shNumber,shOption,shPosnParm,shExSingleQuote,shSingleQuote,shExDoubleQuote,shDoubleQuote,shStatement,shVariable,shSubSh,shAlias,shTest,shCtrlSeq,shSpecial,shCmdParenRegion +syn cluster shCommandSubList contains=shAlias,shArithmetic,shComment,shCmdParenRegion,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shOption,shPosnParm,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable syn cluster shCurlyList contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial syn cluster shDblQuoteList contains=shCommandSub,shDeref,shDerefSimple,shEscape,shPosnParm,shCtrlSeq,shSpecial syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPPS @@ -127,9 +134,10 @@ syn cluster shHereList contains=shBeginHere,shHerePayload syn cluster shHereListDQ contains=shBeginHere,@shDblQuoteList,shHerePayload syn cluster shIdList contains=shCommandSub,shWrapLineOperator,shSetOption,shDeref,shDerefSimple,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr syn cluster shIfList contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey,shFunctionOne,shFunctionTwo -syn cluster shLoopList contains=@shCaseList,shTestOpr,shExpr,shDblBrace,shConditional,shCaseEsac,shTest,@shErrorList,shSet,shOption +syn cluster shLoopList contains=@shCaseList,@shErrorList,shCaseEsac,shConditional,shDblBrace,shExpr,shFor,shForPP,shIf,shOption,shSet,shTest,shTestOpr,shTouch syn cluster shSubShList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator -syn cluster shTestList contains=shCharClass,shComment,shCommandSub,shDeref,shDerefSimple,shExDoubleQuote,shDoubleQuote,shExpr,shNumber,shOperator,shExSingleQuote,shSingleQuote,shTestOpr,shTest,shCtrlSeq +syn cluster shTestList contains=shCharClass,shCommandSub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shSingleQuote,shTest,shTestOpr + " Echo: {{{1 " ==== " This one is needed INSIDE a CommandSub, so that `echo bla` be correct @@ -146,23 +154,28 @@ if exists("b:is_kornshell") || exists("b:is_bash") syn match shStatement "\<alias\>" syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\h[-._[:alnum:]]\+\)\@=" skip="\\$" end="\>\|`" syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\h[-._[:alnum:]]\+=\)\@=" skip="\\$" end="=" + + " Touch: {{{1 + " ===== + syn match shTouch '\<touch\>[^;#]*' skipwhite nextgroup=shTouchList contains=shTouchCmd + syn match shTouchCmd '\<touch\>' contained endif " Error Codes: {{{1 " ============ if !exists("g:sh_no_error") - syn match shDoError "\<done\>" - syn match shIfError "\<fi\>" - syn match shInError "\<in\>" - syn match shCaseError ";;" - syn match shEsacError "\<esac\>" - syn match shCurlyError "}" - syn match shParenError ")" - syn match shOK '\.\(done\|fi\|in\|esac\)' + syn match shDoError "\<done\>" + syn match shIfError "\<fi\>" + syn match shInError "\<in\>" + syn match shCaseError ";;" + syn match shEsacError "\<esac\>" + syn match shCurlyError "}" + syn match shParenError ")" + syn match shOK '\.\(done\|fi\|in\|esac\)' if exists("b:is_kornshell") - syn match shDTestError "]]" + syn match shDTestError "]]" endif - syn match shTestError "]" + syn match shTestError "]" endif " Options: {{{1 @@ -182,7 +195,7 @@ syn match shRedir "\d<<-\=" syn match shOperator "<<\|>>" contained syn match shOperator "[!&;|]" contained syn match shOperator "\[[[^:]\|\]]" contained -syn match shOperator "!\==" skipwhite nextgroup=shPattern +syn match shOperator "[-=/*+%]\==" skipwhite nextgroup=shPattern syn match shPattern "\<\S\+\())\)\@=" contained contains=shExSingleQuote,shSingleQuote,shExDoubleQuote,shDoubleQuote,shDeref " Subshells: {{{1 @@ -194,15 +207,16 @@ syn region shSubSh transparent matchgroup=shSubShRegion start="[^(]\zs(" end=")" "======= syn region shExpr matchgroup=shRange start="\[" skip=+\\\\\|\\$\|\[+ end="\]" contains=@shTestList,shSpecial syn region shTest transparent matchgroup=shStatement start="\<test\s" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1 -syn match shTestOpr contained "<=\|>=\|!=\|==\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!<>]" -syn match shTestOpr contained '=' skipwhite nextgroup=shTestDoubleQuote,shTestSingleQuote,shTestPattern +syn region shNoQuote start='\S' skip='\%(\\\\\)*\\.' end='\ze\s' contained +syn match shTestOpr contained '[^-+/%]\zs=' skipwhite nextgroup=shTestDoubleQuote,shTestSingleQuote,shTestPattern +syn match shTestOpr contained "<=\|>=\|!=\|==\|=\~\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!<>]" syn match shTestPattern contained '\w\+' -syn match shTestDoubleQuote contained '\%(\%(\\\\\)*\\\)\@<!"[^"]*"' +syn region shTestDoubleQuote contained start='\%(\%(\\\\\)*\\\)\@<!"' skip=+\\\\\|\\"+ end='"' syn match shTestSingleQuote contained '\\.' syn match shTestSingleQuote contained "'[^']*'" if exists("b:is_kornshell") || exists("b:is_bash") - syn region shDblBrace matchgroup=Delimiter start="\[\[" skip=+\\\\\|\\$+ end="\]\]" contains=@shTestList - syn region shDblParen matchgroup=Delimiter start="((" skip=+\\\\\|\\$+ end="))" contains=@shTestList + syn region shDblBrace matchgroup=Delimiter start="\[\[" skip=+\%(\\\\\)*\\$+ end="\]\]" contains=@shTestList,shNoQuote,shComment + syn region shDblParen matchgroup=Delimiter start="((" skip=+\%(\\\\\)*\\$+ end="))" contains=@shTestList,shComment endif " Character Class In Range: {{{1 @@ -211,16 +225,11 @@ syn match shCharClass contained "\[:\(backspace\|escape\|return\|xdigit\|alnum " Loops: do, if, while, until {{{1 " ====== -if s:sh_fold_ifdofor - syn region shDo fold transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList - syn region shIf fold transparent matchgroup=shConditional start="\<if\_s" matchgroup=shConditional skip=+-fi\>+ end="\<;\_s*then\>" end="\<fi\>" contains=@shIfList - syn region shFor fold matchgroup=shLoop start="\<for\ze\_s\s*\%(((\)\@!" end="\<in\_s" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn -else - syn region shDo transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList - syn region shIf transparent matchgroup=shConditional start="\<if\_s" matchgroup=shConditional skip=+-fi\>+ end="\<;\_s*then\>" end="\<fi\>" contains=@shIfList - syn region shFor matchgroup=shLoop start="\<for\ze\_s\s*\%(((\)\@!" end="\<in\>" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn - syn match shForPP '\<for\>\ze\_s*((' -endif +ShFoldIfDoFor syn region shDo transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList +ShFoldIfDoFor syn region shIf transparent matchgroup=shConditional start="\<if\_s" matchgroup=shConditional skip=+-fi\>+ end="\<;\_s*then\>" end="\<fi\>" contains=@shIfList +ShFoldIfDoFor syn region shFor matchgroup=shLoop start="\<for\ze\_s\s*\%(((\)\@!" end="\<in\>" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn +ShFoldIfDoFor syn region shForPP matchgroup=shLoop start='\<for\>\_s*((' end='))' contains=shTestOpr + if exists("b:is_kornshell") || exists("b:is_bash") syn cluster shCaseList add=shRepeat syn cluster shFunctionList add=shRepeat @@ -238,13 +247,9 @@ syn match shComma contained "," " ==== syn match shCaseBar contained skipwhite "\(^\|[^\\]\)\(\\\\\)*\zs|" nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote syn match shCaseStart contained skipwhite skipnl "(" nextgroup=shCase,shCaseBar -if s:sh_fold_ifdofor - syn region shCase fold contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment - syn region shCaseEsac fold matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList -else - syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment - syn region shCaseEsac matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList -endif +ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment +ShFoldIfDoFor syn region shCaseEsac matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList + syn keyword shCaseIn contained skipwhite skipnl in nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote if exists("b:is_bash") syn region shCaseExSingleQuote matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+ contains=shStringSpecial,shSpecial skipwhite skipnl nextgroup=shCaseBar contained @@ -280,13 +285,13 @@ if exists("b:is_kornshell") || exists("b:is_bash") elseif !exists("g:sh_no_error") syn region shCommandSub matchgroup=Error start="\$(" end=")" contains=@shCommandSubList endif -syn region shCmdParenRegion matchgroup=shCmdSubRegion start="(" skip='\\\\\|\\.' end=")" contains=@shCommandSubList +syn region shCmdParenRegion matchgroup=shCmdSubRegion start="(\ze[^(]" skip='\\\\\|\\.' end=")" contains=@shCommandSubList if exists("b:is_bash") syn cluster shCommandSubList add=bashSpecialVariables,bashStatement syn cluster shCaseList add=bashAdminStatement,bashStatement syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ALIASES BASH_ARGC BASH_ARGC BASH_ARGV BASH_ARGV BASH_CMDS BASH_CMDS BASH_COMMAND BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_EXECUTION_STRING BASH_LINENO BASH_LINENO BASHOPTS BASHOPTS BASHPID BASHPID BASH_REMATCH BASH_REMATCH BASH_SOURCE BASH_SOURCE BASH_SUBSHELL BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD BASH_XTRACEFD CDPATH COLUMNS COLUMNS COMP_CWORD COMP_CWORD COMP_KEY COMP_KEY COMP_LINE COMP_LINE COMP_POINT COMP_POINT COMPREPLY COMPREPLY COMP_TYPE COMP_TYPE COMP_WORDBREAKS COMP_WORDBREAKS COMP_WORDS COMP_WORDS COPROC COPROC DIRSTACK EMACS EMACS ENV ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNAME FUNCNEST FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_NUMERIC LINENO LINES LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT POSIXLY_CORRECT PPID PROMPT_COMMAND PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_LINE READLINE_POINT READLINE_POINT REPLY SECONDS SHELL SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR TMPDIR UID - syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep less ls mkdir mv rm rmdir rpm sed sleep sort strip tail touch + syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep less ls mkdir mv rm rmdir rpm sed sleep sort strip tail syn keyword bashAdminStatement daemon killall killproc nice reload restart start status stop syn keyword bashStatement command compgen endif @@ -295,7 +300,7 @@ if exists("b:is_kornshell") syn cluster shCommandSubList add=kshSpecialVariables,kshStatement syn cluster shCaseList add=kshStatement syn keyword kshSpecialVariables contained CDPATH COLUMNS EDITOR ENV ERRNO FCEDIT FPATH HISTFILE HISTSIZE HOME IFS LINENO LINES MAIL MAILCHECK MAILPATH OLDPWD OPTARG OPTIND PATH PPID PS1 PS2 PS3 PS4 PWD RANDOM REPLY SECONDS SHELL TMOUT VISUAL - syn keyword kshStatement cat chmod clear cp du egrep expr fgrep find grep killall less ls mkdir mv nice printenv rm rmdir sed sort strip stty tail touch tput + syn keyword kshStatement cat chmod clear cp du egrep expr fgrep find grep killall less ls mkdir mv nice printenv rm rmdir sed sort strip stty tail tput syn keyword kshStatement command setgroups setsenv endif @@ -321,12 +326,11 @@ elseif !exists("g:sh_no_error") endif syn region shSingleQuote matchgroup=shQuote start=+'+ end=+'+ contains=@Spell syn region shDoubleQuote matchgroup=shQuote start=+\%(\%(\\\\\)*\\\)\@<!"+ skip=+\\"+ end=+"+ contains=@shDblQuoteList,shStringSpecial,@Spell -syn match shStringSpecial "[^[:print:] \t]" contained +syn match shStringSpecial "[^[:print:] \t]" contained syn match shStringSpecial "\%(\\\\\)*\\[\\"'`$()#]" -"syn match shSpecial "[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]" nextgroup=shMoreSpecial,shComment -syn match shSpecial "[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]" nextgroup=shMoreSpecial -syn match shSpecial "^\%(\\\\\)*\\[\\"'`$()#]" nextgroup=shComment -syn match shMoreSpecial "\%(\\\\\)*\\[\\"'`$()#]" contained nextgroup=shMoreSpecial +syn match shSpecial "[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]" +syn match shSpecial "^\%(\\\\\)*\\[\\"'`$()#]" +syn match shMoreSpecial "\%(\\\\\)*\\[\\"'`$()#]" nextgroup=shMoreSpecial contained " Comments: {{{1 "========== @@ -340,42 +344,29 @@ syn match shQuickComment contained "#.*$" " Here Documents: {{{1 " ========================================= if version < 600 - syn region shHereDoc matchgroup=shRedir01 start="<<\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shRedir01 end="^END[a-zA-Z_0-9]*$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir02 start="<<-\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shRedir02 end="^\s*END[a-zA-Z_0-9]*$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir03 start="<<\s*\**EOF\**" matchgroup=shRedir03 end="^EOF$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir04 start="<<-\s*\**EOF\**" matchgroup=shRedir04 end="^\s*EOF$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir05 start="<<\s*\**\.\**" matchgroup=shRedir05 end="^\.$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir06 start="<<-\s*\**\.\**" matchgroup=shRedir06 end="^\s*\.$" contains=@shDblQuoteList - -elseif s:sh_fold_heredoc - syn region shHereDoc matchgroup=shRedir07 fold start="<<\s*\z([^ \t|]*\)" matchgroup=shRedir07 end="^\z1\s*$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir08 fold start="<<\s*\"\z([^ \t|]*\)\"" matchgroup=shRedir08 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir09 fold start="<<\s*'\z([^ \t|]*\)'" matchgroup=shRedir09 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir10 fold start="<<-\s*\z([^ \t|]*\)" matchgroup=shRedir10 end="^\s*\z1\s*$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir11 fold start="<<-\s*\"\z([^ \t|]*\)\"" matchgroup=shRedir11 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir12 fold start="<<-\s*'\z([^ \t|]*\)'" matchgroup=shRedir12 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir13 fold start="<<\s*\\\_$\_s*\z([^ \t|]*\)" matchgroup=shRedir13 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir14 fold start="<<\s*\\\_$\_s*\"\z([^ \t|]*\)\"" matchgroup=shRedir14 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir15 fold start="<<-\s*\\\_$\_s*'\z([^ \t|]*\)'" matchgroup=shRedir15 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir16 fold start="<<-\s*\\\_$\_s*\z([^ \t|]*\)" matchgroup=shRedir16 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir17 fold start="<<-\s*\\\_$\_s*\"\z([^ \t|]*\)\"" matchgroup=shRedir17 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir18 fold start="<<\s*\\\_$\_s*'\z([^ \t|]*\)'" matchgroup=shRedir18 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir19 fold start="<<\\\z([^ \t|]*\)" matchgroup=shRedir19 end="^\z1\s*$" + syn region shHereDoc matchgroup=shHereDoc01 start="<<\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shHereDoc01 end="^END[a-zA-Z_0-9]*$" contains=@shDblQuoteList + syn region shHereDoc matchgroup=shHereDoc02 start="<<-\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shHereDoc02 end="^\s*END[a-zA-Z_0-9]*$" contains=@shDblQuoteList + syn region shHereDoc matchgroup=shHereDoc03 start="<<\s*\**EOF\**" matchgroup=shHereDoc03 end="^EOF$" contains=@shDblQuoteList + syn region shHereDoc matchgroup=shHereDoc04 start="<<-\s*\**EOF\**" matchgroup=shHereDoc04 end="^\s*EOF$" contains=@shDblQuoteList + syn region shHereDoc matchgroup=shHereDoc05 start="<<\s*\**\.\**" matchgroup=shHereDoc05 end="^\.$" contains=@shDblQuoteList + syn region shHereDoc matchgroup=shHereDoc06 start="<<-\s*\**\.\**" matchgroup=shHereDoc06 end="^\s*\.$" contains=@shDblQuoteList else - syn region shHereDoc matchgroup=shRedir20 start="<<\s*\\\=\z([^ \t|]*\)" matchgroup=shRedir20 end="^\z1\s*$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir21 start="<<\s*\"\z([^ \t|]*\)\"" matchgroup=shRedir21 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir22 start="<<-\s*\z([^ \t|]*\)" matchgroup=shRedir22 end="^\s*\z1\s*$" contains=@shDblQuoteList - syn region shHereDoc matchgroup=shRedir23 start="<<-\s*'\z([^ \t|]*\)'" matchgroup=shRedir23 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir24 start="<<\s*'\z([^ \t|]*\)'" matchgroup=shRedir24 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir25 start="<<-\s*\"\z([^ \t|]*\)\"" matchgroup=shRedir25 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir26 start="<<\s*\\\_$\_s*\z([^ \t|]*\)" matchgroup=shRedir26 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir27 start="<<-\s*\\\_$\_s*\z([^ \t|]*\)" matchgroup=shRedir27 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir28 start="<<-\s*\\\_$\_s*'\z([^ \t|]*\)'" matchgroup=shRedir28 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir29 start="<<\s*\\\_$\_s*'\z([^ \t|]*\)'" matchgroup=shRedir29 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir30 start="<<\s*\\\_$\_s*\"\z([^ \t|]*\)\"" matchgroup=shRedir30 end="^\z1\s*$" - syn region shHereDoc matchgroup=shRedir31 start="<<-\s*\\\_$\_s*\"\z([^ \t|]*\)\"" matchgroup=shRedir31 end="^\s*\z1\s*$" - syn region shHereDoc matchgroup=shRedir32 start="<<\\\z([^ \t|]*\)" matchgroup=shRedir32 end="^\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc07 start="<<\s*\\\=\z([^ \t|]\+\)" matchgroup=shHereDoc07 end="^\z1\s*$" contains=@shDblQuoteList + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc08 start="<<\s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc08 end="^\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc09 start="<<-\s*\z([^ \t|]\+\)" matchgroup=shHereDoc09 end="^\s*\z1\s*$" contains=@shDblQuoteList + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc10 start="<<-\s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc10 end="^\s*\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc11 start="<<\s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc11 end="^\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc12 start="<<-\s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc12 end="^\s*\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc13 start="<<\s*\\\_$\_s*\z([^ \t|]\+\)" matchgroup=shHereDoc13 end="^\z1\s*$" contains=@shDblQuoteList + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc14 start="<<\s*\\\_$\_s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc14 end="^\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc15 start="<<\s*\\\_$\_s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc15 end="^\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc16 start="<<-\s*\\\_$\_s*\z([^ \t|]\+\)" matchgroup=shHereDoc16 end="^\s*\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc17 start="<<-\s*\\\_$\_s*\\\z([^ \t|]\+\)" matchgroup=shHereDoc17 end="^\s*\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc18 start="<<-\s*\\\_$\_s*'\z([^ \t|]\+\)'" matchgroup=shHereDoc18 end="^\s*\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc19 start="<<-\s*\\\_$\_s*\"\z([^ \t|]\+\)\"" matchgroup=shHereDoc19 end="^\s*\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc20 start="<<\\\z([^ \t|]\+\)" matchgroup=shHereDoc20 end="^\z1\s*$" + ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc21 start="<<-\s*\\\z([^ \t|]\+\)" matchgroup=shHereDoc21 end="^\s*\z1\s*$" endif " Here Strings: {{{1 @@ -388,8 +379,8 @@ endif " Identifiers: {{{1 "============= syn match shSetOption "\s\zs[-+][a-zA-Z0-9]\+\>" contained -syn match shVariable "\<\([bwglsav]:\)\=[a-zA-Z0-9.!@_%+,]*\ze=" nextgroup=shSetIdentifier -syn match shSetIdentifier "=" contained nextgroup=shCmdParenRegion,shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote +syn match shVariable "\<\([bwglsav]:\)\=[a-zA-Z0-9.!@_%+,]*\ze=" nextgroup=shVarAssign +syn match shVarAssign "=" contained nextgroup=shCmdParenRegion,shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote syn region shAtExpr contained start="@(" end=")" contains=@shIdList if exists("b:is_bash") syn region shSetList oneline matchgroup=shSet start="\<\(declare\|typeset\|local\|export\|unset\)\>\ze[^/]" end="$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+#\|=" contains=@shIdList @@ -407,30 +398,24 @@ if !exists("g:is_posix") endif if exists("b:is_bash") - if s:sh_fold_functions - syn region shFunctionOne fold matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*{" end="}" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment - syn region shFunctionTwo fold matchgroup=shFunction start="\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment - else - syn region shFunctionOne matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*{" end="}" contains=@shFunctionList - syn region shFunctionTwo matchgroup=shFunction start="\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained - endif + ShFoldFunctions syn region shFunctionOne matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*{" end="}" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment + ShFoldFunctions syn region shFunctionTwo matchgroup=shFunction start="\<[^d][^o]\&\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment + ShFoldFunctions syn region shFunctionThree matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*(" end=")" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment + ShFoldFunctions syn region shFunctionFour matchgroup=shFunction start="\<[^d][^o]\&\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*)" end=")" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment else - if s:sh_fold_functions - syn region shFunctionOne fold matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{" end="}" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment - syn region shFunctionTwo fold matchgroup=shFunction start="\h\w*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment - else - syn region shFunctionOne matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{" end="}" contains=@shFunctionList - syn region shFunctionTwo matchgroup=shFunction start="\h\w*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained - endif + ShFoldFunctions syn region shFunctionOne matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{" end="}" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment + ShFoldFunctions syn region shFunctionTwo matchgroup=shFunction start="\<[^d][^o]\&\h\w*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment + ShFoldFunctions syn region shFunctionThree matchgroup=shFunction start="^\s*\h\w*\s*()\_s*(" end=")" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment + ShFoldFunctions syn region shFunctionFour matchgroup=shFunction start="\<[^d][^o]\&\h\w*\s*\%(()\)\=\_s*(" end=")" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment endif " Parameter Dereferencing: {{{1 " ======================== -syn match shDerefSimple "\$\%(\k\+\|\d\)" -syn region shDeref matchgroup=PreProc start="\${" end="}" contains=@shDerefList,shDerefVarArray if !exists("g:sh_no_error") syn match shDerefWordError "[^}$[]" contained endif +syn match shDerefSimple "\$\%(\k\+\|\d\)" +syn region shDeref matchgroup=PreProc start="\${" end="}" contains=@shDerefList,shDerefVarArray syn match shDerefSimple "\$[-#*@!?]" syn match shDerefSimple "\$\$" if exists("b:is_bash") || exists("b:is_kornshell") @@ -438,33 +423,45 @@ if exists("b:is_bash") || exists("b:is_kornshell") syn region shDeref matchgroup=PreProc start="\${\$\$" end="}" contains=@shDerefList endif +" ksh: ${!var[*]} array index list syntax: {{{1 +" ======================================== +if exists("b:is_kornshell") + syn region shDeref matchgroup=PreProc start="\${!" end="}" contains=@shDerefVarArray +endif + " bash: ${!prefix*} and ${#parameter}: {{{1 " ==================================== if exists("b:is_bash") syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOp syn match shDerefVar contained "{\@<=!\k\+" nextgroup=@shDerefVarList endif +if exists("b:is_kornshell") + syn match shDerefVar contained "{\@<=!\k[[:alnum:]_.]*" nextgroup=@shDerefVarList +endif syn match shDerefSpecial contained "{\@<=[-*@?0]" nextgroup=shDerefOp,shDerefOpError syn match shDerefSpecial contained "\({[#!]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp syn match shDerefVar contained "{\@<=\k\+" nextgroup=@shDerefVarList +if exists("b:is_kornshell") + syn match shDerefVar contained "{\@<=\k[[:alnum:]_.]*" nextgroup=@shDerefVarList +endif " sh ksh bash : ${var[... ]...} array reference: {{{1 -syn region shDerefVarArray contained matchgroup=shDeref start="\[" end="]" contains=@shCommandSubList nextgroup=shDerefOp,shDerefOpError +syn region shDerefVarArray contained matchgroup=shDeref start="\[" end="]" contains=@shCommandSubList nextgroup=shDerefOp,shDerefOpError " Special ${parameter OPERATOR word} handling: {{{1 -" sh ksh bash : ${parameter:-word} word is default value -" sh ksh bash : ${parameter:=word} assign word as default value -" sh ksh bash : ${parameter:?word} display word if parameter is null -" sh ksh bash : ${parameter:+word} use word if parameter is not null, otherwise nothing -" ksh bash : ${parameter#pattern} remove small left pattern -" ksh bash : ${parameter##pattern} remove large left pattern -" ksh bash : ${parameter%pattern} remove small right pattern -" ksh bash : ${parameter%%pattern} remove large right pattern -" bash : ${parameter^pattern} Case modification -" bash : ${parameter^^pattern} Case modification -" bash : ${parameter,pattern} Case modification -" bash : ${parameter,,pattern} Case modification +" sh ksh bash : ${parameter:-word} word is default value +" sh ksh bash : ${parameter:=word} assign word as default value +" sh ksh bash : ${parameter:?word} display word if parameter is null +" sh ksh bash : ${parameter:+word} use word if parameter is not null, otherwise nothing +" ksh bash : ${parameter#pattern} remove small left pattern +" ksh bash : ${parameter##pattern} remove large left pattern +" ksh bash : ${parameter%pattern} remove small right pattern +" ksh bash : ${parameter%%pattern} remove large right pattern +" bash : ${parameter^pattern} Case modification +" bash : ${parameter^^pattern} Case modification +" bash : ${parameter,pattern} Case modification +" bash : ${parameter,,pattern} Case modification syn cluster shDerefPatternList contains=shDerefPattern,shDerefString if !exists("g:sh_no_error") syn match shDerefOpError contained ":[[:punct:]]" @@ -586,6 +583,7 @@ hi def link shHereDoc shString hi def link shHerePayload shHereDoc hi def link shLoop shStatement hi def link shMoreSpecial shSpecial +hi def link shNoQuote shDoubleQuote hi def link shOption shCommandSub hi def link shPattern shString hi def link shParen shArithmetic @@ -598,12 +596,12 @@ hi def link shSetOption shOption hi def link shSingleQuote shString hi def link shSource shOperator hi def link shStringSpecial shSpecial -hi def link shStringSpecial Unique hi def link shSubShRegion shOperator hi def link shTestOpr shConditional hi def link shTestPattern shString hi def link shTestDoubleQuote shString hi def link shTestSingleQuote shString +hi def link shTouchCmd shStatement hi def link shVariable shSetList hi def link shWrapLineOperator shOperator @@ -660,38 +658,33 @@ hi def link shStatement Statement hi def link shString String hi def link shTodo Todo hi def link shAlias Identifier -hi def link shRedir01 shRedir -hi def link shRedir02 shRedir -hi def link shRedir03 shRedir -hi def link shRedir04 shRedir -hi def link shRedir05 shRedir -hi def link shRedir06 shRedir -hi def link shRedir07 shRedir -hi def link shRedir08 shRedir -hi def link shRedir09 shRedir -hi def link shRedir10 shRedir -hi def link shRedir11 shRedir -hi def link shRedir12 shRedir -hi def link shRedir13 shRedir -hi def link shRedir14 shRedir -hi def link shRedir15 shRedir -hi def link shRedir16 shRedir -hi def link shRedir17 shRedir -hi def link shRedir18 shRedir -hi def link shRedir19 shRedir -hi def link shRedir20 shRedir -hi def link shRedir21 shRedir -hi def link shRedir22 shRedir -hi def link shRedir23 shRedir -hi def link shRedir24 shRedir -hi def link shRedir25 shRedir -hi def link shRedir26 shRedir -hi def link shRedir27 shRedir -hi def link shRedir28 shRedir -hi def link shRedir29 shRedir -hi def link shRedir30 shRedir -hi def link shRedir31 shRedir -hi def link shRedir32 shRedir +hi def link shHereDoc01 shRedir +hi def link shHereDoc02 shRedir +hi def link shHereDoc03 shRedir +hi def link shHereDoc04 shRedir +hi def link shHereDoc05 shRedir +hi def link shHereDoc06 shRedir +hi def link shHereDoc07 shRedir +hi def link shHereDoc08 shRedir +hi def link shHereDoc09 shRedir +hi def link shHereDoc10 shRedir +hi def link shHereDoc11 shRedir +hi def link shHereDoc12 shRedir +hi def link shHereDoc13 shRedir +hi def link shHereDoc14 shRedir +hi def link shHereDoc15 shRedir +hi def link shHereDoc16 shRedir +hi def link shHereDoc17 shRedir +hi def link shHereDoc18 shRedir +hi def link shHereDoc19 shRedir +hi def link shHereDoc20 shRedir +hi def link shHereDoc21 shRedir + +" Delete shell folding commands {{{1 +" ============================= +delc ShFoldFunctions +delc ShFoldHereDoc +delc ShFoldIfDoFor " Set Current Syntax: {{{1 " =================== diff --git a/runtime/syntax/sm.vim b/runtime/syntax/sm.vim index 2f9e6d7d01..8fdc14b71a 100644 --- a/runtime/syntax/sm.vim +++ b/runtime/syntax/sm.vim @@ -1,15 +1,11 @@ " Vim syntax file " Language: sendmail -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Sep 06, 2005 -" Version: 4 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax - -" For version 5.x: Clear all syntax items -" For version 6.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 7 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SM + +if exists("b:current_syntax") finish endif diff --git a/runtime/syntax/sqloracle.vim b/runtime/syntax/sqloracle.vim index f9a7c19e3b..8afe2686bf 100644 --- a/runtime/syntax/sqloracle.vim +++ b/runtime/syntax/sqloracle.vim @@ -1,13 +1,12 @@ " Vim syntax file -" Language: SQL, PL/SQL (Oracle 8i) -" Maintainer: Paul Moore <pf_moore AT yahoo.co.uk> -" Last Change: 2005 Dec 23 - -" For version 5.x: Clear all syntax items -" For version 6.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") +" Language: SQL, PL/SQL (Oracle 11g) +" Maintainer: Christian Brabandt +" Repository: https://github.com/chrisbra/vim-sqloracle-syntax +" License: Vim +" Previous Maintainer: Paul Moore +" Last Change: 2015 Nov 24 + +if exists("b:current_syntax") finish endif @@ -15,75 +14,121 @@ syn case ignore " The SQL reserved words, defined as keywords. -syn keyword sqlSpecial false null true +syn keyword sqlSpecial false null true -syn keyword sqlKeyword access add as asc begin by check cluster column -syn keyword sqlKeyword compress connect current cursor decimal default desc +syn keyword sqlKeyword access add as asc begin by case check cluster column +syn keyword sqlKeyword cache compress connect current cursor decimal default desc syn keyword sqlKeyword else elsif end exception exclusive file for from syn keyword sqlKeyword function group having identified if immediate increment -syn keyword sqlKeyword index initial into is level loop maxextents mode modify -syn keyword sqlKeyword nocompress nowait of offline on online start -syn keyword sqlKeyword successful synonym table then to trigger uid +syn keyword sqlKeyword index initial initrans into is level link logging loop +syn keyword sqlKeyword maxextents maxtrans mode modify monitoring +syn keyword sqlKeyword nocache nocompress nologging noparallel nowait of offline on online start +syn keyword sqlKeyword parallel successful synonym table tablespace then to trigger uid syn keyword sqlKeyword unique user validate values view whenever -syn keyword sqlKeyword where with option order pctfree privileges procedure +syn keyword sqlKeyword where with option order pctfree pctused privileges procedure syn keyword sqlKeyword public resource return row rowlabel rownum rows syn keyword sqlKeyword session share size smallint type using syn keyword sqlOperator not and or syn keyword sqlOperator in any some all between exists syn keyword sqlOperator like escape -syn keyword sqlOperator union intersect minus -syn keyword sqlOperator prior distinct +syn keyword sqlOperator union intersect minus +syn keyword sqlOperator prior distinct syn keyword sqlOperator sysdate out -syn keyword sqlStatement alter analyze audit comment commit create -syn keyword sqlStatement delete drop execute explain grant insert lock noaudit -syn keyword sqlStatement rename revoke rollback savepoint select set -syn keyword sqlStatement truncate update +syn keyword sqlStatement analyze audit comment commit +syn keyword sqlStatement delete drop execute explain grant lock noaudit +syn keyword sqlStatement rename revoke rollback savepoint set +syn keyword sqlStatement truncate +" next ones are contained, so folding works. +syn keyword sqlStatement create update alter select insert contained syn keyword sqlType boolean char character date float integer long syn keyword sqlType mlslabel number raw rowid varchar varchar2 varray -" Strings and characters: -syn region sqlString start=+"+ skip=+\\\\\|\\"+ end=+"+ -syn region sqlString start=+'+ skip=+\\\\\|\\'+ end=+'+ +" Strings: +syn region sqlString start=+"+ skip=+\\\\\|\\"+ end=+"+ +syn region sqlString start=+'+ skip=+\\\\\|\\'+ end=+'+ " Numbers: -syn match sqlNumber "-\=\<\d*\.\=[0-9_]\>" +syn match sqlNumber "-\=\<\d*\.\=[0-9_]\>" " Comments: -syn region sqlComment start="/\*" end="\*/" contains=sqlTodo -syn match sqlComment "--.*$" contains=sqlTodo +syn region sqlComment start="/\*" end="\*/" contains=sqlTodo,@Spell fold +syn match sqlComment "--.*$" contains=sqlTodo,@Spell + +" Setup Folding: +" this is a hack, to get certain statements folded. +" the keywords create/update/alter/select/insert need to +" have contained option. +syn region sqlFold start='^\s*\zs\c\(Create\|Update\|Alter\|Select\|Insert\)' end=';$\|^$' transparent fold contains=ALL syn sync ccomment sqlComment -" Todo. -syn keyword sqlTodo contained TODO FIXME XXX DEBUG NOTE +" Functions: +" (Oracle 11g) +" Aggregate Functions +syn keyword sqlFunction avg collect corr corr_s corr_k count covar_pop covar_samp cume_dist dense_rank first +syn keyword sqlFunction group_id grouping grouping_id last max median min percentile_cont percentile_disc percent_rank rank +syn keyword sqlFunction regr_slope regr_intercept regr_count regr_r2 regr_avgx regr_avgy regr_sxx regr_syy regr_sxy +syn keyword sqlFunction stats_binomial_test stats_crosstab stats_f_test stats_ks_test stats_mode stats_mw_test +syn keyword sqlFunction stats_one_way_anova stats_t_test_one stats_t_test_paired stats_t_test_indep stats_t_test_indepu +syn keyword sqlFunction stats_wsr_test stddev stddev_pop stddev_samp sum +syn keyword sqlFunction sys_xmlagg var_pop var_samp variance xmlagg +" Char Functions +syn keyword sqlFunction ascii chr concat initcap instr length lower lpad ltrim +syn keyword sqlFunction nls_initcap nls_lower nlssort nls_upper regexp_instr regexp_replace +syn keyword sqlFunction regexp_substr replace rpad rtrim soundex substr translate treat trim upper +" Comparison Functions +syn keyword sqlFunction greatest least +" Conversion Functions +syn keyword sqlFunction asciistr bin_to_num cast chartorowid compose convert +syn keyword sqlFunction decompose hextoraw numtodsinterval numtoyminterval rawtohex rawtonhex rowidtochar +syn keyword sqlFunction rowidtonchar scn_to_timestamp timestamp_to_scn to_binary_double to_binary_float +syn keyword sqlFunction to_char to_char to_char to_clob to_date to_dsinterval to_lob to_multi_byte +syn keyword sqlFunction to_nchar to_nchar to_nchar to_nclob to_number to_dsinterval to_single_byte +syn keyword sqlFunction to_timestamp to_timestamp_tz to_yminterval to_yminterval translate unistr +" DataMining Functions +syn keyword sqlFunction cluster_id cluster_probability cluster_set feature_id feature_set +syn keyword sqlFunction feature_value prediction prediction_bounds prediction_cost +syn keyword sqlFunction prediction_details prediction_probability prediction_set +" Datetime Functions +syn keyword sqlFunction add_months current_date current_timestamp dbtimezone extract +syn keyword sqlFunction from_tz last_day localtimestamp months_between new_time +syn keyword sqlFunction next_day numtodsinterval numtoyminterval round sessiontimezone +syn keyword sqlFunction sys_extract_utc sysdate systimestamp to_char to_timestamp +syn keyword sqlFunction to_timestamp_tz to_dsinterval to_yminterval trunc tz_offset +" Numeric Functions +syn keyword sqlFunction abs acos asin atan atan2 bitand ceil cos cosh exp +syn keyword sqlFunction floor ln log mod nanvl power remainder round sign +syn keyword sqlFunction sin sinh sqrt tan tanh trunc width_bucket +" NLS Functions +syn keyword sqlFunction ls_charset_decl_len nls_charset_id nls_charset_name +" Various Functions +syn keyword sqlFunction bfilename cardin coalesce collect decode dump empty_blob empty_clob +syn keyword sqlFunction lnnvl nullif nvl nvl2 ora_hash powermultiset powermultiset_by_cardinality +syn keyword sqlFunction sys_connect_by_path sys_context sys_guid sys_typeid uid user userenv vsizeality +" XML Functions +syn keyword sqlFunction appendchildxml deletexml depth extract existsnode extractvalue insertchildxml +syn keyword sqlFunction insertxmlbefore path sys_dburigen sys_xmlagg sys_xmlgen updatexml xmlagg xmlcast +syn keyword sqlFunction xmlcdata xmlcolattval xmlcomment xmlconcat xmldiff xmlelement xmlexists xmlforest +syn keyword sqlFunction xmlparse xmlpatch xmlpi xmlquery xmlroot xmlsequence xmlserialize xmltable xmltransform +" Todo: +syn keyword sqlTodo TODO FIXME XXX DEBUG NOTE contained " Define the default highlighting. -" For version 5.7 and earlier: only when not done already -" For version 5.8 and later: only when an item doesn't have highlighting yet -if version >= 508 || !exists("did_sql_syn_inits") - if version < 508 - let did_sql_syn_inits = 1 - command -nargs=+ HiLink hi link <args> - else - command -nargs=+ HiLink hi def link <args> - endif - - HiLink sqlComment Comment - HiLink sqlKeyword sqlSpecial - HiLink sqlNumber Number - HiLink sqlOperator sqlStatement - HiLink sqlSpecial Special - HiLink sqlStatement Statement - HiLink sqlString String - HiLink sqlType Type - HiLink sqlTodo Todo - - delcommand HiLink -endif - +command -nargs=+ HiLink hi def link <args> +HiLink sqlComment Comment +HiLink sqlFunction Function +HiLink sqlKeyword sqlSpecial +HiLink sqlNumber Number +HiLink sqlOperator sqlStatement +HiLink sqlSpecial Special +HiLink sqlStatement Statement +HiLink sqlString String +HiLink sqlType Type +HiLink sqlTodo Todo + +delcommand HiLink let b:current_syntax = "sql" - " vim: ts=8 diff --git a/runtime/syntax/srec.vim b/runtime/syntax/srec.vim new file mode 100644 index 0000000000..19b6d97426 --- /dev/null +++ b/runtime/syntax/srec.vim @@ -0,0 +1,96 @@ +" Vim syntax file +" Language: Motorola S-Record +" Maintainer: Markus Heidelberg <markus.heidelberg@web.de> +" Last Change: 2015 Feb 24 + +" Each record (line) is built as follows: +" +" field digits states +" +" +----------+ +" | start | 1 ('S') srecRecStart +" +----------+ +" | type | 1 srecRecType, (srecRecTypeUnknown) +" +----------+ +" | count | 2 srecByteCount +" +----------+ +" | address | 4/6/8 srecNoAddress, srecDataAddress, srecRecCount, srecStartAddress, (srecAddressFieldUnknown) +" +----------+ +" | data | 0..504/502/500 srecDataOdd, srecDataEven, (srecDataUnexpected) +" +----------+ +" | checksum | 2 srecChecksum +" +----------+ +" +" States in parentheses in the upper format description indicate that they +" should not appear in a valid file. + +" For version 5.x: Clear all syntax items +" For version 6.x: Quit when a syntax file was already loaded +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn match srecRecStart "^S" + +syn match srecRecTypeUnknown "^S." contains=srecRecStart +syn match srecRecType "^S[0-35-9]" contains=srecRecStart + +syn match srecByteCount "^S.[0-9a-fA-F]\{2}" contains=srecRecTypeUnknown nextgroup=srecAddressFieldUnknown,srecChecksum +syn match srecByteCount "^S[0-35-9][0-9a-fA-F]\{2}" contains=srecRecType + +syn match srecAddressFieldUnknown "[0-9a-fA-F]\{2}" contained nextgroup=srecAddressFieldUnknown,srecChecksum + +syn match srecNoAddress "^S0[0-9a-fA-F]\{6}" contains=srecByteCount nextgroup=srecDataOdd,srecChecksum +syn match srecDataAddress "^S1[0-9a-fA-F]\{6}" contains=srecByteCount nextgroup=srecDataOdd,srecChecksum +syn match srecDataAddress "^S2[0-9a-fA-F]\{8}" contains=srecByteCount nextgroup=srecDataOdd,srecChecksum +syn match srecDataAddress "^S3[0-9a-fA-F]\{10}" contains=srecByteCount nextgroup=srecDataOdd,srecChecksum +syn match srecRecCount "^S5[0-9a-fA-F]\{6}" contains=srecByteCount nextgroup=srecDataUnexpected,srecChecksum +syn match srecRecCount "^S6[0-9a-fA-F]\{8}" contains=srecByteCount nextgroup=srecDataUnexpected,srecChecksum +syn match srecStartAddress "^S7[0-9a-fA-F]\{10}" contains=srecByteCount nextgroup=srecDataUnexpected,srecChecksum +syn match srecStartAddress "^S8[0-9a-fA-F]\{8}" contains=srecByteCount nextgroup=srecDataUnexpected,srecChecksum +syn match srecStartAddress "^S9[0-9a-fA-F]\{6}" contains=srecByteCount nextgroup=srecDataUnexpected,srecChecksum + +" alternating highlight per byte for easier reading +syn match srecDataOdd "[0-9a-fA-F]\{2}" contained nextgroup=srecDataEven,srecChecksum +syn match srecDataEven "[0-9a-fA-F]\{2}" contained nextgroup=srecDataOdd,srecChecksum +" data bytes which should not exist +syn match srecDataUnexpected "[0-9a-fA-F]\{2}" contained nextgroup=srecDataUnexpected,srecChecksum +" Data digit pair regex usage also results in only highlighting the checksum +" if the number of data characters is even. + +syn match srecChecksum "[0-9a-fA-F]\{2}$" contained + +" Define the default highlighting. +" For version 5.7 and earlier: only when not done already +" For version 5.8 and later: only when an item doesn't have highlighting yet +if version >= 508 || !exists("did_srec_syntax_inits") + if version < 508 + let did_srec_syntax_inits = 1 + command -nargs=+ HiLink hi link <args> + else + command -nargs=+ HiLink hi def link <args> + endif + + " The default methods for highlighting. Can be overridden later + HiLink srecRecStart srecRecType + HiLink srecRecTypeUnknown srecRecType + HiLink srecRecType WarningMsg + HiLink srecByteCount Constant + hi def srecAddressFieldUnknown term=italic cterm=italic gui=italic + HiLink srecNoAddress DiffAdd + HiLink srecDataAddress Comment + HiLink srecRecCount srecNoAddress + HiLink srecStartAddress srecDataAddress + hi def srecDataOdd term=bold cterm=bold gui=bold + hi def srecDataEven term=NONE cterm=NONE gui=NONE + HiLink srecDataUnexpected Error + HiLink srecChecksum DiffChange + + delcommand HiLink +endif + +let b:current_syntax = "srec" + +" vim: ts=8 diff --git a/runtime/syntax/sshconfig.vim b/runtime/syntax/sshconfig.vim index 6d4de6c64e..479277e17f 100644 --- a/runtime/syntax/sshconfig.vim +++ b/runtime/syntax/sshconfig.vim @@ -1,9 +1,10 @@ " Vim syntax file " Language: OpenSSH client configuration file (ssh_config) " Author: David Necas (Yeti) -" Maintainer: Leonard Ehrenfried <leonard.ehrenfried@web.de> -" Last Change: 2012 Feb 24 -" SSH Version: 5.9p1 +" Maintainer: Dominik Fischer <d dot f dot fischer at web dot de> +" Contributor: Leonard Ehrenfried <leonard.ehrenfried@web.de> +" Last Change: 2015 Dec 3 +" SSH Version: 7.0 " " Setup @@ -100,6 +101,8 @@ syn case ignore " Keywords syn keyword sshconfigHostSect Host +syn keyword sshconfigMatch canonical exec host originalhost user localuser all + syn keyword sshconfigKeyword AddressFamily syn keyword sshconfigKeyword BatchMode syn keyword sshconfigKeyword BindAddress @@ -138,6 +141,7 @@ syn keyword sshconfigKeyword HostKeyAlgorithms syn keyword sshconfigKeyword HostKeyAlias syn keyword sshconfigKeyword HostName syn keyword sshconfigKeyword HostbasedAuthentication +syn keyword sshconfigKeyword HostbasedKeyTypes syn keyword sshconfigKeyword IPQoS syn keyword sshconfigKeyword IdentitiesOnly syn keyword sshconfigKeyword IdentityFile @@ -148,6 +152,7 @@ syn keyword sshconfigKeyword LocalCommand syn keyword sshconfigKeyword LocalForward syn keyword sshconfigKeyword LogLevel syn keyword sshconfigKeyword MACs +syn keyword sshconfigKeyword Match syn keyword sshconfigKeyword NoHostAuthenticationForLocalhost syn keyword sshconfigKeyword NumberOfPasswordPrompts syn keyword sshconfigKeyword PKCS11Provider @@ -157,6 +162,8 @@ syn keyword sshconfigKeyword Port syn keyword sshconfigKeyword PreferredAuthentications syn keyword sshconfigKeyword Protocol syn keyword sshconfigKeyword ProxyCommand +syn keyword sshconfigKeyword ProxyUseFDPass +syn keyword sshconfigKeyword PubkeyAcceptedKeyTypes syn keyword sshconfigKeyword PubkeyAuthentication syn keyword sshconfigKeyword RSAAuthentication syn keyword sshconfigKeyword RekeyLimit @@ -211,6 +218,7 @@ if version >= 508 || !exists("did_sshconfig_syntax_inits") HiLink sshconfigSpecial Special HiLink sshconfigKeyword Keyword HiLink sshconfigHostSect Type + HiLink sshconfigMatch Type delcommand HiLink endif diff --git a/runtime/syntax/sshdconfig.vim b/runtime/syntax/sshdconfig.vim index 53bc09de0d..ac90a80aa5 100644 --- a/runtime/syntax/sshdconfig.vim +++ b/runtime/syntax/sshdconfig.vim @@ -1,11 +1,12 @@ " Vim syntax file " Language: OpenSSH server configuration file (sshd_config) -" Maintainer: David Necas (Yeti) -" Maintainer: Leonard Ehrenfried <leonard.ehrenfried@web.de> -" Modified By: Thilo Six +" Author: David Necas (Yeti) +" Maintainer: Dominik Fischer <d dot f dot fischer at web dot de> +" Contributor: Thilo Six +" Contributor: Leonard Ehrenfried <leonard.ehrenfried@web.de> " Originally: 2009-07-09 -" Last Change: 2011 Oct 31 -" SSH Version: 5.9p1 +" Last Change: 2015 Dec 3 +" SSH Version: 7.0 " " Setup @@ -39,6 +40,12 @@ syn keyword sshdconfigYesNo yes no none syn keyword sshdconfigAddressFamily any inet inet6 +syn keyword sshdconfigPrivilegeSeparation sandbox + +syn keyword sshdconfigTcpForwarding local remote + +syn keyword sshdconfigRootLogin prohibit-password without-password forced-commands-only + syn keyword sshdconfigCipher aes128-cbc 3des-cbc blowfish-cbc cast128-cbc syn keyword sshdconfigCipher aes192-cbc aes256-cbc aes128-ctr aes192-ctr aes256-ctr syn keyword sshdconfigCipher arcfour arcfour128 arcfour256 cast128-cbc @@ -49,7 +56,7 @@ syn keyword sshdconfigMAC hmac-sha2-256 hmac-sha256-96 hmac-sha2-512 syn keyword sshdconfigMAC hmac-sha2-512-96 syn match sshdconfigMAC "\<umac-64@openssh\.com\>" -syn keyword sshdconfigRootLogin without-password forced-commands-only +syn keyword sshdconfigRootLogin prohibit-password without-password forced-commands-only syn keyword sshdconfigLogLevel QUIET FATAL ERROR INFO VERBOSE syn keyword sshdconfigLogLevel DEBUG DEBUG1 DEBUG2 DEBUG3 @@ -99,9 +106,12 @@ syn keyword sshdconfigKeyword AcceptEnv syn keyword sshdconfigKeyword AddressFamily syn keyword sshdconfigKeyword AllowAgentForwarding syn keyword sshdconfigKeyword AllowGroups +syn keyword sshdconfigKeyword AllowStreamLocalForwarding syn keyword sshdconfigKeyword AllowTcpForwarding syn keyword sshdconfigKeyword AllowUsers syn keyword sshdconfigKeyword AuthorizedKeysFile +syn keyword sshdconfigKeyword AuthorizedKeysCommand +syn keyword sshdconfigKeyword AuthorizedKeysCommandUser syn keyword sshdconfigKeyword AuthorizedPrincipalsFile syn keyword sshdconfigKeyword Banner syn keyword sshdconfigKeyword ChallengeResponseAuthentication @@ -122,6 +132,8 @@ syn keyword sshdconfigKeyword GSSAPIStrictAcceptorCheck syn keyword sshdconfigKeyword GatewayPorts syn keyword sshdconfigKeyword HostCertificate syn keyword sshdconfigKeyword HostKey +syn keyword sshdconfigKeyword HostKeyAlgorithms +syn keyword sshdconfigKeyword HostbasedAcceptedKeyTypes syn keyword sshdconfigKeyword HostbasedAuthentication syn keyword sshdconfigKeyword HostbasedUsesNameFromPacketOnly syn keyword sshdconfigKeyword IPQoS @@ -147,15 +159,19 @@ syn keyword sshdconfigKeyword PermitBlacklistedKeys syn keyword sshdconfigKeyword PermitEmptyPasswords syn keyword sshdconfigKeyword PermitOpen syn keyword sshdconfigKeyword PermitRootLogin +syn keyword sshdconfigKeyword PermitTTY syn keyword sshdconfigKeyword PermitTunnel syn keyword sshdconfigKeyword PermitUserEnvironment +syn keyword sshdconfigKeyword PermitUserRC syn keyword sshdconfigKeyword PidFile syn keyword sshdconfigKeyword Port syn keyword sshdconfigKeyword PrintLastLog syn keyword sshdconfigKeyword PrintMotd syn keyword sshdconfigKeyword Protocol +syn keyword sshdconfigKeyword PubkeyAcceptedKeyTypes syn keyword sshdconfigKeyword PubkeyAuthentication syn keyword sshdconfigKeyword RSAAuthentication +syn keyword sshdconfigKeyword RekeyLimit syn keyword sshdconfigKeyword RevokedKeys syn keyword sshdconfigKeyword RhostsRSAAuthentication syn keyword sshdconfigKeyword ServerKeyBits @@ -169,6 +185,7 @@ syn keyword sshdconfigKeyword UseDNS syn keyword sshdconfigKeyword UseLogin syn keyword sshdconfigKeyword UsePAM syn keyword sshdconfigKeyword UsePrivilegeSeparation +syn keyword sshdconfigKeyword VersionAddendum syn keyword sshdconfigKeyword X11DisplayOffset syn keyword sshdconfigKeyword X11Forwarding syn keyword sshdconfigKeyword X11UseLocalhost @@ -184,29 +201,32 @@ if version >= 508 || !exists("did_sshdconfig_syntax_inits") command -nargs=+ HiLink hi def link <args> endif - HiLink sshdconfigComment Comment - HiLink sshdconfigTodo Todo - HiLink sshdconfigHostPort sshdconfigConstant - HiLink sshdconfigTime sshdconfigConstant - HiLink sshdconfigNumber sshdconfigConstant - HiLink sshdconfigConstant Constant - HiLink sshdconfigYesNo sshdconfigEnum - HiLink sshdconfigAddressFamily sshdconfigEnum - HiLink sshdconfigCipher sshdconfigEnum - HiLink sshdconfigMAC sshdconfigEnum - HiLink sshdconfigRootLogin sshdconfigEnum - HiLink sshdconfigLogLevel sshdconfigEnum - HiLink sshdconfigSysLogFacility sshdconfigEnum - HiLink sshdconfigVar sshdconfigEnum - HiLink sshdconfigCompression sshdconfigEnum - HiLink sshdconfigIPQoS sshdconfigEnum - HiLink sshdconfigKexAlgo sshdconfigEnum - HiLink sshdconfigTunnel sshdconfigEnum - HiLink sshdconfigSubsystem sshdconfigEnum - HiLink sshdconfigEnum Function - HiLink sshdconfigSpecial Special - HiLink sshdconfigKeyword Keyword - HiLink sshdconfigMatch Type + HiLink sshdconfigComment Comment + HiLink sshdconfigTodo Todo + HiLink sshdconfigHostPort sshdconfigConstant + HiLink sshdconfigTime sshdconfigConstant + HiLink sshdconfigNumber sshdconfigConstant + HiLink sshdconfigConstant Constant + HiLink sshdconfigYesNo sshdconfigEnum + HiLink sshdconfigAddressFamily sshdconfigEnum + HiLink sshdconfigPrivilegeSeparation sshdconfigEnum + HiLink sshdconfigTcpForwarding sshdconfigEnum + HiLink sshdconfigRootLogin sshdconfigEnum + HiLink sshdconfigCipher sshdconfigEnum + HiLink sshdconfigMAC sshdconfigEnum + HiLink sshdconfigRootLogin sshdconfigEnum + HiLink sshdconfigLogLevel sshdconfigEnum + HiLink sshdconfigSysLogFacility sshdconfigEnum + HiLink sshdconfigVar sshdconfigEnum + HiLink sshdconfigCompression sshdconfigEnum + HiLink sshdconfigIPQoS sshdconfigEnum + HiLink sshdconfigKexAlgo sshdconfigEnum + HiLink sshdconfigTunnel sshdconfigEnum + HiLink sshdconfigSubsystem sshdconfigEnum + HiLink sshdconfigEnum Function + HiLink sshdconfigSpecial Special + HiLink sshdconfigKeyword Keyword + HiLink sshdconfigMatch Type delcommand HiLink endif diff --git a/runtime/syntax/strace.vim b/runtime/syntax/strace.vim index 80cd262efc..0f686fd808 100644 --- a/runtime/syntax/strace.vim +++ b/runtime/syntax/strace.vim @@ -1,9 +1,8 @@ " Vim syntax file " This is a GENERATED FILE. Please always refer to source file at the URI below. " Language: strace output -" Maintainer: David Ne\v{c}as (Yeti) <yeti@physics.muni.cz> -" Last Change: 2002-10-10 -" URL: http://trific.ath.cx/Ftp/vim/syntax/strace.vim +" Maintainer: David Necas (Yeti) <yeti@physics.muni.cz> +" Last Change: 2015-01-16 " Setup if version >= 600 @@ -17,7 +16,7 @@ endif syn case match " Parse the line -syn match straceSpecialChar "\\\d\d\d\|\\." contained +syn match straceSpecialChar "\\\o\{1,3}\|\\." contained syn region straceString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=straceSpecialChar oneline syn match straceNumber "\W[+-]\=\(\d\+\)\=\.\=\d\+\([eE][+-]\=\d\+\)\="lc=1 syn match straceNumber "\W0x\x\+"lc=1 diff --git a/runtime/syntax/synload.vim b/runtime/syntax/synload.vim index 48b5956b3c..6183f33a59 100644 --- a/runtime/syntax/synload.vim +++ b/runtime/syntax/synload.vim @@ -60,8 +60,8 @@ fun! s:SynSet() endfun -" Handle adding doxygen to other languages (C, C++, C#, IDL) -au Syntax c,cpp,cs,idl,php +" Handle adding doxygen to other languages (C, C++, C#, IDL, java, php, DataScript) +au Syntax c,cpp,cs,idl,java,php,datascript \ if (exists('b:load_doxygen_syntax') && b:load_doxygen_syntax) \ || (exists('g:load_doxygen_syntax') && g:load_doxygen_syntax) \ | runtime! syntax/doxygen.vim diff --git a/runtime/syntax/systemd.vim b/runtime/syntax/systemd.vim new file mode 100644 index 0000000000..5dfba74408 --- /dev/null +++ b/runtime/syntax/systemd.vim @@ -0,0 +1,8 @@ +" Vim syntax file +" Language: systemd.unit(5) + +if !exists('b:current_syntax') + " Looks a lot like dosini files. + runtime! syntax/dosini.vim + let b:current_syntax = 'systemd' +endif diff --git a/runtime/syntax/tags.vim b/runtime/syntax/tags.vim index 051d65aa6e..c86f17a618 100644 --- a/runtime/syntax/tags.vim +++ b/runtime/syntax/tags.vim @@ -1,8 +1,8 @@ " Language: tags -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchip@PcampbellAfamily.Mbiz> -" Last Change: Sep 06, 2005 -" Version: 3 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Maintainer: Charles E. Campbell <NdrOchip@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 4 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_TAGS " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/teraterm.vim b/runtime/syntax/teraterm.vim new file mode 100644 index 0000000000..521331d8ce --- /dev/null +++ b/runtime/syntax/teraterm.vim @@ -0,0 +1,139 @@ +" Vim syntax file +" Language: Tera Term Language (TTL) +" Based on Tera Term Version 4.86 +" Maintainer: Ken Takata +" URL: https://github.com/k-takata/vim-teraterm +" Last Change: 2015 Jun 24 +" Filenames: *.ttl +" License: VIM License + +if exists("b:current_syntax") + finish +endif + +let s:save_cpo = &cpo +set cpo&vim + +syn case ignore + +syn region ttlComment start=";" end="$" contains=@Spell +syn region ttlComment start="/\*" end="\*/" contains=@Spell +syn region ttlFirstComment start="/\*" end="\*/" contained contains=@Spell + \ nextgroup=ttlStatement,ttlFirstComment + +syn match ttlCharacter "#\%(\d\+\|\$\x\+\)\>" +syn match ttlNumber "\%(\<\d\+\|\$\x\+\)\>" +syn match ttlString "'[^']*'" contains=@Spell +syn match ttlString '"[^"]*"' contains=@Spell +syn cluster ttlConstant contains=ttlCharacter,ttlNumber,ttlString + +syn match ttlLabel ":\s*\w\{1,32}\>" + +syn keyword ttlOperator and or xor not + +syn match ttlVar "\<groupmatchstr\d\>" +syn match ttlVar "\<param\d\>" +syn keyword ttlVar inputstr matchstr paramcnt result timeout mtimeout + + +syn match ttlLine nextgroup=ttlStatement "^" +syn match ttlStatement contained "\s*" + \ nextgroup=ttlIf,ttlElseIf,ttlConditional,ttlRepeat, + \ ttlFirstComment,ttlComment,ttlLabel,@ttlCommand + +syn cluster ttlCommand contains=ttlControlCommand,ttlCommunicationCommand, + \ ttlStringCommand,ttlFileCommand,ttlPasswordCommand, + \ ttlMiscCommand + + +syn keyword ttlIf contained nextgroup=ttlIfExpression if +syn keyword ttlElseIf contained nextgroup=ttlElseIfExpression elseif + +syn match ttlIfExpression contained "\s.*" + \ contains=@ttlConstant,ttlVar,ttlOperator,ttlComment,ttlThen, + \ @ttlCommand +syn match ttlElseIfExpression contained "\s.*" + \ contains=@ttlConstant,ttlVar,ttlOperator,ttlComment,ttlThen + +syn keyword ttlThen contained then +syn keyword ttlConditional contained else endif + +syn keyword ttlRepeat contained for next until enduntil while endwhile +syn match ttlRepeat contained + \ "\<\%(do\|loop\)\%(\s\+\%(while\|until\)\)\?\>" +syn keyword ttlControlCommand contained + \ break call continue end execcmnd exit goto include + \ mpause pause return + + +syn keyword ttlCommunicationCommand contained + \ bplusrecv bplussend callmenu changedir clearscreen + \ closett connect cygconnect disconnect dispstr + \ enablekeyb flushrecv gethostname getmodemstatus + \ gettitle kmtfinish kmtget kmtrecv kmtsend loadkeymap + \ logautoclosemode logclose loginfo logopen logpause + \ logrotate logstart logwrite quickvanrecv + \ quickvansend recvln restoresetup scprecv scpsend + \ send sendbreak sendbroadcast sendfile sendkcode + \ sendln sendlnbroadcast sendmulticast setbaud + \ setdebug setdtr setecho setmulticastname setrts + \ setsync settitle showtt testlink unlink wait + \ wait4all waitevent waitln waitn waitrecv waitregex + \ xmodemrecv xmodemsend ymodemrecv ymodemsend + \ zmodemrecv zmodemsend +syn keyword ttlStringCommand contained + \ code2str expandenv int2str regexoption sprintf + \ sprintf2 str2code str2int strcompare strconcat + \ strcopy strinsert strjoin strlen strmatch strremove + \ strreplace strscan strspecial strsplit strtrim + \ tolower toupper +syn keyword ttlFileCommand contained + \ basename dirname fileclose fileconcat filecopy + \ filecreate filedelete filelock filemarkptr fileopen + \ filereadln fileread filerename filesearch fileseek + \ fileseekback filestat filestrseek filestrseek2 + \ filetruncate fileunlock filewrite filewriteln + \ findfirst findnext findclose foldercreate + \ folderdelete foldersearch getdir getfileattr makepath + \ setdir setfileattr +syn keyword ttlPasswordCommand contained + \ delpassword getpassword ispassword passwordbox + \ setpassword +syn keyword ttlMiscCommand contained + \ beep bringupbox checksum8 checksum8file checksum16 + \ checksum16file checksum32 checksum32file closesbox + \ clipb2var crc16 crc16file crc32 crc32file exec + \ dirnamebox filenamebox getdate getenv getipv4addr + \ getipv6addr getspecialfolder gettime getttdir getver + \ ifdefined inputbox intdim listbox messagebox random + \ rotateleft rotateright setdate setdlgpos setenv + \ setexitcode settime show statusbox strdim uptime + \ var2clipb yesnobox + + +hi def link ttlCharacter Character +hi def link ttlNumber Number +hi def link ttlComment Comment +hi def link ttlFirstComment Comment +hi def link ttlString String +hi def link ttlLabel Label +hi def link ttlIf Conditional +hi def link ttlElseIf Conditional +hi def link ttlThen Conditional +hi def link ttlConditional Conditional +hi def link ttlRepeat Repeat +hi def link ttlControlCommand Keyword +hi def link ttlVar Identifier +hi def link ttlOperator Operator +hi def link ttlCommunicationCommand Keyword +hi def link ttlStringCommand Keyword +hi def link ttlFileCommand Keyword +hi def link ttlPasswordCommand Keyword +hi def link ttlMiscCommand Keyword + +let b:current_syntax = "teraterm" + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim: ts=8 sw=2 sts=2 diff --git a/runtime/syntax/tex.vim b/runtime/syntax/tex.vim index 3f719e8244..d31e14bed0 100644 --- a/runtime/syntax/tex.vim +++ b/runtime/syntax/tex.vim @@ -1,8 +1,8 @@ " Vim syntax file " Language: TeX " Maintainer: Charles E. Campbell <NdrchipO@ScampbellPfamily.AbizM> -" Last Change: Sep 09, 2014 -" Version: 82 +" Last Change: Oct 20, 2015 +" Version: 90 " URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_TEX " " Notes: {{{1 @@ -62,9 +62,6 @@ if version >= 508 || !exists("did_tex_syntax_inits") command -nargs=+ HiLink hi def link <args> endif endif -if exists("g:tex_no_error") && g:tex_no_error - let s:tex_no_error= 1 -endif " by default, enable all region-based highlighting let s:tex_fast= "bcmMprsSvV" @@ -76,9 +73,6 @@ if exists("g:tex_fast") else let s:tex_fast= g:tex_fast endif - let s:tex_no_error= 1 -else - let s:tex_fast= "bcmMprsSvV" endif " let user determine which classes of concealment will be supported @@ -113,14 +107,21 @@ endif " handle folding {{{1 if !exists("g:tex_fold_enabled") - let g:tex_fold_enabled= 0 + let s:tex_fold_enabled= 0 elseif g:tex_fold_enabled && !has("folding") - let g:tex_fold_enabled= 0 + let s:tex_fold_enabled= 0 echomsg "Ignoring g:tex_fold_enabled=".g:tex_fold_enabled."; need to re-compile vim for +fold support" +else + let s:tex_fold_enabled= 1 endif -if g:tex_fold_enabled && &fdm == "manual" +if s:tex_fold_enabled && &fdm == "manual" setl fdm=syntax endif +if s:tex_fold_enabled && has("folding") + com! -nargs=* TexFold <args> fold +else + com! -nargs=* TexFold <args> +endif " (La)TeX keywords: uses the characters 0-9,a-z,A-Z,192-255 only... {{{1 " but _ is the only one that causes problems. @@ -134,40 +135,53 @@ endif if b:tex_stylish setlocal isk+=@-@ endif -if exists("g:tex_nospell") && g:tex_nospell && !exists("g:tex_comment_nospell") - let g:tex_comment_nospell= 1 +if exists("g:tex_no_error") && g:tex_no_error + let s:tex_no_error= 1 +else + let s:tex_no_error= 0 +endif +if exists("g:tex_comment_nospell") && g:tex_comment_nospell + let s:tex_comment_nospell= 1 +else + let s:tex_comment_nospell= 0 +endif +if exists("g:tex_nospell") && g:tex_nospell + let s:tex_nospell = 1 +else + let s:tex_nospell = 0 endif " Clusters: {{{1 " -------- -syn cluster texCmdGroup contains=texCmdBody,texComment,texDefParm,texDelimiter,texDocType,texInput,texLength,texLigature,texMathDelim,texMathOper,texNewCmd,texNewEnv,texRefZone,texSection,texBeginEnd,texBeginEndName,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle -if !exists("s:tex_no_error") - syn cluster texCmdGroup add=texMathError +syn cluster texCmdGroup contains=texCmdBody,texComment,texDefParm,texDelimiter,texDocType,texInput,texLength,texLigature,texMathDelim,texMathOper,texNewCmd,texNewEnv,texRefZone,texSection,texBeginEnd,texBeginEndName,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle +if !s:tex_no_error + syn cluster texCmdGroup add=texMathError endif -syn cluster texEnvGroup contains=texMatcher,texMathDelim,texSpecialChar,texStatement -syn cluster texFoldGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texBoldStyle,texItalStyle,texNoSpell -syn cluster texBoldGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texBoldStyle,texBoldItalStyle,texNoSpell -syn cluster texItalGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texItalStyle,texItalBoldStyle,texNoSpell -if !exists("g:tex_nospell") || !g:tex_nospell - syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,@Spell - syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texStyleStatement,@Spell,texStyleMatcher +syn cluster texEnvGroup contains=texMatcher,texMathDelim,texSpecialChar,texStatement +syn cluster texFoldGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texBoldStyle,texItalStyle,texNoSpell +syn cluster texBoldGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texBoldStyle,texBoldItalStyle,texNoSpell +syn cluster texItalGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texItalStyle,texItalBoldStyle,texNoSpell +if !s:tex_nospell + syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,@Spell + syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texStyleStatement,@Spell,texStyleMatcher else - syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption - syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher + syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption + syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher endif -syn cluster texPreambleMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texMathZoneZ -syn cluster texRefGroup contains=texMatcher,texComment,texDelimiter -if !exists("tex_no_math") - syn cluster texMathZones contains=texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ - syn cluster texMatchGroup add=@texMathZones - syn cluster texMathDelimGroup contains=texMathDelimBad,texMathDelimKey,texMathDelimSet1,texMathDelimSet2 - syn cluster texMathMatchGroup contains=@texMathZones,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMathDelim,texMathMatcher,texMathOper,texNewCmd,texNewEnv,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone - syn cluster texMathZoneGroup contains=texComment,texDelimiter,texLength,texMathDelim,texMathMatcher,texMathOper,texMathSymbol,texMathText,texRefZone,texSpecialChar,texStatement,texTypeSize,texTypeStyle - if !exists("s:tex_no_error") - syn cluster texMathMatchGroup add=texMathError - syn cluster texMathZoneGroup add=texMathError +syn cluster texPreambleMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTitle,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texMathZoneZ +syn cluster texRefGroup contains=texMatcher,texComment,texDelimiter +if !exists("g:tex_no_math") + syn cluster texPreambleMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTitle,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texMathZoneZ + syn cluster texMathZones contains=texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ + syn cluster texMatchGroup add=@texMathZones + syn cluster texMathDelimGroup contains=texMathDelimBad,texMathDelimKey,texMathDelimSet1,texMathDelimSet2 + syn cluster texMathMatchGroup contains=@texMathZones,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMathDelim,texMathMatcher,texMathOper,texNewCmd,texNewEnv,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone + syn cluster texMathZoneGroup contains=texComment,texDelimiter,texLength,texMathDelim,texMathMatcher,texMathOper,texMathSymbol,texMathText,texRefZone,texSpecialChar,texStatement,texTypeSize,texTypeStyle + if !s:tex_no_error + syn cluster texMathMatchGroup add=texMathError + syn cluster texMathZoneGroup add=texMathError endif - syn cluster texMathZoneGroup add=@NoSpell + syn cluster texMathZoneGroup add=@NoSpell " following used in the \part \chapter \section \subsection \subsubsection " \paragraph \subparagraph \author \title highlighting syn cluster texDocGroup contains=texPartZone,@texPartGroup @@ -178,35 +192,35 @@ if !exists("tex_no_math") syn cluster texSubSubSectionGroup contains=texParaZone syn cluster texParaGroup contains=texSubParaZone if has("conceal") && &enc == 'utf-8' - syn cluster texMathZoneGroup add=texGreek,texSuperscript,texSubscript,texMathSymbol - syn cluster texMathMatchGroup add=texGreek,texSuperscript,texSubscript,texMathSymbol + syn cluster texMathZoneGroup add=texGreek,texSuperscript,texSubscript,texMathSymbol + syn cluster texMathMatchGroup add=texGreek,texSuperscript,texSubscript,texMathSymbol endif endif " Try to flag {} and () mismatches: {{{1 if s:tex_fast =~ 'm' - if !exists("s:tex_no_error") - syn region texMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" contains=@texMatchGroup,texError - syn region texMatcher matchgroup=Delimiter start="\[" end="]" contains=@texMatchGroup,texError,@NoSpell + if !s:tex_no_error + syn region texMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchGroup,texError + syn region texMatcher matchgroup=Delimiter start="\[" end="]" transparent contains=@texMatchGroup,texError,@NoSpell else - syn region texMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" contains=@texMatchGroup - syn region texMatcher matchgroup=Delimiter start="\[" end="]" contains=@texMatchGroup + syn region texMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchGroup + syn region texMatcher matchgroup=Delimiter start="\[" end="]" transparent contains=@texMatchGroup endif - if !exists("g:tex_nospell") || !g:tex_nospell - syn region texParen start="(" end=")" contains=@texMatchGroup,@Spell + if !s:tex_nospell + syn region texParen start="(" end=")" transparent contains=@texMatchGroup,@Spell else - syn region texParen start="(" end=")" contains=@texMatchGroup + syn region texParen start="(" end=")" transparent contains=@texMatchGroup endif endif -if !exists("s:tex_no_error") +if !s:tex_no_error syn match texError "[}\])]" endif if s:tex_fast =~ 'M' - if !exists("tex_no_math") - if !exists("s:tex_no_error") + if !exists("g:tex_no_math") + if !s:tex_no_error syn match texMathError "}" contained endif - syn region texMathMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\}" end="}" end="%stopzone\>" contained contains=@texMathMatchGroup + syn region texMathMatcher matchgroup=Delimiter start="{" skip="\%(\\\\\)*\\}" end="}" end="%stopzone\>" contained contains=@texMathMatchGroup endif endif @@ -217,7 +231,7 @@ if exists("g:tex_tex") || b:tex_stylish syn match texStatement "\\[a-zA-Z@]\+" else syn match texStatement "\\\a\+" - if !exists("s:tex_no_error") + if !s:tex_no_error syn match texError "\\\a*@[a-zA-Z@]*" endif endif @@ -225,7 +239,6 @@ endif " TeX/LaTeX delimiters: {{{1 syn match texDelimiter "&" syn match texDelimiter "\\\\" -syn match texDelimiter "[{}]" " Tex/Latex Options: {{{1 syn match texOption "[^\\]\zs#\d\+\|^#\d\+" @@ -246,7 +259,7 @@ syn match texLigature "\\\([ijolL]\|ae\|oe\|ss\|AA\|AE\|OE\)$" " \begin{}/\end{} section markers: {{{1 syn match texBeginEnd "\\begin\>\|\\end\>" nextgroup=texBeginEndName if s:tex_fast =~ 'm' - syn region texBeginEndName matchgroup=Delimiter start="{" end="}" contained nextgroup=texBeginEndModifier contains=texComment + syn region texBeginEndName matchgroup=Delimiter start="{" end="}" contained nextgroup=texBeginEndModifier contains=texComment syn region texBeginEndModifier matchgroup=Delimiter start="\[" end="]" contained contains=texComment,@NoSpell endif @@ -257,7 +270,7 @@ if s:tex_fast =~ 'm' endif " Preamble syntax-based folding support: {{{1 -if g:tex_fold_enabled && has("folding") +if s:tex_fold_enabled && has("folding") syn region texPreamble transparent fold start='\zs\\documentclass\>' end='\ze\\begin{document}' contains=texStyle,@texPreambleMatchGroup endif @@ -335,76 +348,57 @@ syn match texSpaceCodeChar "`\\\=.\(\^.\)\==\(\d\|\"\x\{1,6}\|`.\)" contained " Sections, subsections, etc: {{{1 if s:tex_fast =~ 'p' - if !exists("g:tex_nospell") || !g:tex_nospell - if g:tex_fold_enabled && has("folding") - syn region texDocZone matchgroup=texSection start='\\begin\s*{\s*document\s*}' end='\\end\s*{\s*document\s*}' fold contains=@texFoldGroup,@texDocGroup,@Spell - syn region texPartZone matchgroup=texSection start='\\part\>' end='\ze\s*\\\%(part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texPartGroup,@Spell - syn region texChapterZone matchgroup=texSection start='\\chapter\>' end='\ze\s*\\\%(chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texChapterGroup,@Spell - syn region texSectionZone matchgroup=texSection start='\\section\>' end='\ze\s*\\\%(section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texSectionGroup,@Spell - syn region texSubSectionZone matchgroup=texSection start='\\subsection\>' end='\ze\s*\\\%(\%(sub\)\=section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texSubSectionGroup,@Spell - syn region texSubSubSectionZone matchgroup=texSection start='\\subsubsection\>' end='\ze\s*\\\%(\%(sub\)\{,2}section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texSubSubSectionGroup,@Spell - syn region texParaZone matchgroup=texSection start='\\paragraph\>' end='\ze\s*\\\%(paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texParaGroup,@Spell - syn region texSubParaZone matchgroup=texSection start='\\subparagraph\>' end='\ze\s*\\\%(\%(sub\)\=paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@Spell - syn region texTitle matchgroup=texSection start='\\\%(author\|title\)\>\s*{' end='}' fold contains=@texFoldGroup,@Spell - syn region texAbstract matchgroup=texSection start='\\begin\s*{\s*abstract\s*}' end='\\end\s*{\s*abstract\s*}' fold contains=@texFoldGroup,@Spell - else - syn region texDocZone matchgroup=texSection start='\\begin\s*{\s*document\s*}' end='\\end\s*{\s*document\s*}' contains=@texFoldGroup,@texDocGroup,@Spell - syn region texPartZone matchgroup=texSection start='\\part\>' end='\ze\s*\\\%(part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texPartGroup,@Spell - syn region texChapterZone matchgroup=texSection start='\\chapter\>' end='\ze\s*\\\%(chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texChapterGroup,@Spell - syn region texSectionZone matchgroup=texSection start='\\section\>' end='\ze\s*\\\%(section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSectionGroup,@Spell - syn region texSubSectionZone matchgroup=texSection start='\\subsection\>' end='\ze\s*\\\%(\%(sub\)\=section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSectionGroup,@Spell - syn region texSubSubSectionZone matchgroup=texSection start='\\subsubsection\>' end='\ze\s*\\\%(\%(sub\)\{,2}section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSubSectionGroup,@Spell - syn region texParaZone matchgroup=texSection start='\\paragraph\>' end='\ze\s*\\\%(paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texParaGroup,@Spell - syn region texSubParaZone matchgroup=texSection start='\\subparagraph\>' end='\ze\s*\\\%(\%(sub\)\=paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@Spell - syn region texTitle matchgroup=texSection start='\\\%(author\|title\)\>\s*{' end='}' contains=@texFoldGroup,@Spell - syn region texAbstract matchgroup=texSection start='\\begin\s*{\s*abstract\s*}' end='\\end\s*{\s*abstract\s*}' contains=@texFoldGroup,@Spell - endif - else - if g:tex_fold_enabled && has("folding") - syn region texDocZone matchgroup=texSection start='\\begin\s*{\s*document\s*}' end='\\end\s*{\s*document\s*}' fold contains=@texFoldGroup,@texDocGroup - syn region texPartZone matchgroup=texSection start='\\part\>' end='\ze\s*\\\%(part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texPartGroup - syn region texChapterZone matchgroup=texSection start='\\chapter\>' end='\ze\s*\\\%(chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texChapterGroup - syn region texSectionZone matchgroup=texSection start='\\section\>' end='\ze\s*\\\%(section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texSectionGroup - syn region texSubSectionZone matchgroup=texSection start='\\subsection\>' end='\ze\s*\\\%(\%(sub\)\=section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texSubSectionGroup - syn region texSubSubSectionZone matchgroup=texSection start='\\subsubsection\>' end='\ze\s*\\\%(\%(sub\)\{,2}section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texSubSubSectionGroup - syn region texParaZone matchgroup=texSection start='\\paragraph\>' end='\ze\s*\\\%(paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup,@texParaGroup - syn region texSubParaZone matchgroup=texSection start='\\subparagraph\>' end='\ze\s*\\\%(\%(sub\)\=paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' fold contains=@texFoldGroup - syn region texTitle matchgroup=texSection start='\\\%(author\|title\)\>\s*{' end='}' fold contains=@texFoldGroup - syn region texAbstract matchgroup=texSection start='\\begin\s*{\s*abstract\s*}' end='\\end\s*{\s*abstract\s*}' fold contains=@texFoldGroup - else - syn region texDocZone matchgroup=texSection start='\\begin\s*{\s*document\s*}' end='\\end\s*{\s*document\s*}' contains=@texFoldGroup,@texDocGroup - syn region texPartZone matchgroup=texSection start='\\part\>' end='\ze\s*\\\%(part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texPartGroup - syn region texChapterZone matchgroup=texSection start='\\chapter\>' end='\ze\s*\\\%(chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texChapterGroup - syn region texSectionZone matchgroup=texSection start='\\section\>' end='\ze\s*\\\%(section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSectionGroup - syn region texSubSectionZone matchgroup=texSection start='\\subsection\>' end='\ze\s*\\\%(\%(sub\)\=section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSectionGroup - syn region texSubSubSectionZone matchgroup=texSection start='\\subsubsection\>' end='\ze\s*\\\%(\%(sub\)\{,2}section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSubSectionGroup - syn region texParaZone matchgroup=texSection start='\\paragraph\>' end='\ze\s*\\\%(paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texParaGroup - syn region texSubParaZone matchgroup=texSection start='\\subparagraph\>' end='\ze\s*\\\%(\%(sub\)\=paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup - syn region texTitle matchgroup=texSection start='\\\%(author\|title\)\>\s*{' end='}' contains=@texFoldGroup - syn region texAbstract matchgroup=texSection start='\\begin\s*{\s*abstract\s*}' end='\\end\s*{\s*abstract\s*}' contains=@texFoldGroup - endif + if !s:tex_nospell + TexFold syn region texDocZone matchgroup=texSection start='\\begin\s*{\s*document\s*}' end='\\end\s*{\s*document\s*}' contains=@texFoldGroup,@texDocGroup,@Spell + TexFold syn region texPartZone matchgroup=texSection start='\\part\>' end='\ze\s*\\\%(part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texPartGroup,@Spell + TexFold syn region texChapterZone matchgroup=texSection start='\\chapter\>' end='\ze\s*\\\%(chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texChapterGroup,@Spell + TexFold syn region texSectionZone matchgroup=texSection start='\\section\>' end='\ze\s*\\\%(section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSectionGroup,@Spell + TexFold syn region texSubSectionZone matchgroup=texSection start='\\subsection\>' end='\ze\s*\\\%(\%(sub\)\=section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSectionGroup,@Spell + TexFold syn region texSubSubSectionZone matchgroup=texSection start='\\subsubsection\>' end='\ze\s*\\\%(\%(sub\)\{,2}section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSubSectionGroup,@Spell + TexFold syn region texParaZone matchgroup=texSection start='\\paragraph\>' end='\ze\s*\\\%(paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texParaGroup,@Spell + TexFold syn region texSubParaZone matchgroup=texSection start='\\subparagraph\>' end='\ze\s*\\\%(\%(sub\)\=paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@Spell + TexFold syn region texTitle matchgroup=texSection start='\\\%(author\|title\)\>\s*{' end='}' contains=@texFoldGroup,@Spell + TexFold syn region texAbstract matchgroup=texSection start='\\begin\s*{\s*abstract\s*}' end='\\end\s*{\s*abstract\s*}' contains=@texFoldGroup,@Spell + else + TexFold syn region texDocZone matchgroup=texSection start='\\begin\s*{\s*document\s*}' end='\\end\s*{\s*document\s*}' contains=@texFoldGroup,@texDocGroup + TexFold syn region texPartZone matchgroup=texSection start='\\part\>' end='\ze\s*\\\%(part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texPartGroup + TexFold syn region texChapterZone matchgroup=texSection start='\\chapter\>' end='\ze\s*\\\%(chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texChapterGroup + TexFold syn region texSectionZone matchgroup=texSection start='\\section\>' end='\ze\s*\\\%(section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSectionGroup + TexFold syn region texSubSectionZone matchgroup=texSection start='\\subsection\>' end='\ze\s*\\\%(\%(sub\)\=section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSectionGroup + TexFold syn region texSubSubSectionZone matchgroup=texSection start='\\subsubsection\>' end='\ze\s*\\\%(\%(sub\)\{,2}section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texSubSubSectionGroup + TexFold syn region texParaZone matchgroup=texSection start='\\paragraph\>' end='\ze\s*\\\%(paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup,@texParaGroup + TexFold syn region texSubParaZone matchgroup=texSection start='\\subparagraph\>' end='\ze\s*\\\%(\%(sub\)\=paragraph\>\|\%(sub\)*section\>\|chapter\>\|part\>\|end\s*{\s*document\s*}\)' contains=@texFoldGroup + TexFold syn region texTitle matchgroup=texSection start='\\\%(author\|title\)\>\s*{' end='}' contains=@texFoldGroup + TexFold syn region texAbstract matchgroup=texSection start='\\begin\s*{\s*abstract\s*}' end='\\end\s*{\s*abstract\s*}' contains=@texFoldGroup endif endif " particular support for bold and italic {{{1 if s:tex_fast =~ 'b' if s:tex_conceal =~ 'b' - syn region texBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" end="}" concealends contains=@texBoldGroup - syn region texBoldItalStyle matchgroup=texTypeStyle start="\\textit\s*{" end="}" concealends contains=@texItalGroup - syn region texItalStyle matchgroup=texTypeStyle start="\\textit\s*{" end="}" concealends contains=@texItalGroup - syn region texItalBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" end="}" concealends contains=@texBoldGroup + if !exists("g:tex_nospell") || !g:tex_nospell + syn region texBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup,@Spell + syn region texBoldItalStyle matchgroup=texTypeStyle start="\\textit\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup,@Spell + syn region texItalStyle matchgroup=texTypeStyle start="\\textit\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup,@Spell + syn region texItalBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup,@Spell + else + syn region texBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup + syn region texBoldItalStyle matchgroup=texTypeStyle start="\\textit\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup + syn region texItalStyle matchgroup=texTypeStyle start="\\textit\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup + syn region texItalBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup + endif endif endif " Bad Math (mismatched): {{{1 -if !exists("g:tex_no_math") && !exists("s:tex_no_error") +if !exists("g:tex_no_math") && !s:tex_no_error syn match texBadMath "\\end\s*{\s*\(array\|gathered\|bBpvV]matrix\|split\|subequations\|smallmatrix\|xxalignat\)\s*}" syn match texBadMath "\\end\s*{\s*\(align\|alignat\|displaymath\|displaymath\|eqnarray\|equation\|flalign\|gather\|math\|multline\|xalignat\)\*\=\s*}" syn match texBadMath "\\[\])]" endif " Math Zones: {{{1 -if !exists("tex_no_math") +if !exists("g:tex_no_math") " TexNewMathZone: function creates a mathzone with the given suffix and mathzone name. {{{2 " Starred forms are created if starform is true. Starred " forms have syntax group and synchronization groups with a @@ -412,7 +406,7 @@ if !exists("tex_no_math") fun! TexNewMathZone(sfx,mathzone,starform) let grpname = "texMathZone".a:sfx let syncname = "texSyncMathZone".a:sfx - if g:tex_fold_enabled + if s:tex_fold_enabled let foldcmd= " fold" else let foldcmd= "" @@ -454,17 +448,17 @@ if !exists("tex_no_math") " Inline Math Zones: {{{2 if s:tex_fast =~ 'M' if has("conceal") && &enc == 'utf-8' && s:tex_conceal =~ 'd' - syn region texMathZoneV matchgroup=Delimiter start="\\(" matchgroup=Delimiter end="\\)\|%stopzone\>" keepend concealends contains=@texMathZoneGroup - syn region texMathZoneW matchgroup=Delimiter start="\\\[" matchgroup=Delimiter end="\\]\|%stopzone\>" keepend concealends contains=@texMathZoneGroup - syn region texMathZoneX matchgroup=Delimiter start="\$" skip="\\\\\|\\\$" matchgroup=Delimiter end="\$" end="%stopzone\>" concealends contains=@texMathZoneGroup - syn region texMathZoneY matchgroup=Delimiter start="\$\$" matchgroup=Delimiter end="\$\$" end="%stopzone\>" concealends keepend contains=@texMathZoneGroup + syn region texMathZoneV matchgroup=Delimiter start="\\(" matchgroup=Delimiter end="\\)\|%stopzone\>" keepend concealends contains=@texMathZoneGroup + syn region texMathZoneW matchgroup=Delimiter start="\\\[" matchgroup=Delimiter end="\\]\|%stopzone\>" keepend concealends contains=@texMathZoneGroup + syn region texMathZoneX matchgroup=Delimiter start="\$" skip="\\\\\|\\\$" matchgroup=Delimiter end="\$" end="%stopzone\>" concealends contains=@texMathZoneGroup + syn region texMathZoneY matchgroup=Delimiter start="\$\$" matchgroup=Delimiter end="\$\$" end="%stopzone\>" keepend concealends contains=@texMathZoneGroup else - syn region texMathZoneV matchgroup=Delimiter start="\\(" matchgroup=Delimiter end="\\)\|%stopzone\>" keepend contains=@texMathZoneGroup - syn region texMathZoneW matchgroup=Delimiter start="\\\[" matchgroup=Delimiter end="\\]\|%stopzone\>" keepend contains=@texMathZoneGroup - syn region texMathZoneX matchgroup=Delimiter start="\$" skip="\\\\\|\\\$" matchgroup=Delimiter end="\$" end="%stopzone\>" contains=@texMathZoneGroup - syn region texMathZoneY matchgroup=Delimiter start="\$\$" matchgroup=Delimiter end="\$\$" end="%stopzone\>" keepend contains=@texMathZoneGroup + syn region texMathZoneV matchgroup=Delimiter start="\\(" matchgroup=Delimiter end="\\)\|%stopzone\>" keepend contains=@texMathZoneGroup + syn region texMathZoneW matchgroup=Delimiter start="\\\[" matchgroup=Delimiter end="\\]\|%stopzone\>" keepend contains=@texMathZoneGroup + syn region texMathZoneX matchgroup=Delimiter start="\$" skip="\%(\\\\\)*\\\$" matchgroup=Delimiter end="\$" end="%stopzone\>" contains=@texMathZoneGroup + syn region texMathZoneY matchgroup=Delimiter start="\$\$" matchgroup=Delimiter end="\$\$" end="%stopzone\>" keepend contains=@texMathZoneGroup endif - syn region texMathZoneZ matchgroup=texStatement start="\\ensuremath\s*{" matchgroup=texStatement end="}" end="%stopzone\>" contains=@texMathZoneGroup + syn region texMathZoneZ matchgroup=texStatement start="\\ensuremath\s*{" matchgroup=texStatement end="}" end="%stopzone\>" contains=@texMathZoneGroup endif syn match texMathOper "[_^=]" contained @@ -541,7 +535,7 @@ else syn match texSpecialChar "\\[SP@]\A"me=e-1 endif syn match texSpecialChar "\\\\" -if !exists("tex_no_math") +if !exists("g:tex_no_math") syn match texOnlyMath "[_^]" endif syn match texSpecialChar "\^\^[0-9a-f]\{2}\|\^\^\S" @@ -556,7 +550,7 @@ endif " Comments: {{{1 " Normal TeX LaTeX : %.... " Documented TeX Format: ^^A... -and- leading %s (only) -if !exists("g:tex_comment_nospell") || !g:tex_comment_nospell +if !s:tex_comment_nospell syn cluster texCommentGroup contains=texTodo,@Spell else syn cluster texCommentGroup contains=texTodo,@NoSpell @@ -565,23 +559,23 @@ syn case ignore syn keyword texTodo contained combak fixme todo xxx syn case match if s:extfname == "dtx" - syn match texComment "\^\^A.*$" contains=@texCommentGroup - syn match texComment "^%\+" contains=@texCommentGroup + syn match texComment "\^\^A.*$" contains=@texCommentGroup + syn match texComment "^%\+" contains=@texCommentGroup else - if g:tex_fold_enabled - " allows syntax-folding of 2 or more contiguous comment lines - " single-line comments are not folded - syn match texComment "%.*$" contains=@texCommentGroup - if s:tex_fast =~ 'c' - syn region texComment start="^\zs\s*%.*\_s*%" skip="^\s*%" end='^\ze\s*[^%]' fold - syn region texNoSpell contained fold matchgroup=texComment start="%\s*nospell\s*{" end="%\s*nospell\s*}" contains=@texFoldGroup,@NoSpell - endif - else - syn match texComment "%.*$" contains=@texCommentGroup - if s:tex_fast =~ 'c' - syn region texNoSpell contained matchgroup=texComment start="%\s*nospell\s*{" end="%\s*nospell\s*}" contains=@texFoldGroup,@NoSpell - endif + if s:tex_fold_enabled + " allows syntax-folding of 2 or more contiguous comment lines + " single-line comments are not folded + syn match texComment "%.*$" contains=@texCommentGroup + if s:tex_fast =~ 'c' + TexFold syn region texComment start="^\zs\s*%.*\_s*%" skip="^\s*%" end='^\ze\s*[^%]' contains=@texCommentGroup + TexFold syn region texNoSpell contained matchgroup=texComment start="%\s*nospell\s*{" end="%\s*nospell\s*}" contains=@texFoldGroup,@NoSpell endif + else + syn match texComment "%.*$" contains=@texCommentGroup + if s:tex_fast =~ 'c' + syn region texNoSpell contained matchgroup=texComment start="%\s*nospell\s*{" end="%\s*nospell\s*}" contains=@texFoldGroup,@NoSpell + endif + endif endif " Separate lines used for verb` and verb# so that the end conditions {{{1 @@ -663,7 +657,7 @@ syn match texLength "\<\d\+\([.,]\d\+\)\=\s*\(true\)\=\s*\(bp\|cc\|cm\|dd\|em\ syn match texString "\(``\|''\|,,\)" " makeatletter -- makeatother sections -if !exists("s:tex_no_error") +if !s:tex_no_error if s:tex_fast =~ 'S' syn region texStyle matchgroup=texStatement start='\\makeatletter' end='\\makeatother' contains=@texStyleGroup contained endif @@ -794,8 +788,8 @@ if has("conceal") && &enc == 'utf-8' \ ['left(' , '('], \ ['left\[' , '['], \ ['left\\{' , '{'], - \ ['leftarrow' , '⟵'], - \ ['Leftarrow' , '⟸'], + \ ['leftarrow' , 'â†'], + \ ['Leftarrow' , 'â‡'], \ ['leftarrowtail' , '↢'], \ ['leftharpoondown', '↽'], \ ['leftharpoonup' , '↼'], @@ -881,8 +875,8 @@ if has("conceal") && &enc == 'utf-8' \ ['right)' , ')'], \ ['right]' , ']'], \ ['right\\}' , '}'], - \ ['rightarrow' , '⟶'], - \ ['Rightarrow' , '⟹'], + \ ['rightarrow' , '→'], + \ ['Rightarrow' , '⇒'], \ ['rightarrowtail' , '↣'], \ ['rightleftharpoons', '⇌'], \ ['rightsquigarrow', 'â†'], @@ -995,6 +989,7 @@ if has("conceal") && &enc == 'utf-8' syn match texMathSymbol '\\hat{W}' contained conceal cchar=Å´ syn match texMathSymbol '\\hat{y}' contained conceal cchar=Å· syn match texMathSymbol '\\hat{Y}' contained conceal cchar=Ŷ +" syn match texMathSymbol '\\bar{a}' contained conceal cchar=aÌ… endif " Greek {{{2 @@ -1011,7 +1006,7 @@ if has("conceal") && &enc == 'utf-8' call s:Greek('texGreek','\\zeta\>' ,'ζ') call s:Greek('texGreek','\\eta\>' ,'η') call s:Greek('texGreek','\\theta\>' ,'θ') - call s:Greek('texGreek','\\vartheta\>' ,'Ï‘') + call s:Greek('texGreek','\\vartheta\>' ,'Ï‘') call s:Greek('texGreek','\\kappa\>' ,'κ') call s:Greek('texGreek','\\lambda\>' ,'λ') call s:Greek('texGreek','\\mu\>' ,'μ') @@ -1022,11 +1017,11 @@ if has("conceal") && &enc == 'utf-8' call s:Greek('texGreek','\\rho\>' ,'Ï') call s:Greek('texGreek','\\varrho\>' ,'ϱ') call s:Greek('texGreek','\\sigma\>' ,'σ') - call s:Greek('texGreek','\\varsigma\>' ,'Ï‚') + call s:Greek('texGreek','\\varsigma\>' ,'Ï‚') call s:Greek('texGreek','\\tau\>' ,'Ï„') call s:Greek('texGreek','\\upsilon\>' ,'Ï…') - call s:Greek('texGreek','\\phi\>' ,'φ') - call s:Greek('texGreek','\\varphi\>' ,'Ï•') + call s:Greek('texGreek','\\phi\>' ,'Ï•') + call s:Greek('texGreek','\\varphi\>' ,'φ') call s:Greek('texGreek','\\chi\>' ,'χ') call s:Greek('texGreek','\\psi\>' ,'ψ') call s:Greek('texGreek','\\omega\>' ,'ω') @@ -1050,11 +1045,12 @@ if has("conceal") && &enc == 'utf-8' syn region texSuperscript matchgroup=Delimiter start='\^{' skip="\\\\\|\\[{}]" end='}' contained concealends contains=texSpecialChar,texSuperscripts,texStatement,texSubscript,texSuperscript,texMathMatcher syn region texSubscript matchgroup=Delimiter start='_{' skip="\\\\\|\\[{}]" end='}' contained concealends contains=texSpecialChar,texSubscripts,texStatement,texSubscript,texSuperscript,texMathMatcher endif + " s:SuperSub: fun! s:SuperSub(group,leader,pat,cchar) if a:pat =~ '^\\' || (a:leader == '\^' && a:pat =~ g:tex_superscripts) || (a:leader == '_' && a:pat =~ g:tex_subscripts) " call Decho("SuperSub: group<".a:group."> leader<".a:leader."> pat<".a:pat."> cchar<".a:cchar.">") exe 'syn match '.a:group." '".a:leader.a:pat."' contained conceal cchar=".a:cchar - exe 'syn match '.a:group."s '".a:pat."' contained conceal cchar=".a:cchar.' nextgroup='.a:group.'s' + exe 'syn match '.a:group."s '".a:pat ."' contained conceal cchar=".a:cchar.' nextgroup='.a:group.'s' endif endfun call s:SuperSub('texSuperscript','\^','0','â°') @@ -1245,7 +1241,7 @@ syn sync match texSyncStop groupthere NONE "%stopzone\>" " (one can't tell if a "$$" starts or stops a math zone by itself) " The following grouptheres coupled with minlines above " help improve the odds of good syncing. -if !exists("tex_no_math") +if !exists("g:tex_no_math") syn sync match texSyncMathZoneA groupthere NONE "\\end{abstract}" syn sync match texSyncMathZoneA groupthere NONE "\\end{center}" syn sync match texSyncMathZoneA groupthere NONE "\\end{description}" @@ -1285,7 +1281,7 @@ if did_tex_syntax_inits == 1 HiLink texInputFileOpt texCmdArgs HiLink texInputCurlies texDelimiter HiLink texLigature texSpecialChar - if !exists("tex_no_math") + if !exists("g:tex_no_math") HiLink texMathDelimSet1 texMathDelim HiLink texMathDelimSet2 texMathDelim HiLink texMathDelimKey texMathDelim @@ -1341,6 +1337,7 @@ if did_tex_syntax_inits == 1 endif " Cleanup: {{{1 +delc TexFold unlet s:extfname let b:current_syntax = "tex" let &cpo = s:keepcpo diff --git a/runtime/syntax/upstreamdat.vim b/runtime/syntax/upstreamdat.vim index 7be806730d..e3b415a4bc 100644 --- a/runtime/syntax/upstreamdat.vim +++ b/runtime/syntax/upstreamdat.vim @@ -1,13 +1,14 @@ " Vim syntax file " Language: Innovation Data Processing upstream.dat file " Maintainer: Rob Owens <rowens@fdrinnovation.com> -" Latest Revision: 2013-06-17 +" Latest Revision: 2013-11-27 " Quit when a syntax file was already loaded if exists("b:current_syntax") finish endif +" Parameters: syn keyword upstreamdat_Parameter ACCEPTPCREMOTE syn keyword upstreamdat_Parameter ACCEPTREMOTE syn keyword upstreamdat_Parameter ACTION @@ -291,6 +292,14 @@ syn keyword upstreamdat_Parameter XFERECORDSIZE syn keyword upstreamdat_Parameter XFERRECSEP syn keyword upstreamdat_Parameter XFERRECUSECR +" File Specs: +syn match upstreamdat_Filespec /file spec\c \d\{1,3}.*/ + +" Comments: +syn match upstreamdat_Comment /^#.*/ + hi def link upstreamdat_Parameter Type +"hi def link upstreamdat_Filespec Underlined +hi def link upstreamdat_Comment Comment let b:current_syntax = "upstreamdat" diff --git a/runtime/syntax/upstreamrpt.vim b/runtime/syntax/upstreamrpt.vim new file mode 100644 index 0000000000..170fc8f509 --- /dev/null +++ b/runtime/syntax/upstreamrpt.vim @@ -0,0 +1,310 @@ +" Vim syntax file +" Language: Innovation Data Processing upstream.rpt file +" Maintainer: Rob Owens <rowens@fdrinnovation.com> +" Latest Revision: 2014-03-13 + +" Quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +setlocal foldmethod=syntax + +" Parameters: +syn keyword upstreamdat_Parameter ACCEPTPCREMOTE +syn keyword upstreamdat_Parameter ACCEPTREMOTE +syn keyword upstreamdat_Parameter ACTION +syn keyword upstreamdat_Parameter ACTIVATEONENTRY +syn keyword upstreamdat_Parameter ARCHIVEBIT +syn keyword upstreamdat_Parameter ARCHIVEBIT +syn keyword upstreamdat_Parameter ASCTOEBC +syn keyword upstreamdat_Parameter ASRBACKUP +syn keyword upstreamdat_Parameter ATTENDED +syn keyword upstreamdat_Parameter AUTHORITATIVE +syn keyword upstreamdat_Parameter AUTHORITATIVERESTORE +syn keyword upstreamdat_Parameter AUTHORITATIVERESTORE +syn keyword upstreamdat_Parameter BACKUPPROFILE +syn keyword upstreamdat_Parameter BACKUPPROFILE2 +syn keyword upstreamdat_Parameter BACKUPREPARSEFILES +syn keyword upstreamdat_Parameter BACKUPREPARSEFILES +syn keyword upstreamdat_Parameter BACKUPVERIFY +syn keyword upstreamdat_Parameter BLANKTRUNC +syn keyword upstreamdat_Parameter CALCDASDSIZE +syn keyword upstreamdat_Parameter CHANGEDIRATTRIBS +syn keyword upstreamdat_Parameter CHANGEDIRATTRIBS +syn keyword upstreamdat_Parameter COMPRESSLEVEL +syn keyword upstreamdat_Parameter CONTROLFILE +syn keyword upstreamdat_Parameter DASDOVERRIDE +syn keyword upstreamdat_Parameter DATELIMIT +syn keyword upstreamdat_Parameter DATELIMIT +syn keyword upstreamdat_Parameter DAYSOLD +syn keyword upstreamdat_Parameter DAYSOLD +syn keyword upstreamdat_Parameter DELETED +syn keyword upstreamdat_Parameter DELETED +syn keyword upstreamdat_Parameter DELETEPROMPTS +syn keyword upstreamdat_Parameter DELETEPROMPTS +syn keyword upstreamdat_Parameter DESTINATION +syn keyword upstreamdat_Parameter DESTINATION +syn keyword upstreamdat_Parameter DIRDELETE +syn keyword upstreamdat_Parameter DIRECTORVMC +syn keyword upstreamdat_Parameter DIRONLYRESTOREOK +syn keyword upstreamdat_Parameter DIRSONLY +syn keyword upstreamdat_Parameter DIRSONLY +syn keyword upstreamdat_Parameter DISASTERRECOVERY +syn keyword upstreamdat_Parameter DISPLAY +syn keyword upstreamdat_Parameter DRIVEALIAS +syn keyword upstreamdat_Parameter DRIVEALIAS +syn keyword upstreamdat_Parameter DUALCOPY +syn keyword upstreamdat_Parameter DUPDAYS +syn keyword upstreamdat_Parameter DUPLICATE +syn keyword upstreamdat_Parameter EBCTOASC +syn keyword upstreamdat_Parameter ENCRYPT +syn keyword upstreamdat_Parameter ENCRYPTLEVEL +syn keyword upstreamdat_Parameter EXCLUDELISTNAME +syn keyword upstreamdat_Parameter FAILBACKUPONERROR +syn keyword upstreamdat_Parameter FAILBACKUPONERROR +syn keyword upstreamdat_Parameter FAILIFNOFILES +syn keyword upstreamdat_Parameter FAILIFNOFILES +syn keyword upstreamdat_Parameter FAILIFSKIP +syn keyword upstreamdat_Parameter FAILJOB +syn keyword upstreamdat_Parameter FAILRESTOREONERROR +syn keyword upstreamdat_Parameter FAILRESTOREONERROR +syn keyword upstreamdat_Parameter FILEDATE +syn keyword upstreamdat_Parameter FILEDATE +syn keyword upstreamdat_Parameter FILEDELETE +syn keyword upstreamdat_Parameter FILEDELETE +syn keyword upstreamdat_Parameter FILES +syn keyword upstreamdat_Parameter FILES +syn keyword upstreamdat_Parameter FILESOPENFORUPDAT +syn keyword upstreamdat_Parameter FILESOPENFORUPDAT +syn keyword upstreamdat_Parameter FILETRANSFER +syn keyword upstreamdat_Parameter GETREMOTEFILES +syn keyword upstreamdat_Parameter HARDLINKDB +syn keyword upstreamdat_Parameter HARDLINKS +syn keyword upstreamdat_Parameter HARDLINKS +syn keyword upstreamdat_Parameter HIDDENFILES +syn keyword upstreamdat_Parameter HIDDENFILES +syn keyword upstreamdat_Parameter HOLDTAPE +syn keyword upstreamdat_Parameter HOLDUSERDIRS +syn keyword upstreamdat_Parameter HOSTFILENAME +syn keyword upstreamdat_Parameter HOSTRECORD +syn keyword upstreamdat_Parameter HOSTSORT +syn keyword upstreamdat_Parameter IGNOREPLUGINSFORRESTORE +syn keyword upstreamdat_Parameter INCRDB +syn keyword upstreamdat_Parameter INCRDBARCHIVEBIT +syn keyword upstreamdat_Parameter INCRDBDELETEDFILES +syn keyword upstreamdat_Parameter INCREMENTAL +syn keyword upstreamdat_Parameter INCREMENTAL +syn keyword upstreamdat_Parameter INQOPTIONS +syn keyword upstreamdat_Parameter INSTALLWIN2KAGENT +syn keyword upstreamdat_Parameter INSTALLWIN2KAGENT +syn keyword upstreamdat_Parameter JOBOPTIONS +syn keyword upstreamdat_Parameter JOBRETURNCODEMAP +syn keyword upstreamdat_Parameter JOBWAITTIMELIMIT +syn keyword upstreamdat_Parameter KEEPALIVE +syn keyword upstreamdat_Parameter LANINTERFACE +syn keyword upstreamdat_Parameter LANWSNAME +syn keyword upstreamdat_Parameter LANWSPASSWORD +syn keyword upstreamdat_Parameter LASTACCESS +syn keyword upstreamdat_Parameter LASTACCESS +syn keyword upstreamdat_Parameter LATESTDATE +syn keyword upstreamdat_Parameter LATESTDATE +syn keyword upstreamdat_Parameter LATESTTIME +syn keyword upstreamdat_Parameter LATESTTIME +syn keyword upstreamdat_Parameter LATESTVERSION +syn keyword upstreamdat_Parameter LINEBLOCK +syn keyword upstreamdat_Parameter LINETRUNC +syn keyword upstreamdat_Parameter LISTENFORREMOTE +syn keyword upstreamdat_Parameter LOCALBACKUP +syn keyword upstreamdat_Parameter LOCALBACKUPDIR +syn keyword upstreamdat_Parameter LOCALBACKUPMAX +syn keyword upstreamdat_Parameter LOCALBACKUPMAXFILESIZE +syn keyword upstreamdat_Parameter LOCALBACKUPMAXSIZE +syn keyword upstreamdat_Parameter LOCALEXCLUDEFILE +syn keyword upstreamdat_Parameter LOCALPARAMETERS +syn keyword upstreamdat_Parameter LOCALPASSWORD +syn keyword upstreamdat_Parameter LOCALRESTORE +syn keyword upstreamdat_Parameter LOCALUSER +syn keyword upstreamdat_Parameter LOFS +syn keyword upstreamdat_Parameter LOGNONFATAL +syn keyword upstreamdat_Parameter MAXBACKUPFILESFAIL +syn keyword upstreamdat_Parameter MAXBACKUPTIME +syn keyword upstreamdat_Parameter MAXDUPS +syn keyword upstreamdat_Parameter MAXFILENAMESIZE +syn keyword upstreamdat_Parameter MAXKFILESIZE +syn keyword upstreamdat_Parameter MAXLOGDAYS +syn keyword upstreamdat_Parameter MAXRESTOREFILESFAIL +syn keyword upstreamdat_Parameter MAXRESTORETIME +syn keyword upstreamdat_Parameter MAXRETRY +syn keyword upstreamdat_Parameter MAXRPTDAYS +syn keyword upstreamdat_Parameter MERGE +syn keyword upstreamdat_Parameter MIGRBITS +syn keyword upstreamdat_Parameter MIGRBITS +syn keyword upstreamdat_Parameter MINCOMPRESSSIZE +syn keyword upstreamdat_Parameter MINIMIZE +syn keyword upstreamdat_Parameter MODIFYFILE +syn keyword upstreamdat_Parameter MOUNTPOINTS +syn keyword upstreamdat_Parameter MOUNTPOINTS +syn keyword upstreamdat_Parameter NDS +syn keyword upstreamdat_Parameter NDS +syn keyword upstreamdat_Parameter NEWFILECOMPARE +syn keyword upstreamdat_Parameter NFSBELOW +syn keyword upstreamdat_Parameter NODATAOK +syn keyword upstreamdat_Parameter NODIRFORINCREMENTAL +syn keyword upstreamdat_Parameter NODIRFORINCREMENTAL +syn keyword upstreamdat_Parameter NONFILEDATABITMAP +syn keyword upstreamdat_Parameter NONFILEDATABITMAP +syn keyword upstreamdat_Parameter NOPOINTRESTORE +syn keyword upstreamdat_Parameter NOSPECINHERITANCE +syn keyword upstreamdat_Parameter NOTIFYEVENTS +syn keyword upstreamdat_Parameter NOTIFYFAILUREATTACHMENT +syn keyword upstreamdat_Parameter NOTIFYSUCCESSATTACHMENT +syn keyword upstreamdat_Parameter NOTIFYTARGETS +syn keyword upstreamdat_Parameter NOUIDGIDNAMES +syn keyword upstreamdat_Parameter NOUIDGIDNAMES +syn keyword upstreamdat_Parameter NOVELLMIGRATE +syn keyword upstreamdat_Parameter NOVELLMIGRATE +syn keyword upstreamdat_Parameter NOVELLMIGRATEADDEXT +syn keyword upstreamdat_Parameter NOVELLMIGRATEADDEXT +syn keyword upstreamdat_Parameter NOVELLPROFILE +syn keyword upstreamdat_Parameter NOVELLRECALL +syn keyword upstreamdat_Parameter NTFSADDPERMISSION +syn keyword upstreamdat_Parameter NTFSADDPERMISSION +syn keyword upstreamdat_Parameter NTREGRESTORE +syn keyword upstreamdat_Parameter OSTYPE +syn keyword upstreamdat_Parameter OUTPORT +syn keyword upstreamdat_Parameter PACKFLUSHAFTERFILE +syn keyword upstreamdat_Parameter PACKRECSIZE +syn keyword upstreamdat_Parameter PARAMETER +syn keyword upstreamdat_Parameter PASSWORD +syn keyword upstreamdat_Parameter PATHNAME +syn keyword upstreamdat_Parameter PATHNAME +syn keyword upstreamdat_Parameter PERFORMBITMAP +syn keyword upstreamdat_Parameter PERFORMNUMRECORDS +syn keyword upstreamdat_Parameter PERFORMRECORDSIZE +syn keyword upstreamdat_Parameter PLUGIN +syn keyword upstreamdat_Parameter PLUGIN +syn keyword upstreamdat_Parameter PLUGINPARAMETERS +syn keyword upstreamdat_Parameter PLUGINPARAMETERS +syn keyword upstreamdat_Parameter POSTJOB +syn keyword upstreamdat_Parameter PREJOB +syn keyword upstreamdat_Parameter PRTYCLASS +syn keyword upstreamdat_Parameter PRTYLEVEL +syn keyword upstreamdat_Parameter RECALLCLEANUP +syn keyword upstreamdat_Parameter RECALLOFFLINEFILES +syn keyword upstreamdat_Parameter RECALLOFFLINEFILES +syn keyword upstreamdat_Parameter RECORDSIZE +syn keyword upstreamdat_Parameter REMOTEADDR +syn keyword upstreamdat_Parameter REMOTEAPPLPREF +syn keyword upstreamdat_Parameter REMOTEAPPLRETRY +syn keyword upstreamdat_Parameter REMOTECONNECTTYPE +syn keyword upstreamdat_Parameter REMOTEFLAGS +syn keyword upstreamdat_Parameter REMOTEIPADAPTER +syn keyword upstreamdat_Parameter REMOTELOCALPARAMETERS +syn keyword upstreamdat_Parameter REMOTELOGMODE +syn keyword upstreamdat_Parameter REMOTELUNAME +syn keyword upstreamdat_Parameter REMOTEMAXRETRIES +syn keyword upstreamdat_Parameter REMOTEMODENAME +syn keyword upstreamdat_Parameter REMOTEPARAMETERFILE +syn keyword upstreamdat_Parameter REMOTEPORT +syn keyword upstreamdat_Parameter REMOTEREQUEST +syn keyword upstreamdat_Parameter REMOTERESTART +syn keyword upstreamdat_Parameter REMOTEROUTE +syn keyword upstreamdat_Parameter REMOTETARGETNAME +syn keyword upstreamdat_Parameter REMOTETCP +syn keyword upstreamdat_Parameter REMOTETIMEOUT +syn keyword upstreamdat_Parameter REMOTETMAXRETRY +syn keyword upstreamdat_Parameter REMOTETPN +syn keyword upstreamdat_Parameter REMOTEUSAPPL +syn keyword upstreamdat_Parameter REMOTEVERIFY +syn keyword upstreamdat_Parameter REMOTEWTOCOMP +syn keyword upstreamdat_Parameter REPORTNAME +syn keyword upstreamdat_Parameter REPORTOPTIONS +syn keyword upstreamdat_Parameter RESTARTLASTFILE +syn keyword upstreamdat_Parameter RESTART +syn keyword upstreamdat_Parameter RESTARTTYPE +syn keyword upstreamdat_Parameter RESTARTVERSIONDATE +syn keyword upstreamdat_Parameter RESTOREARCHIVEBIT +syn keyword upstreamdat_Parameter RESTORECHECKPOINT +syn keyword upstreamdat_Parameter RESTOREDATELIMIT +syn keyword upstreamdat_Parameter RESTOREDATELIMIT +syn keyword upstreamdat_Parameter RESTOREFILEFAIL +syn keyword upstreamdat_Parameter RESTOREMOUNTPOINTS +syn keyword upstreamdat_Parameter RESTOREMOUNTPOINTS +syn keyword upstreamdat_Parameter RESTORESEGMENTS +syn keyword upstreamdat_Parameter RESTORESEGMENTS +syn keyword upstreamdat_Parameter RESTORETODIFFFS +syn keyword upstreamdat_Parameter RETAIN +syn keyword upstreamdat_Parameter RETAIN +syn keyword upstreamdat_Parameter ROOTENTRY +syn keyword upstreamdat_Parameter ROOTENTRY +syn keyword upstreamdat_Parameter SAN +syn keyword upstreamdat_Parameter SCHEDULENAME +syn keyword upstreamdat_Parameter SEGMENTEDFILESIZE +syn keyword upstreamdat_Parameter SEGMENTEDFILESIZE +syn keyword upstreamdat_Parameter SEGMENTSIZE +syn keyword upstreamdat_Parameter SEGMENTSIZE +syn keyword upstreamdat_Parameter SENDHOSTDETAILS +syn keyword upstreamdat_Parameter SINGLEFS +syn keyword upstreamdat_Parameter SIZETRC +syn keyword upstreamdat_Parameter SKIP +syn keyword upstreamdat_Parameter SKIPBACKUPSCAN +syn keyword upstreamdat_Parameter SKIPOLD +syn keyword upstreamdat_Parameter SKIPOLD +syn keyword upstreamdat_Parameter SMSTARGETSERVICENAME +syn keyword upstreamdat_Parameter SMSTSA +syn keyword upstreamdat_Parameter SOLO +syn keyword upstreamdat_Parameter SORTBACKUP +syn keyword upstreamdat_Parameter SOSDISK +syn keyword upstreamdat_Parameter SOSDISK +syn keyword upstreamdat_Parameter SOSTIMESTAMP +syn keyword upstreamdat_Parameter SOSTIMESTAMP +syn keyword upstreamdat_Parameter SOSTIMESTAMPPATH +syn keyword upstreamdat_Parameter SOSTIMESTAMPPATH +syn keyword upstreamdat_Parameter SPECNUMBER +syn keyword upstreamdat_Parameter SPECNUMBER +syn keyword upstreamdat_Parameter SPECTYPE +syn keyword upstreamdat_Parameter SPECTYPE +syn keyword upstreamdat_Parameter STARTTIME +syn keyword upstreamdat_Parameter STORAGETYPE +syn keyword upstreamdat_Parameter SUBDIRECTORIES +syn keyword upstreamdat_Parameter SUBDIRECTORIES +syn keyword upstreamdat_Parameter SWITCHTOTAPEMB +syn keyword upstreamdat_Parameter TCPADDRESS +syn keyword upstreamdat_Parameter TCPTIMEOUT +syn keyword upstreamdat_Parameter TIMEOVERRIDE +syn keyword upstreamdat_Parameter TRACE +syn keyword upstreamdat_Parameter TRANSLATE +syn keyword upstreamdat_Parameter ULTRACOMP +syn keyword upstreamdat_Parameter ULTREG +syn keyword upstreamdat_Parameter ULTUPD +syn keyword upstreamdat_Parameter UNCMACHINEALIAS +syn keyword upstreamdat_Parameter UNCMACHINEALIAS +syn keyword upstreamdat_Parameter USEALEBRA +syn keyword upstreamdat_Parameter USECONTROLFILE +syn keyword upstreamdat_Parameter USEGID +syn keyword upstreamdat_Parameter USERID +syn keyword upstreamdat_Parameter USEUID +syn keyword upstreamdat_Parameter USNOUIDGIDERRORS +syn keyword upstreamdat_Parameter UTF8 +syn keyword upstreamdat_Parameter VAULTNUMBER +syn keyword upstreamdat_Parameter VERSIONDATE +syn keyword upstreamdat_Parameter WRITESPARSE +syn keyword upstreamdat_Parameter XFERECORDSIZE +syn keyword upstreamdat_Parameter XFERRECSEP +syn keyword upstreamdat_Parameter XFERRECUSECR + +" File Specs: +syn match upstreamdat_Filespec /file spec\c \d\{1,3}.*/ + +" Comments: +syn match upstreamdat_Comment /^#.*/ + +" List Of Parameters: +syn region upstreamdat_Parms start="Current Parameters:" end="End Of Parameters" transparent fold + +hi def link upstreamdat_Parameter Type +"hi def link upstreamdat_Filespec Underlined +hi def link upstreamdat_Comment Comment + +let b:current_syntax = "upstreamdat" diff --git a/runtime/syntax/usw2kagtlog.vim b/runtime/syntax/usw2kagtlog.vim index 0a34128f9b..a112340d12 100644 --- a/runtime/syntax/usw2kagtlog.vim +++ b/runtime/syntax/usw2kagtlog.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Innovation Data Processing USW2KAgt.log file " Maintainer: Rob Owens <rowens@fdrinnovation.com> -" Latest Revision: 2013-09-19 +" Latest Revision: 2014-04-01 " Quit when a syntax file was already loaded if exists("b:current_syntax") @@ -17,8 +17,12 @@ syn match usw2kagtlog_MsgI /Msg #\(Agt\|PC\|Srv\)\d\{4,5}I/ nextgroup=usw2kagtlo syn match usw2kagtlog_MsgW /Msg #\(Agt\|PC\|Srv\)\d\{4,5}W/ nextgroup=usw2kagtlog_Process skipwhite " Processes: syn region usw2kagtlog_Process start="(" end=")" contained -syn region usw2kagtlog_Process start="Starting the processing for a \zs\"" end="\ze client request" -syn region usw2kagtlog_Process start="Ending the processing for a \zs\"" end="\ze client request" +"syn region usw2kagtlog_Process start="Starting the processing for a \zs\"" end="\ze client request" +"syn region usw2kagtlog_Process start="Ending the processing for a \zs\"" end="\ze client request" +"syn region usw2kagtlog_Process start="Starting the processing for a \zs\"" end="\ze client\s\{0,1}\r\{0,1}\s\{1,9}request" +"syn region usw2kagtlog_Process start="Ending the processing for a \zs\"" end="\ze client\s\{0,1}\r\{0,1}\s\{1,9}request" +syn region usw2kagtlog_Process start="Starting the processing for a \zs\"" end="\ze client" +syn region usw2kagtlog_Process start="Ending the processing for a \zs\"" end="\ze client" " IP Address: syn match usw2kagtlog_IPaddr / \d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}/ " Profile: diff --git a/runtime/syntax/valgrind.vim b/runtime/syntax/valgrind.vim index c247927c2f..d099971826 100644 --- a/runtime/syntax/valgrind.vim +++ b/runtime/syntax/valgrind.vim @@ -2,7 +2,8 @@ " Language: Valgrind Memory Debugger Output " Maintainer: Roger Luethi <rl@hellgate.ch> " Program URL: http://devel-home.kde.org/~sewardj/ -" Last Change: 2014 Oct 02 +" Last Change: 2015 Jan 27 +" Included improvement by Dominique Pelle " " Notes: mostly based on strace.vim and xml.vim " @@ -15,6 +16,9 @@ endif let s:keepcpo= &cpo set cpo&vim +" Lines can be long with demangled c++ functions. +setlocal synmaxcol=8000 + syn case match syn sync minlines=50 @@ -29,8 +33,8 @@ syn region valgrindRegion \ contains=valgrindPidChunk,valgrindLine syn region valgrindPidChunk - \ start=+\(^==\)\@<=+ - \ end=+\(==\)\@=+ + \ start=+^==\zs+ + \ end=+\ze==+ \ contained \ contains=valgrindPid0,valgrindPid1,valgrindPid2,valgrindPid3,valgrindPid4,valgrindPid5,valgrindPid6,valgrindPid7,valgrindPid8,valgrindPid9 \ keepend @@ -64,10 +68,11 @@ syn match valgrindSummary ".*SUMMARY:" contained syn match valgrindLoc "\s\+\(by\|at\|Address\).*$" contained \ contains=valgrindAt,valgrindAddr,valgrindFunc,valgrindBin,valgrindSrc syn match valgrindAt "at\s\@=" contained -syn match valgrindAddr "\(\W\)\@<=0x\x\+" contained -syn match valgrindFunc "\(: \)\@<=\w\+" contained -syn match valgrindBin "\((\(with\|\)in \)\@<=\S\+\()\)\@=" contained -syn match valgrindSrc "\((\)\@<=[^)]*:\d\+\()\)\@=" contained +syn match valgrindAddr "\W\zs0x\x\+" contained + +syn match valgrindFunc ": \zs\h[a-zA-Z0-9_:\[\]()<>&*+\-,=%!|^ ]*\ze([^)]*)$" contained +syn match valgrindBin "(\(with\)\=in \zs\S\+)\@=" contained +syn match valgrindSrc "(\zs[^)]*:\d\+)\@=" contained " Define the default highlighting diff --git a/runtime/syntax/vb.vim b/runtime/syntax/vb.vim index 14f9e64850..0c05b35fbd 100644 --- a/runtime/syntax/vb.vim +++ b/runtime/syntax/vb.vim @@ -223,7 +223,7 @@ syn keyword vbStatement Explicit FileCopy For ForEach Function Get GoSub syn keyword vbStatement GoTo Gosub Implements Kill LSet Let Lib LineInput syn keyword vbStatement Load Lock Loop Mid MkDir Name Next On OnError Open syn keyword vbStatement Option Preserve Private Property Public Put RSet -syn keyword vbStatement RaiseEvent Randomize ReDim Redim Rem Reset Resume +syn keyword vbStatement RaiseEvent Randomize ReDim Redim Reset Resume syn keyword vbStatement Return RmDir SavePicture SaveSetting Seek SendKeys syn keyword vbStatement Sendkeys Set SetAttr Static Step Stop Sub Time syn keyword vbStatement Type Unload Unlock Until Wend While Width With diff --git a/runtime/syntax/vhdl.vim b/runtime/syntax/vhdl.vim index c76b046d8c..916bd9635d 100644 --- a/runtime/syntax/vhdl.vim +++ b/runtime/syntax/vhdl.vim @@ -3,8 +3,7 @@ " Maintainer: Daniel Kho <daniel.kho@tauhop.com> " Previous Maintainer: Czo <Olivier.Sirol@lip6.fr> " Credits: Stephan Hegel <stephan.hegel@snc.siemens.com.cn> -" Last Changed: 2015 Apr 25 by Daniel Kho -" $Id: vhdl.vim,v 1.1 2004/06/13 15:34:56 vimboss Exp $ +" Last Changed: 2015 Dec 4 by Daniel Kho " VHSIC (Very High Speed Integrated Circuit) Hardware Description Language @@ -19,195 +18,223 @@ endif let s:cpo_save = &cpo set cpo&vim -" This is not VHDL. I use the C-Preprocessor cpp to generate different binaries -" from one VHDL source file. Unfortunately there is no preprocessor for VHDL -" available. If you don't like this, please remove the following lines. -"syn match cDefine "^#ifdef[ ]\+[A-Za-z_]\+" -"syn match cDefine "^#endif" - " case is not significant -syn case ignore +syn case ignore " VHDL keywords -syn keyword vhdlStatement access after alias all assert -syn keyword vhdlStatement architecture array attribute -syn keyword vhdlStatement assume assume_guarantee -syn keyword vhdlStatement begin block body buffer bus -syn keyword vhdlStatement case component configuration constant -syn keyword vhdlStatement context cover -syn keyword vhdlStatement default disconnect downto -syn keyword vhdlStatement elsif end entity exit -syn keyword vhdlStatement file for function -syn keyword vhdlStatement fairness force -syn keyword vhdlStatement generate generic group guarded -syn keyword vhdlStatement impure in inertial inout is -syn keyword vhdlStatement label library linkage literal loop -syn keyword vhdlStatement map -syn keyword vhdlStatement new next null -syn keyword vhdlStatement of on open others out -syn keyword vhdlStatement package port postponed procedure process pure -syn keyword vhdlStatement parameter property protected -syn keyword vhdlStatement range record register reject report return -syn keyword vhdlStatement release restrict restrict_guarantee -syn keyword vhdlStatement select severity signal shared -syn keyword vhdlStatement subtype -syn keyword vhdlStatement sequence strong -syn keyword vhdlStatement then to transport type -syn keyword vhdlStatement unaffected units until use -syn keyword vhdlStatement variable -syn keyword vhdlStatement vmode vprop vunit -syn keyword vhdlStatement wait when while with -syn keyword vhdlStatement note warning error failure - -" Special match for "if" and "else" since "else if" shouldn't be highlighted. -" The right keyword is "elsif" -syn match vhdlStatement "\<\(if\|else\)\>" -syn match vhdlNone "\<else\s\+if\>$" -syn match vhdlNone "\<else\s\+if\>\s" +syn keyword vhdlStatement access after alias all assert +syn keyword vhdlStatement architecture array attribute +syn keyword vhdlStatement assume assume_guarantee +syn keyword vhdlStatement begin block body buffer bus +syn keyword vhdlStatement case component configuration constant +syn keyword vhdlStatement context cover +syn keyword vhdlStatement default disconnect downto +syn keyword vhdlStatement elsif end entity exit +syn keyword vhdlStatement file for function +syn keyword vhdlStatement fairness force +syn keyword vhdlStatement generate generic group guarded +syn keyword vhdlStatement impure in inertial inout is +syn keyword vhdlStatement label library linkage literal loop +syn keyword vhdlStatement map +syn keyword vhdlStatement new next null +syn keyword vhdlStatement of on open others out +syn keyword vhdlStatement package port postponed procedure process pure +syn keyword vhdlStatement parameter property protected +syn keyword vhdlStatement range record register reject report return +syn keyword vhdlStatement release restrict restrict_guarantee +syn keyword vhdlStatement select severity signal shared +syn keyword vhdlStatement subtype +syn keyword vhdlStatement sequence strong +syn keyword vhdlStatement then to transport type +syn keyword vhdlStatement unaffected units until use +syn keyword vhdlStatement variable +syn keyword vhdlStatement vmode vprop vunit +syn keyword vhdlStatement wait when while with +syn keyword vhdlStatement note warning error failure + +" Linting of conditionals. +syn match vhdlStatement "\<\(if\|else\)\>" +syn match vhdlError "\<else\s\+if\>" " Predefined VHDL types -syn keyword vhdlType bit bit_vector -syn keyword vhdlType character boolean integer real time -syn keyword vhdlType boolean_vector integer_vector real_vector time_vector -syn keyword vhdlType string severity_level +syn keyword vhdlType bit bit_vector +syn keyword vhdlType character boolean integer real time +syn keyword vhdlType boolean_vector integer_vector real_vector time_vector +syn keyword vhdlType string severity_level " Predefined standard ieee VHDL types -syn keyword vhdlType positive natural signed unsigned -syn keyword vhdlType line text -syn keyword vhdlType std_logic std_logic_vector -syn keyword vhdlType std_ulogic std_ulogic_vector -" Predefined non standard VHDL types for Mentor Graphics Sys1076/QuickHDL -"syn keyword vhdlType qsim_state qsim_state_vector -"syn keyword vhdlType qsim_12state qsim_12state_vector -"syn keyword vhdlType qsim_strength -" Predefined non standard VHDL types for Alliance VLSI CAD -"syn keyword vhdlType mux_bit mux_vector reg_bit reg_vector wor_bit wor_vector +syn keyword vhdlType positive natural signed unsigned +syn keyword vhdlType unresolved_signed unresolved_unsigned u_signed u_unsigned +syn keyword vhdlType line text +syn keyword vhdlType std_logic std_logic_vector +syn keyword vhdlType std_ulogic std_ulogic_vector " array attributes -syn match vhdlAttribute "\'high" -syn match vhdlAttribute "\'left" -syn match vhdlAttribute "\'length" -syn match vhdlAttribute "\'low" -syn match vhdlAttribute "\'range" -syn match vhdlAttribute "\'reverse_range" -syn match vhdlAttribute "\'right" -syn match vhdlAttribute "\'ascending" +syn match vhdlAttribute "\'high" +syn match vhdlAttribute "\'left" +syn match vhdlAttribute "\'length" +syn match vhdlAttribute "\'low" +syn match vhdlAttribute "\'range" +syn match vhdlAttribute "\'reverse_range" +syn match vhdlAttribute "\'right" +syn match vhdlAttribute "\'ascending" " block attributes -syn match vhdlAttribute "\'behaviour" -syn match vhdlAttribute "\'structure" -syn match vhdlAttribute "\'simple_name" -syn match vhdlAttribute "\'instance_name" -syn match vhdlAttribute "\'path_name" -syn match vhdlAttribute "\'foreign" +syn match vhdlAttribute "\'simple_name" +syn match vhdlAttribute "\'instance_name" +syn match vhdlAttribute "\'path_name" +syn match vhdlAttribute "\'foreign" " VHPI " signal attribute -syn match vhdlAttribute "\'active" -syn match vhdlAttribute "\'delayed" -syn match vhdlAttribute "\'event" -syn match vhdlAttribute "\'last_active" -syn match vhdlAttribute "\'last_event" -syn match vhdlAttribute "\'last_value" -syn match vhdlAttribute "\'quiet" -syn match vhdlAttribute "\'stable" -syn match vhdlAttribute "\'transaction" -syn match vhdlAttribute "\'driving" -syn match vhdlAttribute "\'driving_value" +syn match vhdlAttribute "\'active" +syn match vhdlAttribute "\'delayed" +syn match vhdlAttribute "\'event" +syn match vhdlAttribute "\'last_active" +syn match vhdlAttribute "\'last_event" +syn match vhdlAttribute "\'last_value" +syn match vhdlAttribute "\'quiet" +syn match vhdlAttribute "\'stable" +syn match vhdlAttribute "\'transaction" +syn match vhdlAttribute "\'driving" +syn match vhdlAttribute "\'driving_value" " type attributes -syn match vhdlAttribute "\'base" -syn match vhdlAttribute "\'high" -syn match vhdlAttribute "\'left" -syn match vhdlAttribute "\'leftof" -syn match vhdlAttribute "\'low" -syn match vhdlAttribute "\'pos" -syn match vhdlAttribute "\'pred" -syn match vhdlAttribute "\'rightof" -syn match vhdlAttribute "\'succ" -syn match vhdlAttribute "\'val" -syn match vhdlAttribute "\'image" -syn match vhdlAttribute "\'value" - -syn keyword vhdlBoolean true false +syn match vhdlAttribute "\'base" +syn match vhdlAttribute "\'subtype" +syn match vhdlAttribute "\'element" +syn match vhdlAttribute "\'leftof" +syn match vhdlAttribute "\'pos" +syn match vhdlAttribute "\'pred" +syn match vhdlAttribute "\'rightof" +syn match vhdlAttribute "\'succ" +syn match vhdlAttribute "\'val" +syn match vhdlAttribute "\'image" +syn match vhdlAttribute "\'value" + +syn keyword vhdlBoolean true false " for this vector values case is significant -syn case match +syn case match " Values for standard VHDL types -syn match vhdlVector "\'[0L1HXWZU\-\?]\'" -" Values for non standard VHDL types qsim_12state for Mentor Graphics Sys1076/QuickHDL -"syn keyword vhdlVector S0S S1S SXS S0R S1R SXR S0Z S1Z SXZ S0I S1I SXI -syn case ignore +syn match vhdlVector "\'[0L1HXWZU\-\?]\'" +syn case ignore -syn match vhdlVector "B\"[01_]\+\"" -syn match vhdlVector "O\"[0-7_]\+\"" -syn match vhdlVector "X\"[0-9a-f_]\+\"" -syn match vhdlCharacter "'.'" -syn region vhdlString start=+"+ end=+"+ +syn match vhdlVector "B\"[01_]\+\"" +syn match vhdlVector "O\"[0-7_]\+\"" +syn match vhdlVector "X\"[0-9a-f_]\+\"" +syn match vhdlCharacter "'.'" +syn region vhdlString start=+"+ end=+"+ " floating numbers -syn match vhdlNumber "-\=\<\d\+\.\d\+\(E[+\-]\=\d\+\)\>" -syn match vhdlNumber "-\=\<\d\+\.\d\+\>" -syn match vhdlNumber "0*2#[01_]\+\.[01_]\+#\(E[+\-]\=\d\+\)\=" -syn match vhdlNumber "0*16#[0-9a-f_]\+\.[0-9a-f_]\+#\(E[+\-]\=\d\+\)\=" +syn match vhdlNumber "-\=\<\d\+\.\d\+\(E[+\-]\=\d\+\)\>" +syn match vhdlNumber "-\=\<\d\+\.\d\+\>" +syn match vhdlNumber "0*2#[01_]\+\.[01_]\+#\(E[+\-]\=\d\+\)\=" +syn match vhdlNumber "0*16#[0-9a-f_]\+\.[0-9a-f_]\+#\(E[+\-]\=\d\+\)\=" " integer numbers -syn match vhdlNumber "-\=\<\d\+\(E[+\-]\=\d\+\)\>" -syn match vhdlNumber "-\=\<\d\+\>" -syn match vhdlNumber "0*2#[01_]\+#\(E[+\-]\=\d\+\)\=" -syn match vhdlNumber "0*16#[0-9a-f_]\+#\(E[+\-]\=\d\+\)\=" +syn match vhdlNumber "-\=\<\d\+\(E[+\-]\=\d\+\)\>" +syn match vhdlNumber "-\=\<\d\+\>" +syn match vhdlNumber "0*2#[01_]\+#\(E[+\-]\=\d\+\)\=" +syn match vhdlNumber "0*16#[0-9a-f_]\+#\(E[+\-]\=\d\+\)\=" + " operators -syn keyword vhdlOperator and nand or nor xor xnor -syn keyword vhdlOperator rol ror sla sll sra srl -syn keyword vhdlOperator mod rem abs not -syn match vhdlOperator "[&><=:+\-*\/|]" -syn match vhdlSpecial "[().,;]" +syn keyword vhdlOperator and nand or nor xor xnor +syn keyword vhdlOperator rol ror sla sll sra srl +syn keyword vhdlOperator mod rem abs not + +" Concatenation and math operators +syn match vhdlOperator "&\|+\|-\|\*\|\/" + +" Equality and comparison operators +syn match vhdlOperator "=\|\/=\|>\|<\|>=" + +" Assignment operators +syn match vhdlOperator "<=\|:=" +syn match vhdlOperator "=>" + +" VHDL-2008 conversion, matching equality/non-equality operators +syn match vhdlOperator "??\|?=\|?\/=\|?<\|?<=\|?>\|?>=" + +" VHDL-2008 external names +syn match vhdlOperator "<<\|>>" + +" Linting for illegal operators +" '=' +syn match vhdlError "\(=\)[<=&+\-\*\/\\]\+" +syn match vhdlError "[=&+\-\*\\]\+\(=\)" +" '>', '<' +" Allow external names: '<< ... >>' +syn match vhdlError "\(>\)[<&+\-\/\\]\+" +syn match vhdlError "[&+\-\/\\]\+\(>\)" +syn match vhdlError "\(<\)[&+\-\/\\]\+" +syn match vhdlError "[>=&+\-\/\\]\+\(<\)" +" Covers most operators +" support negative sign after operators. E.g. q<=-b; +syn match vhdlError "\(&\|+\|\-\|\*\*\|\/=\|??\|?=\|?\/=\|?<=\|?>=\|>=\|<=\|:=\|=>\)[<>=&+\*\\?:]\+" +syn match vhdlError "[<>=&+\-\*\\:]\+\(&\|+\|\*\*\|\/=\|??\|?=\|?\/=\|?<\|?<=\|?>\|?>=\|>=\|<=\|:=\|=>\)" +syn match vhdlError "\(?<\|?>\)[<>&+\*\/\\?:]\+" +syn match vhdlError "\(<<\|>>\)[<>&+\*\/\\?:]\+" + +"syn match vhdlError "[?]\+\(&\|+\|\-\|\*\*\|??\|?=\|?\/=\|?<\|?<=\|?>\|?>=\|:=\|=>\)" +" '/' +syn match vhdlError "\(\/\)[<>&+\-\*\/\\?:]\+" +syn match vhdlError "[<>=&+\-\*\/\\:]\+\(\/\)" + +syn match vhdlSpecial "<>" +syn match vhdlSpecial "[().,;]" + + " time -syn match vhdlTime "\<\d\+\s\+\(\([fpnum]s\)\|\(sec\)\|\(min\)\|\(hr\)\)\>" -syn match vhdlTime "\<\d\+\.\d\+\s\+\(\([fpnum]s\)\|\(sec\)\|\(min\)\|\(hr\)\)\>" - -syn keyword vhdlTodo contained TODO NOTE -syn keyword vhdlFixme contained FIXME - -" Regex for space is '\s' -" Any number of spaces: \s* -" At least one space: \s+ -syn region vhdlComment start="/\*" end="\*/" contains=vhdlTodo,vhdlFixme,@Spell -syn match vhdlComment "--.*" contains=vhdlTodo,vhdlFixme,@Spell -syn match vhdlPreProc "/\* synthesis .* \*/" -syn match vhdlPreProc "/\* pragma .* \*/" -syn match vhdlPreProc "/\* synopsys .* \*/" -syn match vhdlPreProc "--\s*synthesis .*" -syn match vhdlPreProc "--\s*pragma .*" -syn match vhdlPreProc "--\s*synopsys .*" -" syn match vhdlGlobal "[\'$#~!%@?\^\[\]{}\\]" +syn match vhdlTime "\<\d\+\s\+\(\([fpnum]s\)\|\(sec\)\|\(min\)\|\(hr\)\)\>" +syn match vhdlTime "\<\d\+\.\d\+\s\+\(\([fpnum]s\)\|\(sec\)\|\(min\)\|\(hr\)\)\>" + +syn case match +syn keyword vhdlTodo contained TODO NOTE +syn keyword vhdlFixme contained FIXME +syn case ignore + +syn region vhdlComment start="/\*" end="\*/" contains=vhdlTodo,vhdlFixme,@Spell +syn match vhdlComment "\(^\|\s\)--.*" contains=vhdlTodo,vhdlFixme,@Spell + +" Industry-standard directives. These are not standard VHDL, but are commonly +" used in the industry. +syn match vhdlPreProc "/\*\s*synthesis\s\+translate_\(on\|off\)\s*\*/" +"syn match vhdlPreProc "/\*\s*simulation\s\+translate_\(on\|off\)\s*\*/" +syn match vhdlPreProc "/\*\s*pragma\s\+synthesis_\(on\|off\)\s*\*/" +syn match vhdlPreProc "/\*\s*synopsys\s\+translate_\(on\|off\)\s*\*/" + +syn match vhdlPreProc "\(^\|\s\)--\s*synthesis\s\+translate_\(on\|off\)\s*" +"syn match vhdlPreProc "\(^\|\s\)--\s*simulation\s\+translate_\(on\|off\)\s*" +syn match vhdlPreProc "\(^\|\s\)--\s*pragma\s\+synthesis_\(on\|off\)\s*" +syn match vhdlPreProc "\(^\|\s\)--\s*synopsys\s\+translate_\(on\|off\)\s*" "Modify the following as needed. The trade-off is performance versus functionality. -syn sync minlines=200 +syn sync minlines=600 " Define the default highlighting. " For version 5.7 and earlier: only when not done already " For version 5.8 and later: only when an item doesn't have highlighting yet if version >= 508 || !exists("did_vhdl_syntax_inits") - if version < 508 - let did_vhdl_syntax_inits = 1 - command -nargs=+ HiLink hi link <args> - else - command -nargs=+ HiLink hi def link <args> - endif - - HiLink vhdlSpecial Special - HiLink vhdlStatement Statement - HiLink vhdlCharacter Character - HiLink vhdlString String - HiLink vhdlVector Number - HiLink vhdlBoolean Number - HiLink vhdlTodo Todo - HiLink vhdlFixme Fixme - HiLink vhdlComment Comment - HiLink vhdlNumber Number - HiLink vhdlTime Number - HiLink vhdlType Type - HiLink vhdlOperator Operator -" HiLink vhdlGlobal Error - HiLink vhdlAttribute Special - HiLink vhdlPreProc PreProc - - delcommand HiLink + if version < 508 + let did_vhdl_syntax_inits = 1 + command -nargs=+ HiLink hi link <args> + else + command -nargs=+ HiLink hi def link <args> + endif + + HiLink vhdlSpecial Special + HiLink vhdlStatement Statement + HiLink vhdlCharacter Character + HiLink vhdlString String + HiLink vhdlVector Number + HiLink vhdlBoolean Number + HiLink vhdlTodo Todo + HiLink vhdlFixme Fixme + HiLink vhdlComment Comment + HiLink vhdlNumber Number + HiLink vhdlTime Number + HiLink vhdlType Type + HiLink vhdlOperator Operator + HiLink vhdlError Error + HiLink vhdlAttribute Special + HiLink vhdlPreProc PreProc + + delcommand HiLink endif let b:current_syntax = "vhdl" diff --git a/runtime/syntax/xmath.vim b/runtime/syntax/xmath.vim index 83efb08db2..5434f928c7 100644 --- a/runtime/syntax/xmath.vim +++ b/runtime/syntax/xmath.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: xmath (a simulation tool) -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> " Last Change: Sep 11, 2006 -" Version: 6 -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" Version: 7 +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_XMATH " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/xxd.vim b/runtime/syntax/xxd.vim index 50594e1fcf..b2b1e44603 100644 --- a/runtime/syntax/xxd.vim +++ b/runtime/syntax/xxd.vim @@ -1,10 +1,10 @@ " Vim syntax file " Language: bin using xxd -" Maintainer: Dr. Charles E. Campbell, Jr. <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Sep 06, 2005 -" Version: 7 +" Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> +" Last Change: Oct 23, 2014 +" Version: 8 " Notes: use :help xxd to see how to invoke it -" URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax +" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_XXD " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded diff --git a/runtime/syntax/yacc.vim b/runtime/syntax/yacc.vim index 17a1cab6b7..977ffa75e4 100644 --- a/runtime/syntax/yacc.vim +++ b/runtime/syntax/yacc.vim @@ -1,8 +1,8 @@ " Vim syntax file " Language: Yacc " Maintainer: Charles E. Campbell <NdrOchipS@PcampbellAfamily.Mbiz> -" Last Change: Mar 20, 2014 -" Version: 11 +" Last Change: Apr 02, 2015 +" Version: 13 " URL: http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax " " Options: {{{1 @@ -42,7 +42,7 @@ exe "syn include @yaccCode ".s:Cpath " --------------------------------------------------------------------- " Yacc Clusters: {{{1 syn cluster yaccInitCluster contains=yaccKey,yaccKeyActn,yaccBrkt,yaccType,yaccString,yaccUnionStart,yaccHeader2,yaccComment,yaccDefines,yaccParseParam,yaccParseOption -syn cluster yaccRulesCluster contains=yaccNonterminal,yaccString,yaccComment +syn cluster yaccRulesCluster contains=yaccNonterminal,yaccString " --------------------------------------------------------------------- " Yacc Sections: {{{1 diff --git a/runtime/syntax/yaml.vim b/runtime/syntax/yaml.vim index 073dbf7418..626dc8a77f 100644 --- a/runtime/syntax/yaml.vim +++ b/runtime/syntax/yaml.vim @@ -2,7 +2,7 @@ " Language: YAML (YAML Ain't Markup Language) 1.2 " Maintainer: Nikolai Pavlov <zyx.vim@gmail.com> " First author: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2010-10-08 +" Latest Revision: 2015-03-28 if exists('b:current_syntax') finish @@ -11,13 +11,40 @@ endif let s:cpo_save = &cpo set cpo&vim -let s:ns_char = '\%(\%([\n\r\uFEFF \t]\)\@!\p\)' -let s:ns_word_char = '\%(\w\|-\)' -let s:ns_uri_char = '\%(%\x\x\|'.s:ns_word_char.'\|[#/;?:@&=+$,.!~*''()\[\]]\)' +" Choose the schema to use +" TODO: Validate schema +if !exists('b:yaml_schema') + if exists('g:yaml_schema') + let b:yaml_schema = g:yaml_schema + else + let b:yaml_schema = 'core' + endif +endif + +let s:ns_char = '\%([\n\r\uFEFF \t]\@!\p\)' +let s:ns_word_char = '[[:alnum:]_\-]' +let s:ns_uri_char = '\%(%\x\x\|'.s:ns_word_char.'\|[#/;?:@&=+$,.!~*''()[\]]\)' let s:ns_tag_char = '\%(%\x\x\|'.s:ns_word_char.'\|[#/;?:@&=+$.~*''()]\)' -let s:c_ns_anchor_char = '\%(\%([\n\r\uFEFF \t,\[\]{}]\)\@!\p\)' -let s:c_indicator = '[\-?:,\[\]{}#&*!|>''"%@`]' -let s:c_flow_indicator = '[,\[\]{}]' +let s:c_ns_anchor_char = '\%([\n\r\uFEFF \t,[\]{}]\@!\p\)' +let s:c_indicator = '[\-?:,[\]{}#&*!|>''"%@`]' +let s:c_flow_indicator = '[,[\]{}]' + +let s:ns_char_without_c_indicator = substitute(s:ns_char, '\v\C[\zs', '\=s:c_indicator[1:-2]', '') + +let s:_collection = '[^\@!\(\%(\\\.\|\[^\\\]]\)\+\)]' +let s:_neg_collection = '[^\(\%(\\\.\|\[^\\\]]\)\+\)]' +function s:SimplifyToAssumeAllPrintable(p) + return substitute(a:p, '\V\C\\%('.s:_collection.'\\@!\\p\\)', '[^\1]', '') +endfunction +let s:ns_char = s:SimplifyToAssumeAllPrintable(s:ns_char) +let s:ns_char_without_c_indicator = s:SimplifyToAssumeAllPrintable(s:ns_char_without_c_indicator) +let s:c_ns_anchor_char = s:SimplifyToAssumeAllPrintable(s:c_ns_anchor_char) + +function s:SimplifyAdjacentCollections(p) + return substitute(a:p, '\V\C'.s:_collection.'\\|'.s:_collection, '[\1\2]', 'g') +endfunction +let s:ns_uri_char = s:SimplifyAdjacentCollections(s:ns_uri_char) +let s:ns_tag_char = s:SimplifyAdjacentCollections(s:ns_tag_char) let s:c_verbatim_tag = '!<'.s:ns_uri_char.'\+>' let s:c_named_tag_handle = '!'.s:ns_word_char.'\+!' @@ -46,11 +73,15 @@ let s:ns_tag_prefix = s:ns_local_tag_prefix. let s:ns_plain_safe_out = s:ns_char let s:ns_plain_safe_in = '\%('.s:c_flow_indicator.'\@!'.s:ns_char.'\)' -let s:ns_plain_first_in = '\%('.s:c_indicator.'\@!'.s:ns_char.'\|[?:\-]\%('.s:ns_plain_safe_in.'\)\@=\)' -let s:ns_plain_first_out = '\%('.s:c_indicator.'\@!'.s:ns_char.'\|[?:\-]\%('.s:ns_plain_safe_out.'\)\@=\)' +let s:ns_plain_safe_in = substitute(s:ns_plain_safe_in, '\V\C\\%('.s:_collection.'\\@!'.s:_neg_collection.'\\)', '[^\1\2]', '') +let s:ns_plain_safe_in_without_colhash = substitute(s:ns_plain_safe_in, '\V\C'.s:_neg_collection, '[^\1:#]', '') +let s:ns_plain_safe_out_without_colhash = substitute(s:ns_plain_safe_out, '\V\C'.s:_neg_collection, '[^\1:#]', '') -let s:ns_plain_char_in = '\%('.s:ns_char.'#\|:'.s:ns_plain_safe_in.'\|[:#]\@!'.s:ns_plain_safe_in.'\)' -let s:ns_plain_char_out = '\%('.s:ns_char.'#\|:'.s:ns_plain_safe_out.'\|[:#]\@!'.s:ns_plain_safe_out.'\)' +let s:ns_plain_first_in = '\%('.s:ns_char_without_c_indicator.'\|[?:\-]\%('.s:ns_plain_safe_in.'\)\@=\)' +let s:ns_plain_first_out = '\%('.s:ns_char_without_c_indicator.'\|[?:\-]\%('.s:ns_plain_safe_out.'\)\@=\)' + +let s:ns_plain_char_in = '\%('.s:ns_char.'#\|:'.s:ns_plain_safe_in.'\|'.s:ns_plain_safe_in_without_colhash.'\)' +let s:ns_plain_char_out = '\%('.s:ns_char.'#\|:'.s:ns_plain_safe_out.'\|'.s:ns_plain_safe_out_without_colhash.'\)' let s:ns_plain_out = s:ns_plain_first_out . s:ns_plain_char_out.'*' let s:ns_plain_in = s:ns_plain_first_in . s:ns_plain_char_in.'*' @@ -89,9 +120,11 @@ syn match yamlSingleEscape contained "''" syn match yamlBlockScalarHeader contained '\s\+\zs[|>]\%([+-]\=[1-9]\|[1-9]\=[+-]\)\=' +syn cluster yamlConstant contains=yamlBool,yamlNull + syn cluster yamlFlow contains=yamlFlowString,yamlFlowMapping,yamlFlowCollection syn cluster yamlFlow add=yamlFlowMappingKey,yamlFlowMappingMerge -syn cluster yamlFlow add=yamlConstant,yamlPlainScalar,yamlFloat +syn cluster yamlFlow add=@yamlConstant,yamlPlainScalar,yamlFloat syn cluster yamlFlow add=yamlTimestamp,yamlInteger,yamlMappingKeyStart syn cluster yamlFlow add=yamlComment syn region yamlFlowMapping matchgroup=yamlFlowIndicator start='{' end='}' contains=@yamlFlow @@ -103,15 +136,15 @@ execute 'syn match yamlPlainScalar contained /'.s:ns_plain_in.'/' syn match yamlMappingKeyStart '?\ze\s' syn match yamlMappingKeyStart '?' contained -execute 'syn match yamlFlowMappingKey /'.s:ns_plain_in.'\ze\s*:/ contained '. +execute 'syn match yamlFlowMappingKey /\%#=1'.s:ns_plain_in.'\%(\s\+'.s:ns_plain_in.'\)*\ze\s*:/ contained '. \'nextgroup=yamlKeyValueDelimiter' syn match yamlFlowMappingMerge /<<\ze\s*:/ contained nextgroup=yamlKeyValueDelimiter syn match yamlBlockCollectionItemStart '^\s*\zs-\%(\s\+-\)*\s' nextgroup=yamlBlockMappingKey,yamlBlockMappingMerge " Use the old regexp engine, the NFA engine doesn't like all the \@ items. -execute 'syn match yamlBlockMappingKey /\%#=1^\s*\zs'.s:ns_plain_out.'\ze\s*:\%(\s\|$\)/ '. +execute 'syn match yamlBlockMappingKey /\%#=1^\s*\zs'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ '. \'nextgroup=yamlKeyValueDelimiter' -execute 'syn match yamlBlockMappingKey /\%#=1\s*\zs'.s:ns_plain_out.'\ze\s*:\%(\s\|$\)/ contained '. +execute 'syn match yamlBlockMappingKey /\%#=1\s*\zs'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ contained '. \'nextgroup=yamlKeyValueDelimiter' syn match yamlBlockMappingMerge /^\s*\zs<<\ze:\%(\s\|$\)/ nextgroup=yamlKeyValueDelimiter syn match yamlBlockMappingMerge /<<\ze\s*:\%(\s\|$\)/ nextgroup=yamlKeyValueDelimiter contained @@ -119,14 +152,32 @@ syn match yamlBlockMappingMerge /<<\ze\s*:\%(\s\|$\)/ nextgroup=yamlKeyValueDeli syn match yamlKeyValueDelimiter /\s*:/ contained syn match yamlKeyValueDelimiter /\s*:/ contained -syn keyword yamlConstant true True TRUE false False FALSE -syn keyword yamlConstant null Null NULL -syn match yamlConstant '\<\~\>' - -syn match yamlTimestamp /\%([\[\]{}, \t]\@!\p\)\@<!\%(\d\{4}-\d\d\=-\d\d\=\%(\%([Tt]\|\s\+\)\%(\d\d\=\):\%(\d\d\):\%(\d\d\)\%(\.\%(\d*\)\)\=\%(\s*\%(Z\|[+-]\d\d\=\%(:\d\d\)\=\)\)\=\)\=\)\%([\[\]{}, \t]\@!\p\)\@!/ +syn cluster yamlScalarWithSpecials contains=yamlPlainScalar,yamlBlockMappingKey,yamlFlowMappingKey + +let s:_bounder = s:SimplifyToAssumeAllPrintable('\%([[\]{}, \t]\@!\p\)') +if b:yaml_schema is# 'json' + syn keyword yamlNull null contained containedin=@yamlScalarWithSpecials + syn keyword yamlBool true false + exe 'syn match yamlInteger /'.s:_bounder.'\@1<!\%(0\|-\=[1-9][0-9]*\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlFloat /'.s:_bounder.'\@1<!\%(-\=[1-9][0-9]*\%(\.[0-9]*\)\=\(e[-+]\=[0-9]\+\)\=\|0\|-\=\.inf\|\.nan\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' +elseif b:yaml_schema is# 'core' + syn keyword yamlNull null Null NULL contained containedin=@yamlScalarWithSpecials + syn keyword yamlBool true True TRUE false False FALSE contained containedin=@yamlScalarWithSpecials + exe 'syn match yamlNull /'.s:_bounder.'\@1<!\~'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlInteger /'.s:_bounder.'\@1<!\%([+-]\=\%(0\%(b[0-1_]\+\|[0-7_]\+\|x[0-9a-fA-F_]\+\)\=\|\%([1-9][0-9_]*\%(:[0-5]\=\d\)\+\)\)\|[1-9][0-9_]*\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlFloat /'.s:_bounder.'\@1<!\%([+-]\=\%(\%(\d[0-9_]*\)\.[0-9_]*\%([eE][+-]\=\d\+\)\=\|\.[0-9_]\+\%([eE][-+]\=[0-9]\+\)\=\|\d[0-9_]*\%(:[0-5]\=\d\)\+\.[0-9_]*\|\.\%(inf\|Inf\|INF\)\)\|\%(\.\%(nan\|NaN\|NAN\)\)\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' +elseif b:yaml_schema is# 'pyyaml' + syn keyword yamlNull null Null NULL contained containedin=@yamlScalarWithSpecials + syn keyword yamlBool true True TRUE false False FALSE yes Yes YES no No NO on On ON off Off OFF contained containedin=@yamlScalarWithSpecials + exe 'syn match yamlNull /'.s:_bounder.'\@1<!\~'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlFloat /'.s:_bounder.'\@1<!\%(\v[-+]?%(\d[0-9_]*)\.[0-9_]*%([eE][-+]\d+)?|\.[0-9_]+%([eE][-+]\d+)?|[-+]?\d[0-9_]*%(\:[0-5]?\d)+\.[0-9_]*|[-+]?\.%(inf|Inf|INF)|\.%(nan|NaN|NAN)\m\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlInteger /'.s:_bounder.'\@1<!\%(\v[-+]?0b[0-1_]+|[-+]?0[0-7_]+|[-+]?%(0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+|[-+]?[1-9][0-9_]*%(:[0-5]?\d)+\m\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlTimestamp /'.s:_bounder.'\@1<!\%(\v\d\d\d\d\-\d\d\-\d\d|\d\d\d\d \-\d\d? \-\d\d?%([Tt]|[ \t]+)\d\d?\:\d\d \:\d\d %(\.\d*)?%([ \t]*%(Z|[-+]\d\d?%(\:\d\d)?))?\m\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' +elseif b:yaml_schema is# 'failsafe' + " Nothing +endif +unlet s:_bounder -syn match yamlInteger /\%([\[\]{}, \t]\@!\p\)\@<!\%([+-]\=\%(0\%(b[0-1_]\+\|[0-7_]\+\|x[0-9a-fA-F_]\+\)\=\|\%([1-9][0-9_]*\%(:[0-5]\=\d\)\+\)\)\|[1-9][0-9_]*\)\%([\[\]{}, \t]\@!\p\)\@!/ -syn match yamlFloat /\%([\[\]{}, \t]\@!\p\)\@<!\%([+-]\=\%(\%(\d[0-9_]*\)\.[0-9_]*\%([eE][+-]\d\+\)\=\|\.[0-9_]\+\%([eE][-+][0-9]\+\)\=\|\d[0-9_]*\%(:[0-5]\=\d\)\+\.[0-9_]*\|\.\%(inf\|Inf\|INF\)\)\|\%(\.\%(nan\|NaN\|NAN\)\)\)\%([\[\]{}, \t]\@!\p\)\@!/ execute 'syn match yamlNodeTag '.string(s:c_ns_tag_property) execute 'syn match yamlAnchor '.string(s:c_ns_anchor_property) @@ -170,6 +221,9 @@ hi def link yamlKeyValueDelimiter Special hi def link yamlConstant Constant +hi def link yamlNull yamlConstant +hi def link yamlBool yamlConstant + hi def link yamlAnchor Type hi def link yamlAlias Type hi def link yamlNodeTag Type @@ -180,8 +234,10 @@ hi def link yamlTimestamp Number let b:current_syntax = "yaml" -unlet s:ns_word_char s:ns_uri_char s:c_verbatim_tag s:c_named_tag_handle s:c_secondary_tag_handle s:c_primary_tag_handle s:c_tag_handle s:ns_tag_char s:c_ns_shorthand_tag s:c_non_specific_tag s:c_ns_tag_property s:c_ns_anchor_char s:c_ns_anchor_name s:c_ns_anchor_property s:c_ns_alias_node s:ns_char s:ns_directive_name s:ns_local_tag_prefix s:ns_global_tag_prefix s:ns_tag_prefix s:c_indicator s:ns_plain_safe_out s:c_flow_indicator s:ns_plain_safe_in s:ns_plain_first_in s:ns_plain_first_out s:ns_plain_char_in s:ns_plain_char_out s:ns_plain_out s:ns_plain_in +unlet s:ns_word_char s:ns_uri_char s:c_verbatim_tag s:c_named_tag_handle s:c_secondary_tag_handle s:c_primary_tag_handle s:c_tag_handle s:ns_tag_char s:c_ns_shorthand_tag s:c_non_specific_tag s:c_ns_tag_property s:c_ns_anchor_char s:c_ns_anchor_name s:c_ns_anchor_property s:c_ns_alias_node s:ns_char s:ns_directive_name s:ns_local_tag_prefix s:ns_global_tag_prefix s:ns_tag_prefix s:c_indicator s:ns_plain_safe_out s:c_flow_indicator s:ns_plain_safe_in s:ns_plain_first_in s:ns_plain_first_out s:ns_plain_char_in s:ns_plain_char_out s:ns_plain_out s:ns_plain_in s:ns_char_without_c_indicator s:ns_plain_safe_in_without_colhash s:ns_plain_safe_out_without_colhash +unlet s:_collection s:_neg_collection +delfunction s:SimplifyAdjacentCollections +delfunction s:SimplifyToAssumeAllPrintable let &cpo = s:cpo_save unlet s:cpo_save - diff --git a/runtime/syntax/zsh.vim b/runtime/syntax/zsh.vim index 5e588e7d6c..162577669f 100644 --- a/runtime/syntax/zsh.vim +++ b/runtime/syntax/zsh.vim @@ -2,7 +2,7 @@ " Language: Zsh shell script " Maintainer: Christian Brabandt <cb@256bit.org> " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2015-05-29 +" Latest Revision: 2015-12-25 " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-zsh @@ -125,20 +125,29 @@ syn keyword zshCommands alias autoload bg bindkey break bye cap cd \ zsocket zstyle ztcp " Options, generated by: echo ${(j:\n:)options[(I)*]} | sort +" Create a list of option names from zsh source dir: +" #!/bin/zsh +" topdir=/path/to/zsh-xxx +" grep '^pindex([A-Za-z_]*)$' $topdir/Src/Doc/Zsh/optionsyo | +" while read opt +" do +" echo ${${(L)opt#pindex\(}%\)} +" done + syn case ignore syn keyword zshOptions aliases allexport all_export alwayslastprompt - \ always_lastprompt alwaystoend always_to_end - \ appendhistory append_history autocd autocontinue + \ always_last_prompt always_lastprompt alwaystoend always_to_end appendcreate + \ append_create appendhistory append_history autocd auto_cd autocontinue \ auto_continue autolist auto_list \ automenu auto_menu autonamedirs auto_name_dirs \ autoparamkeys auto_param_keys autoparamslash \ auto_param_slash autopushd auto_pushd autoremoveslash - \ auto_remove_slash autoresume auto_resume badpattern + \ auto_remove_slash autoresume auto_resume badpattern bad_pattern \ banghist bang_hist bareglobqual bare_glob_qual \ bashautolist bash_auto_list bashrematch bash_rematch - \ beep bgnice braceccl brace_ccl braceexpand brace_expand + \ beep bgnice bg_nice braceccl brace_ccl braceexpand brace_expand \ bsdecho bsd_echo caseglob case_glob casematch case_match - \ cbases cdablevars cd_able_vars chasedots chase_dots + \ cbases c_bases cdablevars cdable_vars cd_able_vars chasedots chase_dots \ chaselinks chase_links checkjobs check_jobs \ clobber combiningchars combining_chars completealiases \ complete_aliases completeinword complete_in_word @@ -146,17 +155,17 @@ syn keyword zshOptions aliases allexport all_export alwayslastprompt \ correctall correct_all cprecedences c_precedences \ cshjunkiehistory csh_junkie_history cshjunkieloops \ csh_junkie_loops cshjunkiequotes csh_junkie_quotes - \ cshnullcmd csh_null_cmd cshnullglob csh_null_glob + \ csh_nullcmd csh_null_cmd cshnullcmd csh_null_cmd cshnullglob csh_null_glob \ debugbeforecmd debug_before_cmd dotglob dot_glob dvorak \ emacs equals errexit err_exit errreturn err_return evallineno \ eval_lineno exec extendedglob extended_glob extendedhistory \ extended_history flowcontrol flow_control forcefloat - \ force_float functionargzero function_arg_zero glob globalexport + \ force_float functionargzero function_argzero function_arg_zero glob globalexport \ global_export globalrcs global_rcs globassign glob_assign \ globcomplete glob_complete globdots glob_dots glob_subst - \ globsubst hashall hash_all hashcmds hash_cmds hashdirs - \ hash_dirs hashexecutablesonly hash_executables_only hashlistall - \ hash_list_all histallowclobber hist_allow_clobber histappend + \ globsubst globstarshort glob_star_short hashall hash_all hashcmds + \ hash_cmds hashdirs hash_dirs hashexecutablesonly hash_executables_only + \ hashlistall hash_list_all histallowclobber hist_allow_clobber histappend \ hist_append histbeep hist_beep hist_expand hist_expire_dups_first \ histexpand histexpiredupsfirst histfcntllock hist_fcntl_lock \ histfindnodups hist_find_no_dups histignorealldups @@ -184,7 +193,7 @@ syn keyword zshOptions aliases allexport all_export alwayslastprompt \ numeric_glob_sort octalzeroes octal_zeroes onecmd one_cmd \ overstrike over_strike pathdirs path_dirs pathscript \ path_script physical pipefail pipe_fail posixaliases - \ posix_aliases posixargzero posix_arg_zero posixbuiltins + \ posix_aliases posixargzero posix_arg_zero posix_argzero posixbuiltins \ posix_builtins posixcd posix_cd posixidentifiers posix_identifiers \ posixjobs posix_jobs posixstrings posix_strings posixtraps \ posix_traps printeightbit print_eight_bit printexitvalue @@ -192,8 +201,8 @@ syn keyword zshOptions aliases allexport all_export alwayslastprompt \ prompt_cr promptpercent prompt_percent promptsp prompt_sp \ promptsubst prompt_subst promptvars prompt_vars pushdignoredups \ pushd_ignore_dups pushdminus pushd_minus pushdsilent pushd_silent - \ pushdtohome pushd_to_home rcexpandparam rc_expandparam rcquotes - \ rc_quotes rcs recexact rec_exact rematchpcre re_match_pcre + \ pushdtohome pushd_to_home rcexpandparam rc_expandparam rc_expand_param rcquotes + \ rc_quotes rcs recexact rec_exact rematchpcre re_match_pcre rematch_pcre \ restricted rmstarsilent rm_star_silent rmstarwait rm_star_wait \ sharehistory share_history shfileexpansion sh_file_expansion \ shglob sh_glob shinstdin shin_stdin shnullcmd sh_nullcmd @@ -201,22 +210,22 @@ syn keyword zshOptions aliases allexport all_export alwayslastprompt \ sh_word_split singlecommand single_command singlelinezle single_line_zle \ sourcetrace source_trace stdin sunkeyboardhack sun_keyboard_hack \ trackall track_all transientrprompt transient_rprompt - \ trapsasync trapasync typesetsilent type_set_silent unset verbose vi + \ trapsasync traps_async typesetsilent type_set_silent typeset_silent unset verbose vi \ warncreateglobal warn_create_global xtrace zle syn keyword zshOptions noaliases no_aliases noallexport no_allexport noall_export no_all_export noalwayslastprompt no_alwayslastprompt - \ noalways_lastprompt no_always_lastprompt noalwaystoend no_alwaystoend noalways_to_end no_always_to_end - \ noappendhistory no_appendhistory noappend_history no_append_history noautocd no_autocd noautocontinue no_autocontinue - \ noauto_continue no_auto_continue noautolist no_autolist noauto_list no_auto_list - \ noautomenu no_automenu noauto_menu no_auto_menu noautonamedirs no_autonamedirs noauto_name_dirs no_auto_name_dirs - \ noautoparamkeys no_autoparamkeys noauto_param_keys no_auto_param_keys noautoparamslash no_autoparamslash + \ noalways_lastprompt no_always_lastprompt no_always_last_prompt noalwaystoend no_alwaystoend noalways_to_end no_always_to_end + \ noappendcreate no_appendcreate no_append_create noappendhistory no_appendhistory noappend_history no_append_history noautocd + \ no_autocd no_auto_cd noautocontinue no_autocontinue noauto_continue no_auto_continue noautolist no_autolist noauto_list + \ no_auto_list noautomenu no_automenu noauto_menu no_auto_menu noautonamedirs no_autonamedirs noauto_name_dirs + \ no_auto_name_dirs noautoparamkeys no_autoparamkeys noauto_param_keys no_auto_param_keys noautoparamslash no_autoparamslash \ noauto_param_slash no_auto_param_slash noautopushd no_autopushd noauto_pushd no_auto_pushd noautoremoveslash no_autoremoveslash - \ noauto_remove_slash no_auto_remove_slash noautoresume no_autoresume noauto_resume no_auto_resume nobadpattern no_badpattern + \ noauto_remove_slash no_auto_remove_slash noautoresume no_autoresume noauto_resume no_auto_resume nobadpattern no_badpattern no_bad_pattern \ nobanghist no_banghist nobang_hist no_bang_hist nobareglobqual no_bareglobqual nobare_glob_qual no_bare_glob_qual \ nobashautolist no_bashautolist nobash_auto_list no_bash_auto_list nobashrematch no_bashrematch nobash_rematch no_bash_rematch - \ nobeep no_beep nobgnice no_bgnice nobraceccl no_braceccl nobrace_ccl no_brace_ccl nobraceexpand no_braceexpand nobrace_expand no_brace_expand + \ nobeep no_beep nobgnice no_bgnice no_bg_nice nobraceccl no_braceccl nobrace_ccl no_brace_ccl nobraceexpand no_braceexpand nobrace_expand no_brace_expand \ nobsdecho no_bsdecho nobsd_echo no_bsd_echo nocaseglob no_caseglob nocase_glob no_case_glob nocasematch no_casematch nocase_match no_case_match - \ nocbases no_cbases nocdablevars no_cdablevars nocd_able_vars no_cd_able_vars nochasedots no_chasedots nochase_dots no_chase_dots + \ nocbases no_cbases no_c_bases nocdablevars no_cdablevars no_cdable_vars nocd_able_vars no_cd_able_vars nochasedots no_chasedots nochase_dots no_chase_dots \ nochaselinks no_chaselinks nochase_links no_chase_links nocheckjobs no_checkjobs nocheck_jobs no_check_jobs \ noclobber no_clobber nocombiningchars no_combiningchars nocombining_chars no_combining_chars nocompletealiases no_completealiases \ nocomplete_aliases no_complete_aliases nocompleteinword no_completeinword nocomplete_in_word no_complete_in_word @@ -224,14 +233,15 @@ syn keyword zshOptions noaliases no_aliases noallexport no_allexport no \ nocorrectall no_correctall nocorrect_all no_correct_all nocprecedences no_cprecedences noc_precedences no_c_precedences \ nocshjunkiehistory no_cshjunkiehistory nocsh_junkie_history no_csh_junkie_history nocshjunkieloops no_cshjunkieloops \ nocsh_junkie_loops no_csh_junkie_loops nocshjunkiequotes no_cshjunkiequotes nocsh_junkie_quotes no_csh_junkie_quotes - \ nocshnullcmd no_cshnullcmd nocsh_null_cmd no_csh_null_cmd nocshnullglob no_cshnullglob nocsh_null_glob no_csh_null_glob + \ nocshnullcmd no_cshnullcmd no_csh_nullcmd nocsh_null_cmd no_csh_null_cmd nocshnullglob no_cshnullglob nocsh_null_glob no_csh_null_glob \ nodebugbeforecmd no_debugbeforecmd nodebug_before_cmd no_debug_before_cmd nodotglob no_dotglob nodot_glob no_dot_glob nodvorak no_dvorak \ noemacs no_emacs noequals no_equals noerrexit no_errexit noerr_exit no_err_exit noerrreturn no_errreturn noerr_return no_err_return noevallineno no_evallineno \ noeval_lineno no_eval_lineno noexec no_exec noextendedglob no_extendedglob noextended_glob no_extended_glob noextendedhistory no_extendedhistory \ noextended_history no_extended_history noflowcontrol no_flowcontrol noflow_control no_flow_control noforcefloat no_forcefloat - \ noforce_float no_force_float nofunctionargzero no_functionargzero nofunction_arg_zero no_function_arg_zero noglob no_glob noglobalexport no_globalexport + \ noforce_float no_force_float nofunctionargzero no_functionargzero nofunction_arg_zero no_function_argzero no_function_arg_zero noglob no_glob noglobalexport no_globalexport \ noglobal_export no_global_export noglobalrcs no_globalrcs noglobal_rcs no_global_rcs noglobassign no_globassign noglob_assign no_glob_assign - \ noglobcomplete no_globcomplete noglob_complete no_glob_complete noglobdots no_globdots noglob_dots no_glob_dots noglob_subst no_glob_subst + \ noglobcomplete no_globcomplete noglob_complete no_glob_complete noglobdots no_globdots noglob_dots no_glob_dots + \ noglobstarshort no_glob_star_short noglob_subst no_glob_subst \ noglobsubst no_globsubst nohashall no_hashall nohash_all no_hash_all nohashcmds no_hashcmds nohash_cmds no_hash_cmds nohashdirs no_hashdirs \ nohash_dirs no_hash_dirs nohashexecutablesonly no_hashexecutablesonly nohash_executables_only no_hash_executables_only nohashlistall no_hashlistall \ nohash_list_all no_hash_list_all nohistallowclobber no_histallowclobber nohist_allow_clobber no_hist_allow_clobber nohistappend no_histappend @@ -262,7 +272,7 @@ syn keyword zshOptions noaliases no_aliases noallexport no_allexport no \ nonumeric_glob_sort no_numeric_glob_sort nooctalzeroes no_octalzeroes nooctal_zeroes no_octal_zeroes noonecmd no_onecmd noone_cmd no_one_cmd \ nooverstrike no_overstrike noover_strike no_over_strike nopathdirs no_pathdirs nopath_dirs no_path_dirs nopathscript no_pathscript \ nopath_script no_path_script nophysical no_physical nopipefail no_pipefail nopipe_fail no_pipe_fail noposixaliases no_posixaliases - \ noposix_aliases no_posix_aliases noposixargzero no_posixargzero noposix_arg_zero no_posix_arg_zero noposixbuiltins no_posixbuiltins + \ noposix_aliases no_posix_aliases noposixargzero no_posixargzero no_posix_argzero noposix_arg_zero no_posix_arg_zero noposixbuiltins no_posixbuiltins \ noposix_builtins no_posix_builtins noposixcd no_posixcd noposix_cd no_posix_cd noposixidentifiers no_posixidentifiers noposix_identifiers no_posix_identifiers \ noposixjobs no_posixjobs noposix_jobs no_posix_jobs noposixstrings no_posixstrings noposix_strings no_posix_strings noposixtraps no_posixtraps \ noposix_traps no_posix_traps noprinteightbit no_printeightbit noprint_eight_bit no_print_eight_bit noprintexitvalue no_printexitvalue @@ -270,8 +280,8 @@ syn keyword zshOptions noaliases no_aliases noallexport no_allexport no \ noprompt_cr no_prompt_cr nopromptpercent no_promptpercent noprompt_percent no_prompt_percent nopromptsp no_promptsp noprompt_sp no_prompt_sp \ nopromptsubst no_promptsubst noprompt_subst no_prompt_subst nopromptvars no_promptvars noprompt_vars no_prompt_vars nopushdignoredups no_pushdignoredups \ nopushd_ignore_dups no_pushd_ignore_dups nopushdminus no_pushdminus nopushd_minus no_pushd_minus nopushdsilent no_pushdsilent nopushd_silent no_pushd_silent - \ nopushdtohome no_pushdtohome nopushd_to_home no_pushd_to_home norcexpandparam no_rcexpandparam norc_expandparam no_rc_expandparam norcquotes no_rcquotes - \ norc_quotes no_rc_quotes norcs no_rcs norecexact no_recexact norec_exact no_rec_exact norematchpcre no_rematchpcre nore_match_pcre no_re_match_pcre + \ nopushdtohome no_pushdtohome nopushd_to_home no_pushd_to_home norcexpandparam no_rcexpandparam norc_expandparam no_rc_expandparam no_rc_expand_param norcquotes no_rcquotes + \ norc_quotes no_rc_quotes norcs no_rcs norecexact no_recexact norec_exact no_rec_exact norematchpcre no_rematchpcre nore_match_pcre no_re_match_pcre no_rematch_pcre \ norestricted no_restricted normstarsilent no_rmstarsilent norm_star_silent no_rm_star_silent normstarwait no_rmstarwait norm_star_wait no_rm_star_wait \ nosharehistory no_sharehistory noshare_history no_share_history noshfileexpansion no_shfileexpansion nosh_file_expansion no_sh_file_expansion \ noshglob no_shglob nosh_glob no_sh_glob noshinstdin no_shinstdin noshin_stdin no_shin_stdin noshnullcmd no_shnullcmd nosh_nullcmd no_sh_nullcmd @@ -279,11 +289,11 @@ syn keyword zshOptions noaliases no_aliases noallexport no_allexport no \ nosh_word_split no_sh_word_split nosinglecommand no_singlecommand nosingle_command no_single_command nosinglelinezle no_singlelinezle nosingle_line_zle no_single_line_zle \ nosourcetrace no_sourcetrace nosource_trace no_source_trace nostdin no_stdin nosunkeyboardhack no_sunkeyboardhack nosun_keyboard_hack no_sun_keyboard_hack \ notrackall no_trackall notrack_all no_track_all notransientrprompt no_transientrprompt notransient_rprompt no_transient_rprompt - \ notrapsasync no_trapsasync notrapasync no_trapasync notypesetsilent no_typesetsilent notype_set_silent no_type_set_silent nounset no_unset noverbose no_verbose novi no_vi - \ nowarncreateglobal no_warncreateglobal nowarn_create_global no_warn_create_global noxtrace no_xtrace nozle no_zle + \ notrapsasync no_trapsasync notrapasync no_trapasync no_traps_async notypesetsilent no_typesetsilent notype_set_silent no_type_set_silent no_typeset_silent \nounset no_unset + \ noverbose no_verbose novi no_vi nowarncreateglobal no_warncreateglobal nowarn_create_global no_warn_create_global noxtrace no_xtrace nozle no_zle syn case match -syn keyword zshTypes float integer local typeset declare +syn keyword zshTypes float integer local typeset declare private " XXX: this may be too much " syn match zshSwitches '\s\zs--\=[a-zA-Z0-9-]\+' @@ -303,7 +313,7 @@ syn region zshMathSubst matchgroup=zshSubstDelim transparent \ start='\$((' skip='\\)' \ matchgroup=zshSubstDelim end='))' \ contains=zshParentheses,@zshSubst,zshNumber, - \ @zshDerefs,zshString + \ @zshDerefs,zshString keepend syn region zshBrackets contained transparent start='{' skip='\\}' \ end='}' syn region zshSubst matchgroup=zshSubstDelim start='\${' skip='\\}' diff --git a/runtime/vimrc_example.vim b/runtime/vimrc_example.vim index 97f646b91a..c53dde8ceb 100644 --- a/runtime/vimrc_example.vim +++ b/runtime/vimrc_example.vim @@ -1,8 +1,8 @@ " An example for a vimrc file. " " To use it, copy it to -" for Unix: ~/.vimrc -" for Windows: $VIM\_vimrc +" for Unix: $HOME/.config/nvim/init.vim +" for Windows: %LOCALAPPDATA%\nvim\init.vim set backup " keep a backup file (restore to previous version) set undofile " keep an undo file (undo changes after closing) @@ -34,10 +34,8 @@ augroup vimrcEx " When editing a file, always jump to the last known cursor position. " Don't do it when the position is invalid or when inside an event handler - " Also don't do it when the mark is in the first line, that is the default - " position when opening a file. autocmd BufReadPost * - \ if line("'\"") > 1 && line("'\"") <= line("$") | + \ if line("'\"") >= 1 && line("'\"") <= line("$") | \ execute "normal! g`\"" | \ endif diff --git a/scripts/genoptions.lua b/scripts/genoptions.lua index 2859ca1795..da53d010bd 100644 --- a/scripts/genoptions.lua +++ b/scripts/genoptions.lua @@ -39,6 +39,7 @@ local redraw_flags={ local list_flags={ comma='P_COMMA', + onecomma='P_ONECOMMA', flags='P_FLAGLIST', flagscomma='P_COMMA|P_FLAGLIST', } diff --git a/scripts/git-log-pretty-since.sh b/scripts/git-log-pretty-since.sh new file mode 100755 index 0000000000..d8e3282fb3 --- /dev/null +++ b/scripts/git-log-pretty-since.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# Shows a log with changes grouped next to their merge-commit. +# +# Parameters: +# $1 "since" commit +# $2 "inverse match" regex pattern + +set -e +set -u +set -o pipefail + +__SINCE=$1 +__INVMATCH=$2 + +is_merge_commit() { + git rev-parse $1 >/dev/null 2>&1 \ + || { echo "ERROR: invalid commit: $1"; exit 1; } + git log $1^2 >/dev/null 2>&1 && return 0 || return 1 +} + +for commit in $(git log --format='%H' --first-parent --since $__SINCE); do + if is_merge_commit ${commit} ; then + if [ -z "$__INVMATCH" ] || ! git log --oneline ${commit}^1..${commit}^2 \ + | grep -E "$__INVMATCH" >/dev/null 2>&1 ; then + git log -1 --oneline ${commit} + git log --format=' %h %s' ${commit}^1..${commit}^2 + fi + else + git log -1 --oneline ${commit} + fi +done diff --git a/scripts/legacy2luatest.pl b/scripts/legacy2luatest.pl index ebd8dad1e1..8155353fc7 100755 --- a/scripts/legacy2luatest.pl +++ b/scripts/legacy2luatest.pl @@ -287,7 +287,7 @@ local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('$test_name', function() - setup(clear) + before_each(clear) it('is working', function() @{[join "\n", map { /^$/ ? '' : ' ' . $_ } @{$test_body_lines}]} diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index d50ebd85a2..c726db3920 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -1,5 +1,5 @@ lpeg = require('lpeg') -msgpack = require('MessagePack') +mpack = require('mpack') -- lpeg grammar for building api metadata from a set of header files. It -- ignores comments and preprocessor commands and parses a very small subset @@ -115,7 +115,7 @@ static const uint8_t msgpack_metadata[] = { ]]) -- serialize the API metadata using msgpack and embed into the resulting -- binary for easy querying by clients -packed = msgpack.pack(functions) +packed = mpack.pack(functions) for i = 1, #packed do output:write(string.byte(packed, i)..', ') if i % 10 == 0 then diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000000..514e5b380a --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Performs steps to tag a release. +# +# Steps: +# Create the "release" commit: +# - CMakeLists.txt: Unset NVIM_VERSION_PRERELEASE +# - Tag the commit. +# Create the "version bump" commit: +# - CMakeLists.txt: Set NVIM_VERSION_PRERELEASE to "-dev" +# +# Manual steps: +# - CMakeLists.txt: Bump NVIM_VERSION_* as appropriate. +# - git push --follow-tags + +set -e +set -u +set -o pipefail + +cd "$(git rev-parse --show-toplevel)" + +__LAST_TAG=$(git describe --abbrev=0) +[ -z "$__LAST_TAG" ] && { echo 'ERROR: no tag found'; exit 1; } +__VERSION_MAJOR=$(grep 'set(NVIM_VERSION_MAJOR' CMakeLists.txt\ + |sed -r 's/.*NVIM_VERSION_MAJOR ([[:digit:]]).*/\1/') +__VERSION_MINOR=$(grep 'set(NVIM_VERSION_MINOR' CMakeLists.txt\ + |sed -r 's/.*NVIM_VERSION_MINOR ([[:digit:]]).*/\1/') +__VERSION_PATCH=$(grep 'set(NVIM_VERSION_PATCH' CMakeLists.txt\ + |sed -r 's/.*NVIM_VERSION_PATCH ([[:digit:]]).*/\1/') +__VERSION="${__VERSION_MAJOR}.${__VERSION_MINOR}.${__VERSION_PATCH}" +{ [ -z "$__VERSION_MAJOR" ] || [ -z "$__VERSION_MINOR" ] || [ -z "$__VERSION_PATCH" ]; } \ + && { echo "ERROR: version parse failed: '${__VERSION}'"; exit 1; } +__RELEASE_MSG="NVIM v${__VERSION} + +Features: + +Fixes: + +Changes: + +" +__BUMP_MSG="version bump" + +echo "Most recent tag: ${__LAST_TAG}" +echo "Release version: ${__VERSION}" +sed -i -r 's/(NVIM_VERSION_PRERELEASE) "-dev"/\1 ""/' CMakeLists.txt +echo "Building changelog since ${__LAST_TAG}..." +__CHANGELOG="$(./scripts/git-log-pretty-since.sh $__LAST_TAG 'vim-patch:\S')" + +git add CMakeLists.txt +git commit --edit -m "${__RELEASE_MSG} ${__CHANGELOG}" +git tag -a v${__VERSION} -m "NVIM v${__VERSION}" + +sed -i -r 's/(NVIM_VERSION_PRERELEASE) ""/\1 "-dev"/' CMakeLists.txt +nvim -c '/NVIM_VERSION' -c 'echo "Update version numbers"' CMakeLists.txt +git add CMakeLists.txt +git commit -m "$__BUMP_MSG" + +echo " +Next steps: + - Double-check NVIM_VERSION_* in CMakeLists.txt + - git push --follow-tags + - update website: index.html" diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 4a567727d8..70777535cb 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -5,9 +5,12 @@ set -u set -o pipefail readonly NEOVIM_SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -readonly VIM_SOURCE_DIR_DEFAULT=${NEOVIM_SOURCE_DIR}/.vim-src +readonly VIM_SOURCE_DIR_DEFAULT="${NEOVIM_SOURCE_DIR}/.vim-src" readonly VIM_SOURCE_DIR="${VIM_SOURCE_DIR:-${VIM_SOURCE_DIR_DEFAULT}}" readonly BASENAME="$(basename "${0}")" +readonly BRANCH_PREFIX="vim-" + +CREATED_FILES=() usage() { echo "Helper script for porting Vim patches. For more information, see" @@ -21,6 +24,7 @@ usage() { echo " -p {vim-revision} Download and apply the Vim patch vim-revision." echo " vim-revision can be a version number of the " echo " format '7.4.xxx' or a Git commit hash." + echo " -s Submit a vim-patch pull request to Neovim." echo " -r {pr-number} Review a vim-patch pull request to Neovim." echo echo "Set VIM_SOURCE_DIR to change where Vim's sources are stored." @@ -35,13 +39,33 @@ check_executable() { fi } +clean_files() { + if [[ ${#CREATED_FILES[@]} -eq 0 ]]; then + return + fi + + echo + echo "Created files:" + local file + for file in ${CREATED_FILES[@]}; do + echo " • ${file}" + done + + read -p "Delete these files (Y/n)? " -n 1 -r reply + echo + if [[ "${reply}" =~ ^[Yy]$ ]]; then + rm -- ${CREATED_FILES[@]} + else + echo "You can use 'git clean' to remove these files when you're done." + fi +} + get_vim_sources() { check_executable git - echo "Retrieving Vim sources." if [[ ! -d ${VIM_SOURCE_DIR} ]]; then echo "Cloning Vim sources into '${VIM_SOURCE_DIR}'." - git clone --depth=1000 https://github.com/vim/vim.git "${VIM_SOURCE_DIR}" + git clone https://github.com/vim/vim.git "${VIM_SOURCE_DIR}" cd "${VIM_SOURCE_DIR}" else if [[ ! -d "${VIM_SOURCE_DIR}/.git" ]]; then @@ -49,8 +73,8 @@ get_vim_sources() { echo " Please remove it and try again." exit 1 fi - echo "Updating Vim sources in '${VIM_SOURCE_DIR}'." cd "${VIM_SOURCE_DIR}" + echo "Updating Vim sources in '${VIM_SOURCE_DIR}'." git pull && echo "✔ Updated Vim sources." || echo "✘ Could not update Vim sources; ignoring error." @@ -58,11 +82,13 @@ get_vim_sources() { } commit_message() { - echo "vim-patch:${vim_version} - -${vim_message} + printf 'vim-patch:%s\n\n%s\n\n%s' "${vim_version}" \ + "${vim_message}" "${vim_commit_url}" +} -${vim_commit_url}" +find_git_remote() { + git remote -v \ + | awk '$2 ~ /github.com[:/]neovim\/neovim/ && $3 == "(fetch)" {print $1}' } assign_commit_details() { @@ -70,22 +96,26 @@ assign_commit_details() { # Interpret parameter as version number (tag). vim_version="${1}" vim_tag="v${1}" - vim_commit=$( cd "${VIM_SOURCE_DIR}" \ - && git log -1 --format="%H" ${vim_tag} ) + vim_commit=$(cd "${VIM_SOURCE_DIR}" \ + && git log -1 --format="%H" ${vim_tag}) local strip_commit_line=true else # Interpret parameter as commit hash. vim_version="${1:0:7}" - vim_commit="${1}" + vim_commit=$(cd "${VIM_SOURCE_DIR}" \ + && git log -1 --format="%H" ${vim_version}) local strip_commit_line=false fi vim_commit_url="https://github.com/vim/vim/commit/${vim_commit}" - vim_message="$(git log -1 --pretty='format:%B' "${vim_commit}")" + vim_message="$(cd "${VIM_SOURCE_DIR}" \ + && git log -1 --pretty='format:%B' "${vim_commit}" \ + | sed -e 's/\(#[0-9]*\)/vim\/vim\1/g')" if [[ ${strip_commit_line} == "true" ]]; then # Remove first line of commit message. vim_message="$(echo "${vim_message}" | sed -e '1d')" fi + patch_file="vim-${vim_version}.patch" } get_vim_patch() { @@ -100,98 +130,133 @@ get_vim_patch() { echo echo "✔ Found Vim revision '${vim_commit}'." - # Collect patch details and store into variables. - vim_full="$(git show -1 --pretty=medium "${vim_commit}")" - vim_diff="$(git show -1 "${vim_commit}" \ - | sed -e 's/\( [ab]\/src\)/\1\/nvim/g')" # Change directory to src/nvim. - neovim_message="$(commit_message)" - neovim_pr=" -\`\`\` -${vim_message} -\`\`\` + # Patch surgery: preprocess the patch. + # - transform src/ paths to src/nvim/ + local vim_full="$(git show -1 --pretty=medium "${vim_commit}" \ + | LC_ALL=C sed -e 's/\( [ab]\/src\)/\1\/nvim/g')" + local neovim_branch="${BRANCH_PREFIX}${vim_version}" -${vim_commit_url} - -Original patch: - -\`\`\`diff -${vim_diff} -\`\`\`" - neovim_branch="vim-${vim_version}" - - echo - echo "Creating Git branch." cd "${NEOVIM_SOURCE_DIR}" - output="$(git checkout -b "${neovim_branch}" 2>&1)" && - echo "✔ ${output}" || - (echo "✘ ${output}"; false) + local git_remote=$(find_git_remote) + local checked_out_branch="$(git rev-parse --abbrev-ref HEAD)" + + if [[ "${checked_out_branch}" == ${BRANCH_PREFIX}* ]]; then + echo "✔ Current branch '${checked_out_branch}' seems to be a vim-patch" + echo " branch; not creating a new branch." + else + echo + echo "Fetching '${git_remote}/master'." + output="$(git fetch "${git_remote}" master 2>&1)" && + echo "✔ ${output}" || + (echo "✘ ${output}"; false) + + echo + echo "Creating new branch '${neovim_branch}' based on '${git_remote}/master'." + cd "${NEOVIM_SOURCE_DIR}" + output="$(git checkout -b "${neovim_branch}" "${git_remote}/master" 2>&1)" && + echo "✔ ${output}" || + (echo "✘ ${output}"; false) + fi echo echo "Creating empty commit with correct commit message." - output="$(git commit --allow-empty --file 2>&1 - <<< "${neovim_message}")" && + output="$(commit_message | git commit --allow-empty --file 2>&1 -)" && echo "✔ ${output}" || (echo "✘ ${output}"; false) echo echo "Creating files." - echo "${vim_diff}" > "${NEOVIM_SOURCE_DIR}/${neovim_branch}.diff" - echo "✔ Saved diff to '${NEOVIM_SOURCE_DIR}/${neovim_branch}.diff'." - echo "${vim_full}" > "${NEOVIM_SOURCE_DIR}/${neovim_branch}.patch" - echo "✔ Saved full commit details to '${NEOVIM_SOURCE_DIR}/${neovim_branch}.patch'." - echo "${neovim_pr}" > "${NEOVIM_SOURCE_DIR}/${neovim_branch}.pr" - echo "✔ Saved suggested PR description to '${NEOVIM_SOURCE_DIR}/${neovim_branch}.pr'." - echo "You can use 'git clean' to remove these files when you're done." + echo "${vim_full}" > "${NEOVIM_SOURCE_DIR}/${patch_file}" + echo "✔ Saved full commit details to '${NEOVIM_SOURCE_DIR}/${patch_file}'." echo echo "Instructions:" echo echo " Proceed to port the patch." - echo " You might want to try 'patch -p1 < ${neovim_branch}.diff' first." + echo " You might want to try 'patch -p1 < ${patch_file}' first." + echo + echo " If the patch contains a new test, consider porting it to Lua." + echo " You might want to try 'scripts/legacy2luatest.pl'." echo echo " Stage your changes ('git add ...') and use 'git commit --amend' to commit." echo - echo " Push your changes with 'git push origin ${neovim_branch}' and create a" - echo " pull request called '[RFC] vim-patch:${vim_version}'. You might want " - echo " to use the text in '${neovim_branch}.pr' as the description of this pull request." + echo " To port additional patches related to ${vim_version} and add them to the current" + echo " branch, call '${BASENAME} -p' again. Please use this only if it wouldn't make" + echo " sense to send in each patch individually, as it will increase the size of the" + echo " pull request and make it harder to review." + echo + echo " When you are finished, use '${BASENAME} -s' to submit a pull request." echo echo " See https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-vim" echo " for more information." } -list_vim_patches() { - get_vim_sources +submit_pr() { + check_executable git + check_executable hub + + cd "${NEOVIM_SOURCE_DIR}" + local checked_out_branch="$(git rev-parse --abbrev-ref HEAD)" + if [[ "${checked_out_branch}" != ${BRANCH_PREFIX}* ]]; then + echo "✘ Current branch '${checked_out_branch}' doesn't seem to be a vim-patch branch." + exit 1 + fi + + local git_remote=$(find_git_remote) + local pr_body="$(git log --reverse --format='#### %s%n%n%b%n' ${git_remote}/master..HEAD)" + local patches=("$(git log --reverse --format='%s' ${git_remote}/master..HEAD)") + patches=(${patches[@]//vim-patch:}) # Remove 'vim-patch:' prefix for each item in array. + local pr_title="${patches[@]}" # Create space-separated string from array. + pr_title="${pr_title// /,}" # Replace spaces with commas. + + local pr_message="$(printf '[RFC] vim-patch:%s\n\n%s\n' "${pr_title#,}" "${pr_body}")" + + echo "Pushing to 'origin/${checked_out_branch}'." + output="$(git push origin "${checked_out_branch}" 2>&1)" && + echo "✔ ${output}" || + (echo "✘ ${output}"; git reset --soft HEAD^1; false) + + echo + echo "Creating pull request." + output="$(hub pull-request -F - 2>&1 <<< "${pr_message}")" && + echo "✔ ${output}" || + (echo "✘ ${output}"; false) echo - echo "Vim patches missing from Neovim:" + echo "Cleaning up files." + local patch_file + for patch_file in ${patches[@]}; do + patch_file="vim-${patch_file}.patch" + if [[ ! -f "${NEOVIM_SOURCE_DIR}/${patch_file}" ]]; then + continue + fi + rm -- "${NEOVIM_SOURCE_DIR}/${patch_file}" + echo "✔ Removed '${NEOVIM_SOURCE_DIR}/${patch_file}'." + done +} + +list_vim_patches() { + get_vim_sources - # Get tags since 7.4.442. - local vim_tags=$(cd "${VIM_SOURCE_DIR}" && \ - git tag --contains v7.4.442) + printf "\nVim patches missing from Neovim:\n" - # Get non-versioned commits since e2719096. - if git log -1 --grep='.' --invert-grep > /dev/null 2>&1 ; then - local vim_runtime_commits=$(cd "${VIM_SOURCE_DIR}" && \ - git log --format='%H' --grep='^patch' --grep='^updated for version' --invert-grep e2719096250a19ecdd9a35d13702879f163d2a50..HEAD) - else - # --invert-grep requires git 2.4+ - echo "Warning: some runtime updates may not be listed (requires git 2.4+)." - local vim_runtime_commits=$(cd "${VIM_SOURCE_DIR}" && \ - git log --format='%H' --grep='Updated' e2719096250a19ecdd9a35d13702879f163d2a50..HEAD) - fi + # Get commits since 7.4.602. + local vim_commits=$(cd "${VIM_SOURCE_DIR}" && git log --reverse --format='%H' v7.4.602..HEAD) local vim_commit - for vim_commit in ${vim_tags} ${vim_runtime_commits}; do + for vim_commit in ${vim_commits}; do local is_missing - if [[ ${vim_commit} =~ v([0-9].[0-9].([0-9]{3,4})) ]]; then - local patch_number="${BASH_REMATCH[2]}" + local vim_tag=$(cd "${VIM_SOURCE_DIR}" && git describe --tags --exact-match "${vim_commit}" 2>/dev/null) + if [[ -n "${vim_tag}" ]]; then + local patch_number="${vim_tag:5}" # Remove prefix like "v7.4." # Tagged Vim patch, check version.c: is_missing="$(sed -n '/static int included_patches/,/}/p' "${NEOVIM_SOURCE_DIR}/src/nvim/version.c" | - grep -x -e "[[:space:]]*//${patch_number} NA" -e "[[:space:]]*${patch_number}," >/dev/null && echo "false" || echo "true")" - vim_commit="${BASH_REMATCH[1]//-/.}" + grep -x -e "[[:space:]]*//[[:space:]]${patch_number} NA.*" -e "[[:space:]]*${patch_number}," >/dev/null && echo "false" || echo "true")" + vim_commit="${vim_tag#v}" else # Untagged Vim patch (e.g. runtime updates), check the Neovim git log: is_missing="$(cd "${NEOVIM_SOURCE_DIR}" && - git log -1 --no-merges --grep="vim\-patch:${vim_commit:0:7}" --pretty=format:"false")" + git log -1 --no-merges --grep="vim\-patch:${vim_commit:0:7}" --pretty=format:false)" fi if [[ ${is_missing} != "false" ]]; then @@ -203,38 +268,38 @@ list_vim_patches() { echo "Instructions:" echo echo " To port one of the above patches to Neovim, execute" - echo " this script with the patch revision as argument." + echo " this script with the patch revision as argument and" + echo " follow the instructions." echo echo " Examples: '${BASENAME} -p 7.4.487'" echo " '${BASENAME} -p 1e8ebf870720e7b671f98f22d653009826304c4f'" + echo + echo " NOTE: Please port the _oldest_ patch if you possibly can." + echo " Out-of-order patches increase the possibility of bugs." } -review_pr() { - check_executable curl - check_executable nvim - - get_vim_sources - - local pr="${1}" - echo - echo "Downloading data for pull request #${pr}." +review_commit() { + local neovim_commit_url="${1}" + local neovim_patch_url="${neovim_commit_url}.patch" local git_patch_prefix='Subject: \[PATCH\] ' - local neovim_patch="$(curl -Ssf "https://patch-diff.githubusercontent.com/raw/neovim/neovim/pull/${pr}.patch")" - echo "${neovim_patch}" > a + local neovim_patch="$(curl -Ssf "${neovim_patch_url}")" local vim_version="$(head -n 4 <<< "${neovim_patch}" | sed -n "s/${git_patch_prefix}vim-patch:\([a-z0-9.]*\)$/\1/p")" + echo if [[ -n "${vim_version}" ]]; then echo "✔ Detected Vim patch '${vim_version}'." else echo "✘ Could not detect the Vim patch number." - echo " This script assumes that the PR contains a single commit" - echo " with 'vim-patch:XXX' as its title." + echo " This script assumes that the PR contains only commits" + echo " with 'vim-patch:XXX' in their title." exit 1 fi assign_commit_details "${vim_version}" + local vim_patch_url="${vim_commit_url}.patch" + local expected_commit_message="$(commit_message)" local message_length="$(wc -l <<< "${expected_commit_message}")" local commit_message="$(tail -n +4 <<< "${neovim_patch}" | head -n "${message_length}")" @@ -246,28 +311,57 @@ review_pr() { echo "${expected_commit_message}" echo " Actual:" echo "${commit_message#${git_patch_prefix}}" - exit 1 fi - local base_name="vim-${vim_version}" echo echo "Creating files." - curl -Ssfo "${NEOVIM_SOURCE_DIR}/n${base_name}.diff" "https://patch-diff.githubusercontent.com/raw/neovim/neovim/pull/${pr}.diff" - echo "✔ Saved pull request diff to '${NEOVIM_SOURCE_DIR}/n${base_name}.diff'." - echo "${neovim_patch}" > "${NEOVIM_SOURCE_DIR}/n${base_name}.patch" - echo "✔ Saved full pull request commit details to '${NEOVIM_SOURCE_DIR}/n${base_name}.patch'." - git show "${vim_commit}" > "${NEOVIM_SOURCE_DIR}/${base_name}.diff" - echo "✔ Saved Vim diff to '${NEOVIM_SOURCE_DIR}/${base_name}.diff'." - git show "${vim_commit}" > "${NEOVIM_SOURCE_DIR}/${base_name}.patch" - echo "✔ Saved full Vim commit details to '${NEOVIM_SOURCE_DIR}/${base_name}.patch'." - echo "You can use 'git clean' to remove these files when you're done." + echo "${neovim_patch}" > "${NEOVIM_SOURCE_DIR}/n${patch_file}" + echo "✔ Saved pull request diff to '${NEOVIM_SOURCE_DIR}/n${patch_file}'." + CREATED_FILES+=("${NEOVIM_SOURCE_DIR}/n${patch_file}") + + curl -Ssfo "${NEOVIM_SOURCE_DIR}/${patch_file}" "${vim_patch_url}" + echo "✔ Saved Vim diff to '${NEOVIM_SOURCE_DIR}/${patch_file}'." + CREATED_FILES+=("${NEOVIM_SOURCE_DIR}/${patch_file}") echo echo "Launching nvim." - exec nvim -O "${NEOVIM_SOURCE_DIR}/${base_name}.diff" "${NEOVIM_SOURCE_DIR}/n${base_name}.diff" + nvim -c "cd ${NEOVIM_SOURCE_DIR}" \ + -O "${NEOVIM_SOURCE_DIR}/${patch_file}" "${NEOVIM_SOURCE_DIR}/n${patch_file}" +} + +review_pr() { + check_executable curl + check_executable nvim + check_executable jq + + get_vim_sources + + local pr="${1}" + echo + echo "Downloading data for pull request #${pr}." + + local pr_commit_urls=($(curl -Ssf "https://api.github.com/repos/neovim/neovim/pulls/${pr}/commits" \ + | jq -r '.[].html_url')) + + echo "Found ${#pr_commit_urls[@]} commit(s)." + + local pr_commit_url + local reply + for pr_commit_url in ${pr_commit_urls[@]}; do + review_commit "${pr_commit_url}" + if [[ "${pr_commit_url}" != "${pr_commit_urls[-1]}" ]]; then + read -p "Continue with next commit (Y/n)? " -n 1 -r reply + echo + if [[ ! "${reply}" =~ ^[Yy]$ ]]; then + break + fi + fi + done + + clean_files } -while getopts "hlp:r:" opt; do +while getopts "hlp:r:s" opt; do case ${opt} in h) usage @@ -285,6 +379,10 @@ while getopts "hlp:r:" opt; do review_pr "${OPTARG}" exit 0 ;; + s) + submit_pr + exit 0 + ;; *) exit 1 ;; diff --git a/src/.clang-format b/src/.clang-format index 35e545ac4b..5a910ff34b 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -1,4 +1,4 @@ -BasedOnStyle: llvm +BasedOnStyle: Google Language: Cpp ColumnLimit: 80 IndentWidth: 2 @@ -10,3 +10,9 @@ AlignEscapedNewlinesLeft: false AllowShortFunctionsOnASingleLine: false SpacesBeforeTrailingComments: 2 PenaltyReturnTypeOnItsOwnLine: 200 +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BinPackParameters: false +BreakBeforeBinaryOperators: true +ContinuationIndentWidth: 4 diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 1038895b86..6b2ce08d36 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -1,4 +1,5 @@ include(CheckLibraryExists) +include(CheckCCompilerFlag) option(USE_GCOV "Enable gcov support" OFF) @@ -41,25 +42,33 @@ include_directories(${GENERATED_DIR}) include_directories(${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR}) -file(MAKE_DIRECTORY ${GENERATED_DIR}/os) -file(MAKE_DIRECTORY ${GENERATED_DIR}/api) -file(MAKE_DIRECTORY ${GENERATED_DIR}/api/private) -file(MAKE_DIRECTORY ${GENERATED_DIR}/msgpack_rpc) -file(MAKE_DIRECTORY ${GENERATED_DIR}/tui) -file(MAKE_DIRECTORY ${GENERATED_DIR}/event) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/os) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api/private) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/msgpack_rpc) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/tui) -file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/event) - -file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c - tui/*.c event/*.c) + +file(GLOB NEOVIM_SOURCES *.c) + +foreach(subdir + os + api + api/private + msgpack_rpc + tui + event + eval + ) + file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) + file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) + file(GLOB sources ${subdir}/*.c) + list(APPEND NEOVIM_SOURCES ${sources}) +endforeach() + file(GLOB_RECURSE NEOVIM_HEADERS *.h) file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) +# Sort file lists to ensure generated files are created in the same order from +# build to build. +list(SORT NEOVIM_SOURCES) +list(SORT NEOVIM_HEADERS) + foreach(sfile ${NEOVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") @@ -72,23 +81,17 @@ list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) # Handle legacy files that don't yet pass -Wconversion. set(CONV_SOURCES buffer.c - charset.c diff.c edit.c eval.c - ex_cmds2.c ex_cmds.c ex_docmd.c ex_getln.c fileio.c - getchar.c mbyte.c memline.c message.c - misc1.c ops.c - path.c - quickfix.c regexp.c screen.c search.c @@ -108,6 +111,10 @@ if(NOT MSVC) ${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") endif() +if(DEFINED MIN_LOG_LEVEL) + add_definitions(-DMIN_LOG_LEVEL=${MIN_LOG_LEVEL}) +endif() + get_directory_property(gen_cdefs COMPILE_DEFINITIONS) foreach(gen_cdef ${gen_cdefs} DO_NOT_DEFINE_EMPTY_ATTRIBUTES) if(NOT "${gen_cdef}" MATCHES "INCLUDE_GENERATED_DECLARATIONS") @@ -252,8 +259,14 @@ install_helper(TARGETS nvim) if(CLANG_ASAN_UBSAN) message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.") + check_c_compiler_flag(-fno-sanitize-recover=all SANITIZE_RECOVER_ALL) + if(SANITIZE_RECOVER_ALL) + set(SANITIZE_RECOVER -fno-sanitize-recover=all) # Clang 3.6+ + else() + set(SANITIZE_RECOVER -fno-sanitize-recover) # Clang 3.5- + endif() set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-sanitize-recover -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist") set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ") elseif(CLANG_MSAN) message(STATUS "Enabling Clang memory sanitizer for nvim.") diff --git a/src/nvim/README.md b/src/nvim/README.md index e4939d94fd..f16c6de12f 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -11,7 +11,7 @@ that are constantly changing. As the code becomes more organized and stable, this document will be updated to reflect the changes. If you are looking for module-specific details, it is best to read the source -code. Some files are extensively commented at the top(eg: terminal.c, +code. Some files are extensively commented at the top (e.g. terminal.c, screen.c). ### Top-level program loops @@ -43,13 +43,13 @@ a typical editing session: Note that we have split user actions into sequences of inputs that change the state of the editor. While there's no documentation about a "g command -mode"(step 16), internally it is implemented similarly to "operator-pending +mode" (step 16), internally it is implemented similarly to "operator-pending mode". From this we can see that Vim has the behavior of a input-driven state -machine(more specifically, a pushdown automaton since it requires a stack for +machine (more specifically, a pushdown automaton since it requires a stack for transitioning back from states). Assuming each state has a callback responsible -for handling keys, this pseudocode(a python-like language) shows a good +for handling keys, this pseudocode (a python-like language) shows a good representation of the main program loop: ```py @@ -129,20 +129,20 @@ def insert_state(data, key): While the actual code is much more complicated, the above gives an idea of how Neovim is organized internally. Some states like the `g_command_state` or `get_operator_count_state` do not have a dedicated `state_enter` callback, but -are implicitly embedded into other states(this will change later as we continue +are implicitly embedded into other states (this will change later as we continue the refactoring effort). To start reading the actual code, here's the recommended order: -1. `state_enter()` function(state.c). This is the actual program loop, +1. `state_enter()` function (state.c). This is the actual program loop, note that a `VimState` structure is used, which contains function pointers for the callback and state data. -2. `main()` function(main.c). After all startup, `normal_enter` is called +2. `main()` function (main.c). After all startup, `normal_enter` is called at the end of function to enter normal mode. -3. `normal_enter()` function(normal.c) is a small wrapper for setting +3. `normal_enter()` function (normal.c) is a small wrapper for setting up the NormalState structure and calling `state_enter`. -4. `normal_check()` function(normal.c) is called before each iteration of +4. `normal_check()` function (normal.c) is called before each iteration of normal mode. -5. `normal_execute()` function(normal.c) is called when a key is read in normal +5. `normal_execute()` function (normal.c) is called when a key is read in normal mode. The basic structure described for normal mode in 3, 4 and 5 is used for other @@ -159,7 +159,7 @@ asynchronous events, which can include: - msgpack-rpc requests - job control callbacks -- timers(not implemented yet but the support code is already there) +- timers (not implemented yet but the support code is already there) Neovim implements this functionality by entering another event loop while waiting for characters, so instead of: @@ -180,11 +180,11 @@ def state_enter(state_callback, data): while state_callback(data, event) # invoke the callback for the current state ``` -where `event` is something the operating system delivers to us, including(but +where `event` is something the operating system delivers to us, including (but not limited to) user input. The `read_next_event()` part is internally implemented by libuv, the platform layer used by Neovim. Since Neovim inherited its code from Vim, the states are not prepared to receive -"arbitrary events", so we use a special key to represent those(When a state +"arbitrary events", so we use a special key to represent those (When a state receives an "arbitrary event", it normally doesn't do anything other update the screen). diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b7a86af134..55b535c78c 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -19,6 +19,7 @@ #include "nvim/mark.h" #include "nvim/fileio.h" #include "nvim/move.h" +#include "nvim/syntax.h" #include "nvim/window.h" #include "nvim/undo.h" @@ -44,14 +45,22 @@ Integer buffer_line_count(Buffer buffer, Error *err) /// Gets a buffer line /// +/// @deprecated use buffer_get_lines instead. +/// for positive indices (including 0) use +/// "buffer_get_lines(buffer, index, index+1, true)" +/// for negative indices use +/// "buffer_get_lines(buffer, index-1, index, true)" +/// /// @param buffer The buffer handle /// @param index The line index /// @param[out] err Details of an error that may have occurred /// @return The line string String buffer_get_line(Buffer buffer, Integer index, Error *err) { - String rv = {.size = 0}; - Array slice = buffer_get_line_slice(buffer, index, index, true, true, err); + String rv = { .size = 0 }; + + index = convert_index(index); + Array slice = buffer_get_lines(buffer, index, index+1, true, err); if (!err->set && slice.size) { rv = slice.items[0].data.string; @@ -64,6 +73,12 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// Sets a buffer line /// +/// @deprecated use buffer_set_lines instead. +/// for positive indices use +/// "buffer_set_lines(buffer, index, index+1, true, [line])" +/// for negative indices use +/// "buffer_set_lines(buffer, index-1, index, true, [line])" +/// /// @param buffer The buffer handle /// @param index The line index /// @param line The new line. @@ -71,23 +86,34 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) { Object l = STRING_OBJ(line); - Array array = {.items = &l, .size = 1}; - buffer_set_line_slice(buffer, index, index, true, true, array, err); + Array array = { .items = &l, .size = 1 }; + index = convert_index(index); + buffer_set_lines(buffer, index, index+1, true, array, err); } /// Deletes a buffer line /// +/// @deprecated use buffer_set_lines instead. +/// for positive indices use +/// "buffer_set_lines(buffer, index, index+1, true, [])" +/// for negative indices use +/// "buffer_set_lines(buffer, index-1, index, true, [])" /// @param buffer The buffer handle /// @param index The line index /// @param[out] err Details of an error that may have occurred void buffer_del_line(Buffer buffer, Integer index, Error *err) { Array array = ARRAY_DICT_INIT; - buffer_set_line_slice(buffer, index, index, true, true, array, err); + index = convert_index(index); + buffer_set_lines(buffer, index, index+1, true, array, err); } /// Retrieves a line range from the buffer /// +/// @deprecated use buffer_get_lines(buffer, newstart, newend, false) +/// where newstart = start + int(not include_start) - int(start < 0) +/// newend = end + int(include_end) - int(end < 0) +/// int(bool) = 1 if bool is true else 0 /// @param buffer The buffer handle /// @param start The first line index /// @param end The last line index @@ -102,16 +128,48 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, Boolean include_end, Error *err) { + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + return buffer_get_lines(buffer, start , end, false, err); +} + + +/// Retrieves a line range from the buffer +/// +/// Indexing is zero-based, end-exclusive. Negative indices are interpreted +/// as length+1+index, i e -1 refers to the index past the end. So to get the +/// last element set start=-2 and end=-1. +/// +/// Out-of-bounds indices are clamped to the nearest valid value, unless +/// `strict_indexing` is set. +/// +/// @param buffer The buffer handle +/// @param start The first line index +/// @param end The last line index (exclusive) +/// @param strict_indexing whether out-of-bounds should be an error. +/// @param[out] err Details of an error that may have occurred +/// @return An array of lines +ArrayOf(String) buffer_get_lines(Buffer buffer, + Integer start, + Integer end, + Boolean strict_indexing, + Error *err) +{ Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); - if (!buf || !inbounds(buf, start)) { + if (!buf) { return rv; } - start = normalize_index(buf, start) + (include_start ? 0 : 1); - include_end = include_end || (end >= buf->b_ml.ml_line_count); - end = normalize_index(buf, end) + (include_end ? 1 : 0); + bool oob = false; + start = normalize_index(buf, start, &oob); + end = normalize_index(buf, end, &oob); + + if (strict_indexing && oob) { + api_set_error(err, Validation, _("Index out of bounds")); + return rv; + } if (start >= end) { // Return 0-length array @@ -151,8 +209,14 @@ end: return rv; } + /// Replaces a line range on the buffer /// +/// @deprecated use buffer_set_lines(buffer, newstart, newend, false, lines) +/// where newstart = start + int(not include_start) + int(start < 0) +/// newend = end + int(include_end) + int(end < 0) +/// int(bool) = 1 if bool is true else 0 +/// /// @param buffer The buffer handle /// @param start The first line index /// @param end The last line index @@ -169,20 +233,52 @@ void buffer_set_line_slice(Buffer buffer, ArrayOf(String) replacement, Error *err) { + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + buffer_set_lines(buffer, start, end, false, replacement, err); +} + + +/// Replaces line range on the buffer +/// +/// Indexing is zero-based, end-exclusive. Negative indices are interpreted +/// as length+1+index, i e -1 refers to the index past the end. So to change +/// or delete the last element set start=-2 and end=-1. +/// +/// To insert lines at a given index, set both start and end to the same index. +/// To delete a range of lines, set replacement to an empty array. +/// +/// Out-of-bounds indices are clamped to the nearest valid value, unless +/// `strict_indexing` is set. +/// +/// @param buffer The buffer handle +/// @param start The first line index +/// @param end The last line index (exclusive) +/// @param strict_indexing whether out-of-bounds should be an error. +/// @param replacement An array of lines to use as replacement +/// @param[out] err Details of an error that may have occurred +void buffer_set_lines(Buffer buffer, + Integer start, + Integer end, + Boolean strict_indexing, + ArrayOf(String) replacement, + Error *err) +{ buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { return; } - if (!inbounds(buf, start)) { + bool oob = false; + start = normalize_index(buf, start, &oob); + end = normalize_index(buf, end, &oob); + + if (strict_indexing && oob) { api_set_error(err, Validation, _("Index out of bounds")); return; } - start = normalize_index(buf, start) + (include_start ? 0 : 1); - include_end = include_end || (end >= buf->b_ml.ml_line_count); - end = normalize_index(buf, end) + (include_end ? 1 : 0); if (start > end) { api_set_error(err, @@ -327,13 +423,16 @@ Object buffer_get_var(Buffer buffer, String name, Error *err) return dict_get_value(buf->b_vars, name, err); } -/// Sets a buffer-scoped (b:) variable. 'nil' value deletes the variable. +/// Sets a buffer-scoped (b:) variable /// /// @param buffer The buffer handle /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -342,7 +441,27 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) return (Object) OBJECT_INIT; } - return dict_set_value(buf->b_vars, name, value, err); + return dict_set_value(buf->b_vars, name, value, false, err); +} + +/// Removes a buffer-scoped (b:) variable +/// +/// @param buffer The buffer handle +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object buffer_del_var(Buffer buffer, String name, Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return (Object) OBJECT_INIT; + } + + return dict_set_value(buf->b_vars, name, NIL, true, err); } /// Gets a buffer option value @@ -456,6 +575,8 @@ Boolean buffer_is_valid(Buffer buffer) /// Inserts a sequence of lines to a buffer at a certain index /// +/// @deprecated use buffer_set_lines(buffer, lnum, lnum, true, lines) +/// /// @param buffer The buffer handle /// @param lnum Insert the lines after `lnum`. If negative, it will append /// to the end of the buffer. @@ -466,8 +587,9 @@ void buffer_insert(Buffer buffer, ArrayOf(String) lines, Error *err) { - bool end_start = lnum < 0; - buffer_set_line_slice(buffer, lnum, lnum, !end_start, end_start, lines, err); + // "lnum" will be the index of the line after inserting, + // no matter if it is negative or not + buffer_set_lines(buffer, lnum, lnum, true, lines, err); } /// Return a tuple (row,col) representing the position of the named mark @@ -514,6 +636,99 @@ ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err) return rv; } +/// Adds a highlight to buffer. +/// +/// This can be used for plugins which dynamically generate highlights to a +/// buffer (like a semantic highlighter or linter). The function adds a single +/// highlight to a buffer. Unlike matchaddpos() highlights follow changes to +/// line numbering (as lines are inserted/removed above the highlighted line), +/// like signs and marks do. +/// +/// "src_id" is useful for batch deletion/updating of a set of highlights. When +/// called with src_id = 0, an unique source id is generated and returned. +/// Succesive calls can pass in it as "src_id" to add new highlights to the same +/// source group. All highlights in the same group can then be cleared with +/// buffer_clear_highlight. If the highlight never will be manually deleted +/// pass in -1 for "src_id". +/// +/// If "hl_group" is the empty string no highlight is added, but a new src_id +/// is still returned. This is useful for an external plugin to synchrounously +/// request an unique src_id at initialization, and later asynchronously add and +/// clear highlights in response to buffer changes. +/// +/// @param buffer The buffer handle +/// @param src_id Source group to use or 0 to use a new group, +/// or -1 for ungrouped highlight +/// @param hl_group Name of the highlight group to use +/// @param line The line to highlight +/// @param col_start Start of range of columns to highlight +/// @param col_end End of range of columns to highlight, +/// or -1 to highlight to end of line +/// @param[out] err Details of an error that may have occurred +/// @return The src_id that was used +Integer buffer_add_highlight(Buffer buffer, + Integer src_id, + String hl_group, + Integer line, + Integer col_start, + Integer col_end, + Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return 0; + } + + if (line < 0 || line >= MAXLNUM) { + api_set_error(err, Validation, _("Line number outside range")); + return 0; + } + if (col_start < 0 || col_start > MAXCOL) { + api_set_error(err, Validation, _("Column value outside range")); + return 0; + } + if (col_end < 0 || col_end > MAXCOL) { + col_end = MAXCOL; + } + + int hlg_id = syn_name2id((char_u*)hl_group.data); + src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1, + (colnr_T)col_start+1, (colnr_T)col_end); + return src_id; +} + +/// Clears highlights from a given source group and a range of lines +/// +/// To clear a source group in the entire buffer, pass in 1 and -1 to +/// line_start and line_end respectively. +/// +/// @param buffer The buffer handle +/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param line_start Start of range of lines to clear +/// @param line_end End of range of lines to clear (exclusive) +/// or -1 to clear to end of file. +/// @param[out] err Details of an error that may have occurred +void buffer_clear_highlight(Buffer buffer, + Integer src_id, + Integer line_start, + Integer line_end, + Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return; + } + + if (line_start < 0 || line_start >= MAXLNUM) { + api_set_error(err, Validation, _("Line number outside range")); + return; + } + if (line_end < 0 || line_end > MAXLNUM) { + line_end = MAXLNUM; + } + + bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end); +} // Check if deleting lines made the cursor position invalid. // Changed the lines from "lo" to "hi" and added "extra" lines (negative if @@ -538,20 +753,26 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) } // Normalizes 0-based indexes to buffer line numbers -static int64_t normalize_index(buf_T *buf, int64_t index) +static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob) { + int64_t line_count = buf->b_ml.ml_line_count; // Fix if < 0 - index = index < 0 ? buf->b_ml.ml_line_count + index : index; + index = index < 0 ? line_count + index +1 : index; + + // Check for oob + if (index > line_count) { + *oob = true; + index = line_count; + } else if (index < 0) { + *oob = true; + index = 0; + } // Convert the index to a vim line number index++; - // Fix if > line_count - index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index; return index; } -// Returns true if the 0-indexed `index` is within the 1-indexed buffer bounds. -static bool inbounds(buf_T *buf, int64_t index) +static int64_t convert_index(int64_t index) { - linenr_T nlines = buf->b_ml.ml_line_count; - return index >= -nlines && index < nlines; + return index < 0 ? index - 1 : index; } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 6c8e324649..fbfa87d5ae 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -99,4 +99,3 @@ struct key_value_pair { #endif // NVIM_API_PRIVATE_DEFS_H - diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7a0b5191d7..db3e499427 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -90,14 +90,17 @@ Object dict_get_value(dict_T *dict, String key, Error *err) } /// Set a value in a dict. Objects are recursively expanded into their -/// vimscript equivalents. Passing 'nil' as value deletes the key. +/// vimscript equivalents. /// /// @param dict The vimscript dict /// @param key The key /// @param value The new value +/// @param del Delete key in place of setting it. Argument `value` is ignored in +/// this case. /// @param[out] err Details of an error that may have occurred /// @return the old value, if any -Object dict_set_value(dict_T *dict, String key, Object value, Error *err) +Object dict_set_value(dict_T *dict, String key, Object value, bool del, + Error *err) { Object rv = OBJECT_INIT; @@ -118,7 +121,7 @@ Object dict_set_value(dict_T *dict, String key, Object value, Error *err) dictitem_T *di = dict_find(dict, (uint8_t *)key.data, (int)key.size); - if (value.type == kObjectTypeNil) { + if (del) { // Delete the key if (di == NULL) { // Doesn't exist, fail @@ -397,13 +400,13 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) switch (obj.type) { case kObjectTypeNil: - tv->v_type = VAR_NUMBER; - tv->vval.v_number = 0; + tv->v_type = VAR_SPECIAL; + tv->vval.v_special = kSpecialVarNull; break; case kObjectTypeBoolean: - tv->v_type = VAR_NUMBER; - tv->vval.v_number = obj.data.boolean; + tv->v_type = VAR_SPECIAL; + tv->vval.v_special = obj.data.boolean? kSpecialVarTrue: kSpecialVarFalse; break; case kObjectTypeBuffer: @@ -651,6 +654,21 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup) } switch (obj->v_type) { + case VAR_SPECIAL: + switch (obj->vval.v_special) { + case kSpecialVarTrue: + case kSpecialVarFalse: { + rv.type = kObjectTypeBoolean; + rv.data.boolean = (obj->vval.v_special == kSpecialVarTrue); + break; + } + case kSpecialVarNull: { + rv.type = kObjectTypeNil; + break; + } + } + break; + case VAR_STRING: rv.type = kObjectTypeString; rv.data.string = cstr_to_string((char *) obj->vval.v_string); @@ -730,6 +748,10 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup) } } break; + + case VAR_UNKNOWN: + case VAR_FUNC: + break; } return rv; diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 126ee4072d..c8311b0aa0 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -54,13 +54,16 @@ Object tabpage_get_var(Tabpage tabpage, String name, Error *err) return dict_get_value(tab->tp_vars, name, err); } -/// Sets a tab-scoped (t:) variable. 'nil' value deletes the variable. +/// Sets a tab-scoped (t:) variable /// /// @param tabpage handle /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return The tab page handle +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -69,7 +72,27 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) return (Object) OBJECT_INIT; } - return dict_set_value(tab->tp_vars, name, value, err); + return dict_set_value(tab->tp_vars, name, value, false, err); +} + +/// Removes a tab-scoped (t:) variable +/// +/// @param tabpage handle +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object tabpage_del_var(Tabpage tabpage, String name, Error *err) +{ + tabpage_T *tab = find_tab_by_handle(tabpage, err); + + if (!tab) { + return (Object) OBJECT_INIT; + } + + return dict_set_value(tab->tp_vars, name, NIL, true, err); } /// Gets the current window in a tab page diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 9279f6b469..46ac3c9022 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -116,8 +116,14 @@ String vim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, } char *ptr = NULL; - replace_termcodes((char_u *)str.data, (char_u **)&ptr, - from_part, do_lt, special); + // Set 'cpoptions' the way we want it. + // FLAG_CPO_BSLASH set - backslashes are *not* treated specially + // FLAG_CPO_KEYCODE set - keycodes are *not* reverse-engineered + // FLAG_CPO_SPECI unset - <Key> sequences *are* interpreted + // The third from end parameter of replace_termcodes() is true so that the + // <lt> sequence is recognised - needed for a real backslash. + replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, + from_part, do_lt, special, CPO_TO_CPO_FLAGS); return cstr_as_string(ptr); } @@ -291,7 +297,7 @@ void vim_change_directory(String dir, Error *err) return; } - post_chdir(false); + post_chdir(kCdScopeGlobal); try_end(err); } @@ -331,15 +337,31 @@ Object vim_get_var(String name, Error *err) return dict_get_value(&globvardict, name, err); } -/// Sets a global variable. Passing 'nil' as value deletes the variable. +/// Sets a global variable /// /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return the old value if any +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object vim_set_var(String name, Object value, Error *err) { - return dict_set_value(&globvardict, name, value, err); + return dict_set_value(&globvardict, name, value, false, err); +} + +/// Removes a global variable +/// +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object vim_del_var(String name, Error *err) +{ + return dict_set_value(&globvardict, name, NIL, true, err); } /// Gets a vim variable diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index aad616c7bf..f644453358 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -57,8 +57,8 @@ void window_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) { win_T *win = find_window_by_handle(window, err); - if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger || - pos.items[1].type != kObjectTypeInteger) { + if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger + || pos.items[1].type != kObjectTypeInteger) { api_set_error(err, Validation, _("Argument \"pos\" must be a [row, col] array")); @@ -197,13 +197,16 @@ Object window_get_var(Window window, String name, Error *err) return dict_get_value(win->w_vars, name, err); } -/// Sets a window-scoped (w:) variable. 'nil' value deletes the variable. +/// Sets a window-scoped (w:) variable /// /// @param window The window handle /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. Object window_set_var(Window window, String name, Object value, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -212,7 +215,27 @@ Object window_set_var(Window window, String name, Object value, Error *err) return (Object) OBJECT_INIT; } - return dict_set_value(win->w_vars, name, value, err); + return dict_set_value(win->w_vars, name, value, false, err); +} + +/// Removes a window-scoped (w:) variable +/// +/// @param window The window handle +/// @param name The variable name +/// @param[out] err Details of an error that may have occurred +/// @return The old value or nil if there was no previous value. +/// +/// @warning It may return nil if there was no previous value +/// or if previous value was `v:null`. +Object window_del_var(Window window, String name, Error *err) +{ + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return (Object) OBJECT_INIT; + } + + return dict_set_value(win->w_vars, name, NIL, true, err); } /// Gets a window option value diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index b432e12c02..db97bd9dc4 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -1367,8 +1367,8 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int prev_laa = A_firstc_laa(prev_c, prev_c1); if (curr_laa) { - if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) && - !prev_laa) { + if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) + && !prev_laa) { curr_c = chg_c_laa2f(curr_laa); } else { curr_c = chg_c_laa2i(curr_laa); @@ -1454,19 +1454,19 @@ static bool A_is_harakat(int c) // (alphabet/number/punctuation) static bool A_is_iso(int c) { - return (c >= a_HAMZA && c <= a_GHAIN) || - (c >= a_TATWEEL && c <= a_HAMZA_BELOW) || - c == a_MINI_ALEF; + return ((c >= a_HAMZA && c <= a_GHAIN) + || (c >= a_TATWEEL && c <= a_HAMZA_BELOW) + || c == a_MINI_ALEF); } // A_is_formb returns true if 'c' is an Arabic 10646-1 FormB character. // (alphabet/number/punctuation) static bool A_is_formb(int c) { - return (c >= a_s_FATHATAN && c <= a_s_DAMMATAN) || - c == a_s_KASRATAN || - (c >= a_s_FATHA && c <= a_f_LAM_ALEF) || - c == a_BYTE_ORDER_MARK; + return ((c >= a_s_FATHATAN && c <= a_s_DAMMATAN) + || c == a_s_KASRATAN + || (c >= a_s_FATHA && c <= a_f_LAM_ALEF) + || c == a_BYTE_ORDER_MARK); } // A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B). diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 2b3e94d5a0..44ff540b40 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -85,10 +85,25 @@ # define PATHSEPSTR "/" #endif -static inline bool ascii_iswhite(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; -static inline bool ascii_isdigit(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; -static inline bool ascii_isxdigit(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; -static inline bool ascii_isspace(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; +static inline bool ascii_iswhite(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + +static inline bool ascii_isdigit(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + +static inline bool ascii_isxdigit(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + +static inline bool ascii_isbdigit(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + +static inline bool ascii_isspace(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; /// Checks if `c` is a space or tab character. /// @@ -122,6 +137,14 @@ static inline bool ascii_isxdigit(int c) || (c >= 'A' && c <= 'F'); } +/// Checks if `c` is a binary digit, that is, 0-1. +/// +/// @see {ascii_isdigit} +static inline bool ascii_isbdigit(int c) +{ + return (c == '0' || c == '1'); +} + /// Checks if `c` is a white-space character, that is, /// one of \f, \n, \r, \t, \v. /// diff --git a/src/nvim/assert.h b/src/nvim/assert.h index 3a900aca65..2c43777858 100644 --- a/src/nvim/assert.h +++ b/src/nvim/assert.h @@ -8,17 +8,32 @@ // defined(__has_feature) && __has_feature(...). Therefore we define Clang's // __has_feature and __has_extension macro's before referring to them. #ifndef __has_feature - #define __has_feature(x) 0 +# define __has_feature(x) 0 #endif #ifndef __has_extension - #define __has_extension __has_feature +# define __has_extension __has_feature #endif -/// STATIC_ASSERT(condition, message) - assert at compile time if !cond +/// @def STATIC_ASSERT +/// @brief Assert at compile time if condition is not satisfied. /// -/// example: -/// STATIC_ASSERT(sizeof(void *) == 8, "need 64-bits mode"); +/// Should be put on its own line, followed by a semicolon. +/// +/// Example: +/// +/// STATIC_ASSERT(sizeof(void *) == 8, "Expected 64-bit mode"); +/// +/// @param[in] condition Condition to check, should be an integer constant +/// expression. +/// @param[in] message Message which will be given if check fails. + +/// @def STATIC_ASSERT_EXPR +/// @brief Like #STATIC_ASSERT, but can be used where expressions are used. +/// +/// STATIC_ASSERT_EXPR may be put in brace initializer lists. Error message +/// given in this case is not very nice with the current implementation though +/// and `message` argument is ignored. // define STATIC_ASSERT as C11's _Static_assert whenever either C11 mode is // detected or the compiler is known to support it. Note that Clang in C99 @@ -29,50 +44,74 @@ // clearer messages we get from _Static_assert, we suppress the warnings // temporarily. +#define STATIC_ASSERT_PRAGMA_START +#define STATIC_ASSERT_PRAGMA_END +#define STATIC_ASSERT(cond, msg) \ + do { \ + STATIC_ASSERT_PRAGMA_START \ + STATIC_ASSERT_STATEMENT(cond, msg); \ + STATIC_ASSERT_PRAGMA_END \ + } while (0) + // the easiest case, when the mode is C11 (generic compiler) or Clang // advertises explicit support for c_static_assert, meaning it won't warn. #if __STDC_VERSION__ >= 201112L || __has_feature(c_static_assert) - #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) +# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) // if we're dealing with gcc >= 4.6 in C99 mode, we can still use // _Static_assert but we need to suppress warnings, this is pretty ugly. #elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) - #define STATIC_ASSERT(cond, msg) \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-pedantic\"") \ - _Static_assert(cond, msg); \ - _Pragma("GCC diagnostic pop") \ + +# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) + +# undef STATIC_ASSERT_PRAGMA_START +# define STATIC_ASSERT_PRAGMA_START \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-pedantic\"") \ + +# undef STATIC_ASSERT_PRAGMA_END +# define STATIC_ASSERT_PRAGMA_END \ + _Pragma("GCC diagnostic pop") \ // the same goes for clang in C99 mode, but we suppress a different warning #elif defined(__clang__) && __has_extension(c_static_assert) - #define STATIC_ASSERT(cond, msg) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \ - _Static_assert(cond, msg); \ - _Pragma("clang diagnostic pop") \ + +# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg) + +# undef STATIC_ASSERT_PRAGMA_START +# define STATIC_ASSERT_PRAGMA_START \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \ + +# undef STATIC_ASSERT_PRAGMA_END +# define STATIC_ASSERT_PRAGMA_END \ + _Pragma("clang diagnostic pop") \ // TODO(aktau): verify that this works, don't have MSVC on hand. #elif _MSC_VER >= 1600 - #define STATIC_ASSERT(cond, msg) static_assert(cond, msg) + +# define STATIC_ASSERT_STATEMENT(cond, msg) static_assert(cond, msg) // fallback for compilers that don't support _Static_assert or static_assert // not as pretty but gets the job done. Credit goes to Pádraig Brady and // contributors. #else - #define ASSERT_CONCAT_(a, b) a##b - #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) - // These can't be used after statements in c89. - #ifdef __COUNTER__ - #define STATIC_ASSERT(e,m) \ - { enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }; } - #else - // This can't be used twice on the same line so ensure if using in headers - // that the headers are not included twice (by wrapping in #ifndef...#endif) - // Note it doesn't cause an issue when used on same line of separate modules - // compiled with gcc -combine -fwhole-program. - #define STATIC_ASSERT(e,m) \ - { enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }; } - #endif +# define STATIC_ASSERT_STATEMENT STATIC_ASSERT_EXPR +#endif + +#define ASSERT_CONCAT_(a, b) a##b +#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) +// These can't be used after statements in c89. +#ifdef __COUNTER__ +# define STATIC_ASSERT_EXPR(e, m) \ + ((enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }) 0) +#else +// This can't be used twice on the same line so ensure if using in headers +// that the headers are not included twice (by wrapping in #ifndef...#endif) +// Note it doesn't cause an issue when used on same line of separate modules +// compiled with gcc -combine -fwhole-program. +# define STATIC_ASSERT_EXPR(e, m) \ + ((enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }) 0) #endif #endif // NVIM_ASSERT_H diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index aa4a8d8332..8d891effae 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -83,6 +83,7 @@ return { 'TermResponse', -- after setting "v:termresponse" 'TextChanged', -- text was modified 'TextChangedI', -- text was modified in Insert mode + 'TextYankPost', -- after a yank or delete was done (y, d, c) 'User', -- user defined autocommand 'VimEnter', -- after starting Vim 'VimLeave', -- before exiting Vim diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index fae8e9ecd0..72716daf0e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -22,6 +22,7 @@ #include "nvim/api/private/handle.h" #include "nvim/ascii.h" +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/charset.h" @@ -140,14 +141,21 @@ open_buffer ( /* mark cursor position as being invalid */ curwin->w_valid = 0; - if (curbuf->b_ffname != NULL - ) { + if (curbuf->b_ffname != NULL) { + int old_msg_silent = msg_silent; + if (shortmess(SHM_FILEINFO)) { + msg_silent = 1; + } + retval = readfile(curbuf->b_ffname, curbuf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_NEW); - /* Help buffer is filtered. */ - if (curbuf->b_help) + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, + flags | READ_NEW); + msg_silent = old_msg_silent; + + // Help buffer is filtered. + if (curbuf->b_help) { fix_help_buffer(); + } } else if (read_stdin) { int save_bin = curbuf->b_p_bin; linenr_T line_count; @@ -256,17 +264,16 @@ open_buffer ( return retval; } -/* - * Return TRUE if "buf" points to a valid buffer (in the buffer list). - */ -int buf_valid(buf_T *buf) +/// Check that "buf" points to a valid buffer (in the buffer list). +bool buf_valid(buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { FOR_ALL_BUFFERS(bp) { if (bp == buf) { - return TRUE; + return true; } } - return FALSE; + return false; } /* @@ -400,10 +407,9 @@ close_buffer ( buf->b_nwindows = nwindows; buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); - if ( - win_valid(win) && - win->w_buffer == buf) - win->w_buffer = NULL; /* make sure we don't use the buffer now */ + if (win_valid(win) && win->w_buffer == buf) { + win->w_buffer = NULL; // make sure we don't use the buffer now + } /* Autocommands may have deleted the buffer. */ if (!buf_valid(buf)) @@ -579,16 +585,17 @@ free_buffer_stuff ( ) { if (free_options) { - clear_wininfo(buf); /* including window-local options */ - free_buf_options(buf, TRUE); + clear_wininfo(buf); // including window-local options + free_buf_options(buf, true); ga_clear(&buf->b_s.b_langp); } - vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ + vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables hash_init(&buf->b_vars->dv_hashtab); - uc_clear(&buf->b_ucmds); /* clear local user commands */ - buf_delete_signs(buf); /* delete any signs */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ + uc_clear(&buf->b_ucmds); // clear local user commands + buf_delete_signs(buf); // delete any signs + bufhl_clear_all(buf); // delete any highligts + map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings + map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs xfree(buf->b_start_fenc); buf->b_start_fenc = NULL; } @@ -918,13 +925,15 @@ do_buffer ( if (buf == NULL) { /* could not find it */ if (start == DOBUF_FIRST) { - /* don't warn when deleting */ - if (!unload) - EMSGN(_("E86: Buffer %" PRId64 " does not exist"), count); - } else if (dir == FORWARD) + // don't warn when deleting + if (!unload) { + EMSGN(_(e_nobufnr), count); + } + } else if (dir == FORWARD) { EMSG(_("E87: Cannot go beyond last buffer")); - else + } else { EMSG(_("E88: Cannot go before first buffer")); + } return FAIL; } @@ -1284,14 +1293,15 @@ void enter_buffer(buf_T *buf) redraw_later(NOT_VALID); } -/* - * Change to the directory of the current buffer. - */ +// Change to the directory of the current buffer. +// Don't do this while still starting up. void do_autochdir(void) { if (p_acd) { - if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK) { - shorten_fnames(TRUE); + if (starting == 0 + && curbuf->b_ffname != NULL + && vim_chdirfile(curbuf->b_ffname) == OK) { + shorten_fnames(true); } } } @@ -1329,8 +1339,8 @@ buflist_new ( /* We can use inode numbers when the file exists. Works better * for hard links. */ FileID file_id; - bool file_id_valid = (sfname != NULL && - os_fileid((char *)sfname, &file_id)); + bool file_id_valid = (sfname != NULL + && os_fileid((char *)sfname, &file_id)); if (ffname != NULL && !(flags & BLN_DUMMY) && (buf = buflist_findname_file_id(ffname, &file_id, file_id_valid)) != NULL) { @@ -1414,7 +1424,6 @@ buflist_new ( return NULL; if (aborting()) /* autocmds may abort script processing */ return NULL; - /* buf->b_nwindows = 0; why was this here? */ free_buffer_stuff(buf, FALSE); /* delete local variables et al. */ /* Init the options. */ @@ -1475,6 +1484,9 @@ buflist_new ( fmarks_check_names(buf); /* check file marks for this file */ buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */ if (!(flags & BLN_DUMMY)) { + // Tricky: these autocommands may change the buffer list. They could also + // split the window with re-using the one empty buffer. This may result in + // unexpectedly losing the empty buffer. apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf); if (!buf_valid(buf)) { return NULL; @@ -1541,6 +1553,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_ep); clear_string_option(&buf->b_p_path); clear_string_option(&buf->b_p_tags); + clear_string_option(&buf->b_p_tc); clear_string_option(&buf->b_p_dict); clear_string_option(&buf->b_p_tsr); clear_string_option(&buf->b_p_qe); @@ -1596,21 +1609,28 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) col = 0; if (options & GETF_SWITCH) { - /* If 'switchbuf' contains "useopen": jump to first window containing - * "buf" if one exists */ - if (swb_flags & SWB_USEOPEN) + // If 'switchbuf' contains "useopen": jump to first window containing + // "buf" if one exists + if (swb_flags & SWB_USEOPEN) { wp = buf_jump_open_win(buf); - /* If 'switchbuf' contains "usetab": jump to first window in any tab - * page containing "buf" if one exists */ - if (wp == NULL && (swb_flags & SWB_USETAB)) + } + + // If 'switchbuf' contains "usetab": jump to first window in any tab + // page containing "buf" if one exists + if (wp == NULL && (swb_flags & SWB_USETAB)) { wp = buf_jump_open_tab(buf); - /* If 'switchbuf' contains "split" or "newtab" and the current buffer - * isn't empty: open new window */ - if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) { - if (swb_flags & SWB_NEWTAB) /* Open in a new tab */ + } + + // If 'switchbuf' contains "split", "vsplit" or "newtab" and the + // current buffer isn't empty: open new tab or window + if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB)) + && !bufempty()) { + if (swb_flags & SWB_NEWTAB) { tabpage_new(); - else if (win_split(0, 0) == FAIL) /* Open in a new window */ + } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0) + == FAIL) { return FAIL; + } RESET_BINDING(curwin); } } @@ -1709,18 +1729,15 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, return NULL; } -/* - * Find file in buffer list by a regexp pattern. - * Return fnum of the found buffer. - * Return < 0 for error. - */ -int -buflist_findpat ( - char_u *pattern, - char_u *pattern_end, /* pointer to first char after pattern */ - int unlisted, /* find unlisted buffers */ - int diffmode, /* find diff-mode buffers only */ - int curtab_only /* find buffers in current tab only */ +/// Find file in buffer list by a regexp pattern. +/// Return fnum of the found buffer. +/// Return < 0 for error. +int buflist_findpat( + const char_u *pattern, + const char_u *pattern_end, // pointer to first char after pattern + int unlisted, // find unlisted buffers + int diffmode, // find diff-mode buffers only + int curtab_only // find buffers in current tab only ) { int match = -1; @@ -1732,12 +1749,15 @@ buflist_findpat ( int toggledollar; if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) { - if (*pattern == '%') + if (*pattern == '%') { match = curbuf->b_fnum; - else + } else { match = curwin->w_alt_fnum; - if (diffmode && !diff_mode_buf(buflist_findnr(match))) + } + buf_T *found_buf = buflist_findnr(match); + if (diffmode && !(found_buf && diff_mode_buf(found_buf))) { match = -1; + } } /* * Try four ways of matching a listed buffer: @@ -2042,16 +2062,15 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, } -/* - * Return true when "wip" has 'diff' set and the diff is only for another tab - * page. That's because a diff is local to a tab page. - */ +/// Check that "wip" has 'diff' set and the diff is only for another tab page. +/// That's because a diff is local to a tab page. static bool wininfo_other_tab_diff(wininfo_T *wip) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (wip->wi_opt.wo_diff) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - /* return false when it's a window in the current tab page, thus - * the buffer was in diff mode here */ + // return false when it's a window in the current tab page, thus + // the buffer was in diff mode here if (wip->wi_win == wp) { return false; } @@ -2176,15 +2195,16 @@ void buflist_list(exarg_T *eap) len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", buf->b_fnum, buf->b_p_bl ? ' ' : 'u', - buf == curbuf ? '%' : - (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), - buf->b_ml.ml_mfp == NULL ? ' ' : - (buf->b_nwindows == 0 ? 'h' : 'a'), + buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), + buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'), !MODIFIABLE(buf) ? '-' : (buf->b_p_ro ? '=' : ' '), - (buf->b_flags & BF_READERR) ? 'x' - : (bufIsChanged(buf) ? '+' : ' '), + (buf->b_flags & BF_READERR) ? 'x' : (bufIsChanged(buf) ? '+' : ' '), NameBuff); + if (len > IOSIZE - 20) { + len = IOSIZE - 20; + } + /* put "line 999" in column 40 or after the file name */ i = 40 - vim_strsize(IObuff); do { @@ -2408,52 +2428,62 @@ void buflist_altfpos(win_T *win) buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE); } -/* - * Return TRUE if 'ffname' is not the same file as current file. - * Fname must have a full path (expanded by path_get_absolute_path()). - */ -int otherfile(char_u *ffname) +/// Check that "ffname" is not the same file as current file. +/// Fname must have a full path (expanded by path_get_absolute_path()). +/// +/// @param ffname full path name to check +bool otherfile(char_u *ffname) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return otherfile_buf(curbuf, ffname, NULL, false); } -static int otherfile_buf(buf_T *buf, char_u *ffname, - FileID *file_id_p, bool file_id_valid) +/// Check that "ffname" is not the same file as the file loaded in "buf". +/// Fname must have a full path (expanded by path_get_absolute_path()). +/// +/// @param buf buffer to check +/// @param ffname full path name to check +/// @param file_id_p information about the file at "ffname". +/// @param file_id_valid whether a valid "file_id_p" was passed in. +static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p, + bool file_id_valid) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* no name is different */ + // no name is different if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) { - return TRUE; + return true; } if (fnamecmp(ffname, buf->b_ffname) == 0) { - return FALSE; + return false; } { FileID file_id; - /* If no struct stat given, get it now */ + // If no struct stat given, get it now if (file_id_p == NULL) { file_id_p = &file_id; file_id_valid = os_fileid((char *)ffname, file_id_p); } if (!file_id_valid) { // file_id not valid, assume files are different. - return TRUE; + return true; } - /* Use dev/ino to check if the files are the same, even when the names - * are different (possible with links). Still need to compare the - * name above, for when the file doesn't exist yet. - * Problem: The dev/ino changes when a file is deleted (and created - * again) and remains the same when renamed/moved. We don't want to - * stat() each buffer each time, that would be too slow. Get the - * dev/ino again when they appear to match, but not when they appear - * to be different: Could skip a buffer when it's actually the same - * file. */ + // Use dev/ino to check if the files are the same, even when the names + // are different (possible with links). Still need to compare the + // name above, for when the file doesn't exist yet. + // Problem: The dev/ino changes when a file is deleted (and created + // again) and remains the same when renamed/moved. We don't want to + // stat() each buffer each time, that would be too slow. Get the + // dev/ino again when they appear to match, but not when they appear + // to be different: Could skip a buffer when it's actually the same + // file. if (buf_same_file_id(buf, file_id_p)) { buf_set_file_id(buf); - if (buf_same_file_id(buf, file_id_p)) - return FALSE; + if (buf_same_file_id(buf, file_id_p)) { + return false; + } } } - return TRUE; + return true; } // Set file_id for a buffer. @@ -2470,11 +2500,14 @@ void buf_set_file_id(buf_T *buf) } } -// return TRUE if file_id in buffer "buf" matches with "file_id". +/// Check that file_id in buffer "buf" matches with "file_id". +/// +/// @param buf buffer +/// @param file_id file id static bool buf_same_file_id(buf_T *buf, FileID *file_id) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return buf->file_id_valid - && os_fileid_equal(&(buf->file_id), file_id); + return buf->file_id_valid && os_fileid_equal(&(buf->file_id), file_id); } /* @@ -2757,23 +2790,28 @@ void maketitle(void) resettitle(); } -/* - * Used for title and icon: Check if "str" differs from "*last". Set "*last" - * from "str" if it does. - * Return TRUE when "*last" changed. - */ -static int ti_change(char_u *str, char_u **last) +/// Used for title and icon: Check if "str" differs from "*last". Set "*last" +/// from "str" if it does by freeing the old value of "*last" and duplicating +/// "str". +/// +/// @param str desired title string +/// @param[in,out] last current title string +// +/// @return true when "*last" changed. +static bool ti_change(char_u *str, char_u **last) + FUNC_ATTR_WARN_UNUSED_RESULT { if ((str == NULL) != (*last == NULL) || (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) { xfree(*last); - if (str == NULL) + if (str == NULL) { *last = NULL; - else + } else { *last = vim_strsave(str); - return TRUE; + } + return true; } - return FALSE; + return false; } /* @@ -2825,7 +2863,7 @@ typedef enum { /// @param fillchar Character to use when filling empty space in the statusline /// @param maxwidth The maximum width to make the statusline /// @param hltab HL attributes (can be NULL) -/// @param tabtab tab page nrs (can be NULL) +/// @param tabtab Tab clicks definition (can be NULL). /// /// @return The final width of the statusline int build_stl_str_hl( @@ -2837,13 +2875,15 @@ int build_stl_str_hl( int fillchar, int maxwidth, struct stl_hlrec *hltab, - struct stl_hlrec *tabtab + StlClickRecord *tabtab ) { int groupitem[STL_MAX_ITEM]; struct stl_item { // Where the item starts in the status line output buffer - char_u *start; + char_u *start; + // Function to run for ClickFunc items. + char *cmd; // The minimum width of the item int minwid; // The maximum width of the item @@ -2855,10 +2895,10 @@ int build_stl_str_hl( Middle, Highlight, TabPage, + ClickFunc, Trunc - } type; - } item[STL_MAX_ITEM]; - + } type; + } item[STL_MAX_ITEM]; #define TMPLEN 70 char_u tmp[TMPLEN]; char_u *usefmt = fmt; @@ -2999,7 +3039,7 @@ int build_stl_str_hl( && item[groupitem[groupdepth]].minwid == 0) { bool has_normal_items = false; for (long n = groupitem[groupdepth] + 1; n < curitem; n++) { - if (item[n].type == Normal) { + if (item[n].type == Normal || item[n].type == Highlight) { has_normal_items = true; break; } @@ -3163,6 +3203,24 @@ int build_stl_str_hl( continue; } + if (*fmt_p == STL_CLICK_FUNC) { + fmt_p++; + char *t = (char *) fmt_p; + while (*fmt_p != STL_CLICK_FUNC && *fmt_p) { + fmt_p++; + } + if (*fmt_p != STL_CLICK_FUNC) { + break; + } + item[curitem].type = ClickFunc; + item[curitem].start = out_p; + item[curitem].cmd = xmemdupz(t, (size_t) (((char *) fmt_p - t))); + item[curitem].minwid = minwid; + fmt_p++; + curitem++; + continue; + } + // Denotes the end of the minwid // the maxwid may follow immediately after if (*fmt_p == '.') { @@ -3280,6 +3338,7 @@ int build_stl_str_hl( } break; } + case STL_LINE: num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum); @@ -3820,16 +3879,37 @@ int build_stl_str_hl( // Store the info about tab pages labels. if (tabtab != NULL) { - struct stl_hlrec *sp = tabtab; + StlClickRecord *cur_tab_rec = tabtab; for (long l = 0; l < itemcnt; l++) { if (item[l].type == TabPage) { - sp->start = item[l].start; - sp->userhl = item[l].minwid; - sp++; + cur_tab_rec->start = (char *) item[l].start; + if (item[l].minwid == 0) { + cur_tab_rec->def.type = kStlClickDisabled; + cur_tab_rec->def.tabnr = 0; + } else { + int tabnr = item[l].minwid; + if (item[l].minwid > 0) { + cur_tab_rec->def.type = kStlClickTabSwitch; + } else { + cur_tab_rec->def.type = kStlClickTabClose; + tabnr = -tabnr; + } + cur_tab_rec->def.tabnr = tabnr; + } + cur_tab_rec->def.func = NULL; + cur_tab_rec++; + } else if (item[l].type == ClickFunc) { + cur_tab_rec->start = (char *) item[l].start; + cur_tab_rec->def.type = kStlClickFuncRun; + cur_tab_rec->def.tabnr = item[l].minwid; + cur_tab_rec->def.func = item[l].cmd; + cur_tab_rec++; } } - sp->start = NULL; - sp->userhl = 0; + cur_tab_rec->start = NULL; + cur_tab_rec->def.type = kStlClickDisabled; + cur_tab_rec->def.tabnr = 0; + cur_tab_rec->def.func = NULL; } return width; @@ -3851,6 +3931,11 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) above = wp->w_topline - 1; above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; + if (wp->w_topline == 1 && wp->w_topfill >= 1) { + // All buffer lines are displayed and there is an indication + // of filler lines, that can be considered seeing all lines. + above = 0; + } below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1; if (below <= 0) STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen); @@ -3862,26 +3947,29 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) : (int)(above * 100L / (above + below))); } -/* - * Append (file 2 of 8) to "buf[buflen]", if editing more than one file. - * Return TRUE if it was appended. - */ -static int -append_arg_number ( - win_T *wp, - char_u *buf, - int buflen, - int add_file /* Add "file" before the arg number */ -) +/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file. +/// +/// @param wp window whose buffers to check +/// @param[in,out] buf string buffer to add the text to +/// @param buflen length of the string buffer +/// @param add_file if true, add "file" before the arg number +/// +/// @return true if it was appended. +static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) + FUNC_ATTR_NONNULL_ALL { - char_u *p; + // Nothing to do + if (ARGCOUNT <= 1) { + return false; + } - if (ARGCOUNT <= 1) /* nothing to do */ - return FALSE; + char_u *p = buf + STRLEN(buf); // go to the end of the buffer + + // Early out if the string is getting too long + if (p - buf + 35 >= buflen) { + return false; + } - p = buf + STRLEN(buf); /* go to the end of the buffer */ - if (p - buf + 35 >= buflen) /* getting too long */ - return FALSE; *p++ = ' '; *p++ = '('; if (add_file) { @@ -3889,9 +3977,10 @@ append_arg_number ( p += 5; } vim_snprintf((char *)p, (size_t)(buflen - (p - buf)), - wp->w_arg_idx_invalid ? "(%d) of %d)" - : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); - return TRUE; + wp->w_arg_idx_invalid + ? "(%d) of %d)" + : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); + return true; } /* @@ -4525,11 +4614,16 @@ char_u *buf_spname(buf_T *buf) return NULL; } -/* - * Find a window for buffer "buf". - * If found true is returned and "wp" and "tp" are set to the window and tabpage. - * If not found false is returned. - */ +/// Find a window for buffer "buf". +/// If found true is returned and "wp" and "tp" are set to +/// the window and tabpage. +/// If not found, false is returned. +/// +/// @param buf buffer to find a window for +/// @param[out] wp stores the found window +/// @param[out] tp stores the found tabpage +/// +/// @return true if a window was found for the buffer. bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) { *wp = NULL; @@ -4811,6 +4905,224 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a } } +// bufhl: plugin highlights associated with a buffer + +/// Adds a highlight to buffer. +/// +/// Unlike matchaddpos() highlights follow changes to line numbering (as lines +/// are inserted/removed above the highlighted line), like signs and marks do. +/// +/// When called with "src_id" set to 0, a unique source id is generated and +/// returned. Succesive calls can pass it in as "src_id" to add new highlights +/// to the same source group. All highlights in the same group can be cleared +/// at once. If the highlight never will be manually deleted pass in -1 for +/// "src_id" +/// +/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id +/// is still returned. +/// +/// @param buf The buffer to add highlights to +/// @param src_id src_id to use or 0 to use a new src_id group, +/// or -1 for ungrouped highlight. +/// @param hl_id Id of the highlight group to use +/// @param lnum The line to highlight +/// @param col_start First column to highlight +/// @param col_end The last column to highlight, +/// or -1 to highlight to end of line +/// @return The src_id that was used +int bufhl_add_hl(buf_T *buf, + int src_id, + int hl_id, + linenr_T lnum, + colnr_T col_start, + colnr_T col_end) { + static int next_src_id = 1; + if (src_id == 0) { + src_id = next_src_id++; + } + if (hl_id <= 0) { + // no highlight group or invalid line, just return src_id + return src_id; + } + if (!buf->b_bufhl_info) { + buf->b_bufhl_info = map_new(linenr_T, bufhl_vec_T)(); + } + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, + lnum, true); + + bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo); + hlentry->src_id = src_id; + hlentry->hl_id = hl_id; + hlentry->start = col_start; + hlentry->stop = col_end; + + if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { + changed_lines_buf(buf, lnum, lnum+1, 0); + redraw_buf_later(buf, VALID); + } + return src_id; +} + +/// Clear bufhl highlights from a given source group and range of lines. +/// +/// @param buf The buffer to remove highlights from +/// @param src_id highlight source group to clear, or -1 to clear all groups. +/// @param line_start first line to clear +/// @param line_end last line to clear or MAXLNUM to clear to end of file. +void bufhl_clear_line_range(buf_T *buf, + int src_id, + linenr_T line_start, + linenr_T line_end) { + if (!buf->b_bufhl_info) { + return; + } + linenr_T line; + linenr_T first_changed = MAXLNUM, last_changed = -1; + // In the case line_start - line_end << bufhl_info->size + // it might be better to reverse this, i e loop over the lines + // to clear on. + bufhl_vec_T unused; + map_foreach(buf->b_bufhl_info, line, unused, { + (void)unused; + if (line_start <= line && line <= line_end) { + if (bufhl_clear_line(buf->b_bufhl_info, src_id, line)) { + if (line > last_changed) { + last_changed = line; + } + if (line < first_changed) { + first_changed = line; + } + } + } + }) + + if (last_changed != -1) { + changed_lines_buf(buf, first_changed, last_changed+1, 0); + redraw_buf_later(buf, VALID); + } +} + +/// Clear bufhl highlights from a given source group and given line +/// +/// @param bufhl_info The highlight info for the buffer +/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param lnum Linenr where the highlight should be cleared +static bool bufhl_clear_line(bufhl_info_T *bufhl_info, int src_id, int lnum) { + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(bufhl_info, + lnum, false); + size_t oldsize = kv_size(*lineinfo); + if (src_id < 0) { + kv_size(*lineinfo) = 0; + } else { + size_t newind = 0; + for (size_t i = 0; i < kv_size(*lineinfo); i++) { + if (kv_A(*lineinfo, i).src_id != src_id) { + if (i != newind) { + kv_A(*lineinfo, newind) = kv_A(*lineinfo, i); + } + newind++; + } + } + kv_size(*lineinfo) = newind; + } + + if (kv_size(*lineinfo) == 0) { + kv_destroy(*lineinfo); + map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum); + } + return kv_size(*lineinfo) != oldsize; +} + +/// Remove all highlights and free the highlight data +void bufhl_clear_all(buf_T* buf) { + if (!buf->b_bufhl_info) { + return; + } + bufhl_clear_line_range(buf, -1, 1, MAXLNUM); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = NULL; +} + +/// Adjust a placed highlight for inserted/deleted lines. +void bufhl_mark_adjust(buf_T* buf, + linenr_T line1, + linenr_T line2, + long amount, + long amount_after) { + if (!buf->b_bufhl_info) { + return; + } + + bufhl_info_T *newmap = map_new(linenr_T, bufhl_vec_T)(); + linenr_T line; + bufhl_vec_T lineinfo; + map_foreach(buf->b_bufhl_info, line, lineinfo, { + if (line >= line1 && line <= line2) { + if (amount == MAXLNUM) { + bufhl_clear_line(buf->b_bufhl_info, -1, line); + continue; + } else { + line += amount; + } + } else if (line > line2) { + line += amount_after; + } + map_put(linenr_T, bufhl_vec_T)(newmap, line, lineinfo); + }); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = newmap; +} + + +/// Get highlights to display at a specific line +/// +/// @param buf The buffer handle +/// @param lnum The line number +/// @param[out] info The highligts for the line +/// @return true if there was highlights to display +bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) { + if (!buf->b_bufhl_info) { + return false; + } + + info->valid_to = -1; + info->entries = map_get(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum); + return kv_size(info->entries) > 0; +} + +/// get highlighting at column col +/// +/// It is is assumed this will be called with +/// non-decreasing column nrs, so that it is +/// possible to only recalculate highlights +/// at endpoints. +/// +/// @param info The info returned by bufhl_start_line +/// @param col The column to get the attr for +/// @return The highilight attr to display at the column +int bufhl_get_attr(bufhl_lineinfo_T *info, colnr_T col) { + if (col <= info->valid_to) { + return info->current; + } + int attr = 0; + info->valid_to = MAXCOL; + for (size_t i = 0; i < kv_size(info->entries); i++) { + bufhl_hl_item_T entry = kv_A(info->entries, i); + if (entry.start <= col && col <= entry.stop) { + int entry_attr = syn_id2attr(entry.hl_id); + attr = hl_combine_attr(attr, entry_attr); + if (entry.stop < info->valid_to) { + info->valid_to = entry.stop; + } + } else if (col < entry.start && entry.start-1 < info->valid_to) { + info->valid_to = entry.start-1; + } + } + info->current = attr; + return attr; +} + + /* * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. */ @@ -4825,50 +5137,54 @@ void set_buflisted(int on) } } -/* - * Read the file for "buf" again and check if the contents changed. - * Return TRUE if it changed or this could not be checked. - */ -int buf_contents_changed(buf_T *buf) +/// Read the file for "buf" again and check if the contents changed. +/// Return true if it changed or this could not be checked. +/// +/// @param buf buffer to check +/// +/// @return true if the buffer's contents have changed +bool buf_contents_changed(buf_T *buf) + FUNC_ATTR_NONNULL_ALL { - buf_T *newbuf; - int differ = TRUE; - linenr_T lnum; - aco_save_T aco; - exarg_T ea; + bool differ = true; - /* Allocate a buffer without putting it in the buffer list. */ - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); - if (newbuf == NULL) - return TRUE; + // Allocate a buffer without putting it in the buffer list. + buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + if (newbuf == NULL) { + return true; + } - /* Force the 'fileencoding' and 'fileformat' to be equal. */ + // Force the 'fileencoding' and 'fileformat' to be equal. + exarg_T ea; prep_exarg(&ea, buf); - /* set curwin/curbuf to buf and save a few things */ + // set curwin/curbuf to buf and save a few things + aco_save_T aco; aucmd_prepbuf(&aco, newbuf); if (ml_open(curbuf) == OK && readfile(buf->b_ffname, buf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, - &ea, READ_NEW | READ_DUMMY) == OK) { - /* compare the two files line by line */ + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, + &ea, READ_NEW | READ_DUMMY) == OK) { + // compare the two files line by line if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) { - differ = FALSE; - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) - if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0) { - differ = TRUE; + differ = false; + for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { + if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) { + differ = true; break; } + } } } xfree(ea.cmd); - /* restore curwin/curbuf and a few other things */ + // restore curwin/curbuf and a few other things aucmd_restbuf(&aco); - if (curbuf != newbuf) /* safety check */ - wipe_buffer(newbuf, FALSE); + if (curbuf != newbuf) { // safety check + wipe_buffer(newbuf, false); + } return differ; } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 49025d3925..d51a2f7dae 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -4,6 +4,7 @@ #include "nvim/window.h" #include "nvim/pos.h" // for linenr_T #include "nvim/ex_cmds_defs.h" // for exarg_T +#include "nvim/screen.h" // for StlClickRecord // Values for buflist_getfile() enum getf_values { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 3eabb7ee43..0324f6b88a 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -28,6 +28,8 @@ typedef struct file_buffer buf_T; // Forward declaration #include "nvim/profile.h" // for String #include "nvim/api/private/defs.h" +// for Map(K, V) +#include "nvim/map.h" #define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma) @@ -59,21 +61,21 @@ typedef struct file_buffer buf_T; // Forward declaration #define VALID_BOTLINE_AP 0x40 /* w_botine is approximated */ #define VALID_TOPLINE 0x80 /* w_topline is valid (for cursor position) */ -/* flags for b_flags */ -#define BF_RECOVERED 0x01 /* buffer has been recovered */ -#define BF_CHECK_RO 0x02 /* need to check readonly when loading file - into buffer (set by ":e", may be reset by - ":buf" */ -#define BF_NEVERLOADED 0x04 /* file has never been loaded into buffer, - many variables still need to be set */ -#define BF_NOTEDITED 0x08 /* Set when file name is changed after - starting to edit, reset when file is - written out. */ -#define BF_NEW 0x10 /* file didn't exist when editing started */ -#define BF_NEW_W 0x20 /* Warned for BF_NEW and file created */ -#define BF_READERR 0x40 /* got errors while reading the file */ -#define BF_DUMMY 0x80 /* dummy buffer, only used internally */ -#define BF_PRESERVED 0x100 /* ":preserve" was used */ +// flags for b_flags +#define BF_RECOVERED 0x01 // buffer has been recovered +#define BF_CHECK_RO 0x02 // need to check readonly when loading file + // into buffer (set by ":e", may be reset by + // ":buf") +#define BF_NEVERLOADED 0x04 // file has never been loaded into buffer, + // many variables still need to be set +#define BF_NOTEDITED 0x08 // Set when file name is changed after + // starting to edit, reset when file is + // written out. +#define BF_NEW 0x10 // file didn't exist when editing started +#define BF_NEW_W 0x20 // Warned for BF_NEW and file created +#define BF_READERR 0x40 // got errors while reading the file +#define BF_DUMMY 0x80 // dummy buffer, only used internally +#define BF_PRESERVED 0x100 // ":preserve" was used /* Mask to check for flags that prevent normal writing */ #define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR) @@ -101,6 +103,11 @@ typedef int scid_T; /* script ID */ // for signlist_T #include "nvim/sign_defs.h" +// for bufhl_*_T +#include "nvim/bufhl_defs.h" + +typedef Map(linenr_T, bufhl_vec_T) bufhl_info_T; + // for FileID #include "nvim/os/fs_defs.h" @@ -134,8 +141,8 @@ struct buffblock { struct buffheader { buffblock_T bh_first; // first (dummy) block of list buffblock_T *bh_curr; // buffblock for appending - int bh_index; // index for reading - int bh_space; // space in bh_curr for appending + size_t bh_index; // index for reading + size_t bh_space; // space in bh_curr for appending }; /* @@ -526,9 +533,9 @@ struct file_buffer { /* * Character table, only used in charset.c for 'iskeyword' - * 32 bytes of 8 bits: 1 bit per character 0-255. + * bitset with 4*64=256 bits: 1 bit per character 0-255. */ - char_u b_chartab[32]; + uint64_t b_chartab[4]; /* Table used for mappings local to a buffer. */ mapblock_T *(b_maphash[256]); @@ -592,84 +599,88 @@ struct file_buffer { int b_p_scriptID[BV_COUNT]; /* SIDs for buffer-local options */ - int b_p_ai; /* 'autoindent' */ - int b_p_ai_nopaste; /* b_p_ai saved for paste mode */ - char_u *b_p_bkc; ///< 'backupcopy' - unsigned int b_bkc_flags; ///< flags for 'backupcopy' - int b_p_ci; /* 'copyindent' */ - int b_p_bin; /* 'binary' */ - int b_p_bomb; /* 'bomb' */ - char_u *b_p_bh; /* 'bufhidden' */ - char_u *b_p_bt; /* 'buftype' */ - int b_p_bl; /* 'buflisted' */ - int b_p_cin; /* 'cindent' */ - char_u *b_p_cino; /* 'cinoptions' */ - char_u *b_p_cink; /* 'cinkeys' */ - char_u *b_p_cinw; /* 'cinwords' */ - char_u *b_p_com; /* 'comments' */ - char_u *b_p_cms; /* 'commentstring' */ - char_u *b_p_cpt; /* 'complete' */ - char_u *b_p_cfu; /* 'completefunc' */ - char_u *b_p_ofu; /* 'omnifunc' */ - int b_p_eol; /* 'endofline' */ - int b_p_et; /* 'expandtab' */ - int b_p_et_nobin; /* b_p_et saved for binary mode */ - char_u *b_p_fenc; /* 'fileencoding' */ - char_u *b_p_ff; /* 'fileformat' */ - char_u *b_p_ft; /* 'filetype' */ - char_u *b_p_fo; /* 'formatoptions' */ - char_u *b_p_flp; /* 'formatlistpat' */ - int b_p_inf; /* 'infercase' */ - char_u *b_p_isk; /* 'iskeyword' */ - char_u *b_p_def; /* 'define' local value */ - char_u *b_p_inc; /* 'include' */ - char_u *b_p_inex; /* 'includeexpr' */ - uint32_t b_p_inex_flags; /* flags for 'includeexpr' */ - char_u *b_p_inde; /* 'indentexpr' */ - uint32_t b_p_inde_flags; /* flags for 'indentexpr' */ - char_u *b_p_indk; /* 'indentkeys' */ - char_u *b_p_fex; /* 'formatexpr' */ - uint32_t b_p_fex_flags; /* flags for 'formatexpr' */ - char_u *b_p_kp; /* 'keywordprg' */ - int b_p_lisp; /* 'lisp' */ - char_u *b_p_mps; /* 'matchpairs' */ - int b_p_ml; /* 'modeline' */ - int b_p_ml_nobin; /* b_p_ml saved for binary mode */ - int b_p_ma; /* 'modifiable' */ - char_u *b_p_nf; /* 'nrformats' */ - int b_p_pi; /* 'preserveindent' */ - char_u *b_p_qe; /* 'quoteescape' */ - int b_p_ro; /* 'readonly' */ - long b_p_sw; /* 'shiftwidth' */ - int b_p_si; /* 'smartindent' */ - long b_p_sts; /* 'softtabstop' */ - long b_p_sts_nopaste; /* b_p_sts saved for paste mode */ - char_u *b_p_sua; /* 'suffixesadd' */ - bool b_p_swf; /* 'swapfile' */ - long b_p_smc; /* 'synmaxcol' */ - char_u *b_p_syn; /* 'syntax' */ - long b_p_ts; /* 'tabstop' */ - long b_p_tw; /* 'textwidth' */ - long b_p_tw_nobin; /* b_p_tw saved for binary mode */ - long b_p_tw_nopaste; /* b_p_tw saved for paste mode */ - long b_p_wm; /* 'wrapmargin' */ - long b_p_wm_nobin; /* b_p_wm saved for binary mode */ - long b_p_wm_nopaste; /* b_p_wm saved for paste mode */ - char_u *b_p_keymap; /* 'keymap' */ - - /* local values for options which are normally global */ - char_u *b_p_gp; /* 'grepprg' local value */ - char_u *b_p_mp; /* 'makeprg' local value */ - char_u *b_p_efm; /* 'errorformat' local value */ - char_u *b_p_ep; /* 'equalprg' local value */ - char_u *b_p_path; /* 'path' local value */ - int b_p_ar; /* 'autoread' local value */ - char_u *b_p_tags; /* 'tags' local value */ - char_u *b_p_dict; /* 'dictionary' local value */ - char_u *b_p_tsr; /* 'thesaurus' local value */ - long b_p_ul; /* 'undolevels' local value */ - int b_p_udf; /* 'undofile' */ - char_u *b_p_lw; // 'lispwords' local value + int b_p_ai; ///< 'autoindent' + int b_p_ai_nopaste; ///< b_p_ai saved for paste mode + char_u *b_p_bkc; ///< 'backupco + unsigned int b_bkc_flags; ///< flags for 'backupco + int b_p_ci; ///< 'copyindent' + int b_p_bin; ///< 'binary' + int b_p_bomb; ///< 'bomb' + char_u *b_p_bh; ///< 'bufhidden' + char_u *b_p_bt; ///< 'buftype' + int b_p_bl; ///< 'buflisted' + int b_p_cin; ///< 'cindent' + char_u *b_p_cino; ///< 'cinoptions' + char_u *b_p_cink; ///< 'cinkeys' + char_u *b_p_cinw; ///< 'cinwords' + char_u *b_p_com; ///< 'comments' + char_u *b_p_cms; ///< 'commentstring' + char_u *b_p_cpt; ///< 'complete' + char_u *b_p_cfu; ///< 'completefunc' + char_u *b_p_ofu; ///< 'omnifunc' + int b_p_eol; ///< 'endofline' + int b_p_fixeol; ///< 'fixendofline' + int b_p_et; ///< 'expandtab' + int b_p_et_nobin; ///< b_p_et saved for binary mode + int b_p_et_nopaste; ///< b_p_et saved for paste mode + char_u *b_p_fenc; ///< 'fileencoding' + char_u *b_p_ff; ///< 'fileformat' + char_u *b_p_ft; ///< 'filetype' + char_u *b_p_fo; ///< 'formatoptions' + char_u *b_p_flp; ///< 'formatlistpat' + int b_p_inf; ///< 'infercase' + char_u *b_p_isk; ///< 'iskeyword' + char_u *b_p_def; ///< 'define' local value + char_u *b_p_inc; ///< 'include' + char_u *b_p_inex; ///< 'includeexpr' + uint32_t b_p_inex_flags; ///< flags for 'includeexpr' + char_u *b_p_inde; ///< 'indentexpr' + uint32_t b_p_inde_flags; ///< flags for 'indentexpr' + char_u *b_p_indk; ///< 'indentkeys' + char_u *b_p_fex; ///< 'formatexpr' + uint32_t b_p_fex_flags; ///< flags for 'formatexpr' + char_u *b_p_kp; ///< 'keywordprg' + int b_p_lisp; ///< 'lisp' + char_u *b_p_mps; ///< 'matchpairs' + int b_p_ml; ///< 'modeline' + int b_p_ml_nobin; ///< b_p_ml saved for binary mode + int b_p_ma; ///< 'modifiable' + char_u *b_p_nf; ///< 'nrformats' + int b_p_pi; ///< 'preserveindent' + char_u *b_p_qe; ///< 'quoteescape' + int b_p_ro; ///< 'readonly' + long b_p_sw; ///< 'shiftwidth' + int b_p_si; ///< 'smartindent' + long b_p_sts; ///< 'softtabstop' + long b_p_sts_nopaste; ///< b_p_sts saved for paste mode + char_u *b_p_sua; ///< 'suffixesadd' + bool b_p_swf; ///< 'swapfile' + long b_p_smc; ///< 'synmaxcol' + char_u *b_p_syn; ///< 'syntax' + long b_p_ts; ///< 'tabstop' + long b_p_tw; ///< 'textwidth' + long b_p_tw_nobin; ///< b_p_tw saved for binary mode + long b_p_tw_nopaste; ///< b_p_tw saved for paste mode + long b_p_wm; ///< 'wrapmargin' + long b_p_wm_nobin; ///< b_p_wm saved for binary mode + long b_p_wm_nopaste; ///< b_p_wm saved for paste mode + char_u *b_p_keymap; ///< 'keymap' + + // local values for options which are normally global + char_u *b_p_gp; ///< 'grepprg' local value + char_u *b_p_mp; ///< 'makeprg' local value + char_u *b_p_efm; ///< 'errorformat' local value + char_u *b_p_ep; ///< 'equalprg' local value + char_u *b_p_path; ///< 'path' local value + int b_p_ar; ///< 'autoread' local value + char_u *b_p_tags; ///< 'tags' local value + char_u *b_p_tc; ///< 'tagcase' local value + unsigned b_tc_flags; ///< flags for 'tagcase' + char_u *b_p_dict; ///< 'dictionary' local value + char_u *b_p_tsr; ///< 'thesaurus' local value + long b_p_ul; ///< 'undolevels' local value + int b_p_udf; ///< 'undofile' + char_u *b_p_lw; ///< 'lispwords' local value /* end of buffer options */ @@ -750,6 +761,10 @@ struct file_buffer { Terminal *terminal; // Terminal instance associated with the buffer dict_T *additional_data; // Additional data from shada file if any. + + int b_mapped_ctrl_c; // modes where CTRL-C is mapped + + bufhl_info_T *b_bufhl_info; // buffer stored highlights }; /* @@ -803,10 +818,12 @@ struct tabpage_S { was set */ diff_T *tp_first_diff; buf_T *(tp_diffbuf[DB_COUNT]); - int tp_diff_invalid; /* list of diffs is outdated */ - frame_T *(tp_snapshot[SNAP_COUNT]); /* window layout snapshots */ - dictitem_T tp_winvar; /* variable for "t:" Dictionary */ - dict_T *tp_vars; /* internal variables, local to tab page */ + int tp_diff_invalid; ///< list of diffs is outdated */ + frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots + dictitem_T tp_winvar; ///< variable for "t:" Dictionary + dict_T *tp_vars; ///< internal variables, local to tab page + char_u *localdir; ///< Absolute path of local directory or + ///< NULL }; /* @@ -901,13 +918,14 @@ struct posmatch typedef struct matchitem matchitem_T; struct matchitem { matchitem_T *next; - int id; /* match ID */ - int priority; /* match priority */ - char_u *pattern; /* pattern to highlight */ - int hlg_id; /* highlight group ID */ - regmmatch_T match; /* regexp program for pattern */ - posmatch_T pos; // position matches - match_T hl; /* struct for doing the actual highlighting */ + int id; ///< match ID + int priority; ///< match priority + char_u *pattern; ///< pattern to highlight + int hlg_id; ///< highlight group ID + regmmatch_T match; ///< regexp program for pattern + posmatch_T pos; ///< position matches + match_T hl; ///< struct for doing the actual highlighting + int conceal_char; ///< cchar for Conceal highlighting }; /* @@ -939,16 +957,14 @@ struct window_S { time through cursupdate() to the current virtual column */ - /* - * the next six are used to update the visual part - */ - char w_old_visual_mode; /* last known VIsual_mode */ - linenr_T w_old_cursor_lnum; /* last known end of visual part */ - colnr_T w_old_cursor_fcol; /* first column for block visual part */ - colnr_T w_old_cursor_lcol; /* last column for block visual part */ - linenr_T w_old_visual_lnum; /* last known start of visual part */ - colnr_T w_old_visual_col; /* last known start of visual part */ - colnr_T w_old_curswant; /* last known value of Curswant */ + // the next seven are used to update the visual part + char w_old_visual_mode; ///< last known VIsual_mode + linenr_T w_old_cursor_lnum; ///< last known end of visual part + colnr_T w_old_cursor_fcol; ///< first column for block visual part + colnr_T w_old_cursor_lcol; ///< last column for block visual part + linenr_T w_old_visual_lnum; ///< last known start of visual part + colnr_T w_old_visual_col; ///< last known start of visual part + colnr_T w_old_curswant; ///< last known value of Curswant /* * "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h new file mode 100644 index 0000000000..e47bb2a238 --- /dev/null +++ b/src/nvim/bufhl_defs.h @@ -0,0 +1,25 @@ +#ifndef NVIM_BUFHL_DEFS_H +#define NVIM_BUFHL_DEFS_H + +#include "nvim/pos.h" +#include "nvim/lib/kvec.h" +// bufhl: buffer specific highlighting + +struct bufhl_hl_item +{ + int src_id; + int hl_id; // highlight group + colnr_T start; // first column to highlight + colnr_T stop; // last column to highlight +}; +typedef struct bufhl_hl_item bufhl_hl_item_T; + +typedef kvec_t(struct bufhl_hl_item) bufhl_vec_T; + +typedef struct { + bufhl_vec_T entries; + int current; + colnr_T valid_to; +} bufhl_lineinfo_T; + +#endif // NVIM_BUFHL_DEFS_H diff --git a/src/nvim/charset.c b/src/nvim/charset.c index b93eafbf60..d0dc7b66fc 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -32,16 +32,16 @@ #endif -static int chartab_initialized = FALSE; +static bool chartab_initialized = false; -// b_chartab[] is an array of 32 bytes, each bit representing one of the +// b_chartab[] is an array with 256 bits, each bit representing one of the // characters 0-255. #define SET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] |= (1 << ((c) & 0x7)) + (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f)) #define RESET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] &= ~(1 << ((c) & 0x7)) + (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) #define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 3] & (1 << ((c) & 0x7))) + ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) /// Fill chartab[]. Also fills curbuf->b_chartab[] with flags for keyword /// characters for current buffer. @@ -69,12 +69,12 @@ static int chartab_initialized = FALSE; /// an error, OK otherwise. int init_chartab(void) { - return buf_init_chartab(curbuf, TRUE); + return buf_init_chartab(curbuf, true); } /// Helper for init_chartab /// -/// @param global FALSE: only set buf->b_chartab[] +/// @param global false: only set buf->b_chartab[] /// /// @return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has /// an error, OK otherwise. @@ -84,13 +84,13 @@ int buf_init_chartab(buf_T *buf, int global) int c2; char_u *p; int i; - int tilde; - int do_isalpha; + bool tilde; + bool do_isalpha; if (global) { // Set the default size for printable characters: // From <Space> to '~' is 1 (printable), others are 2 (not printable). - // This also inits all 'isident' and 'isfname' flags to FALSE. + // This also inits all 'isident' and 'isfname' flags to false. c = 0; while (c < ' ') { @@ -133,7 +133,7 @@ int buf_init_chartab(buf_T *buf, int global) } } - // Init word char flags all to FALSE + // Init word char flags all to false memset(buf->b_chartab, 0, (size_t)32); if (enc_dbcs != 0) { @@ -169,11 +169,11 @@ int buf_init_chartab(buf_T *buf, int global) } while (*p) { - tilde = FALSE; - do_isalpha = FALSE; + tilde = false; + do_isalpha = false; if ((*p == '^') && (p[1] != NUL)) { - tilde = TRUE; + tilde = true; ++p; } @@ -212,7 +212,7 @@ int buf_init_chartab(buf_T *buf, int global) // standard function isalpha(). This takes care of locale for // single-byte characters). if (c == '@') { - do_isalpha = TRUE; + do_isalpha = true; c = 1; c2 = 255; } else { @@ -231,7 +231,7 @@ int buf_init_chartab(buf_T *buf, int global) if (i == 0) { // (re)set ID flag if (tilde) { - chartab[c] &= ~CT_ID_CHAR; + chartab[c] &= (uint8_t)~CT_ID_CHAR; } else { chartab[c] |= CT_ID_CHAR; } @@ -244,18 +244,18 @@ int buf_init_chartab(buf_T *buf, int global) || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) && !(enc_dbcs && (MB_BYTE2LEN(c) == 2))) { if (tilde) { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) - + ((dy_flags & DY_UHEX) ? 4 : 2); - chartab[c] &= ~CT_PRINT_CHAR; + chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + + ((dy_flags & DY_UHEX) ? 4 : 2)); + chartab[c] &= (uint8_t)~CT_PRINT_CHAR; } else { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) + 1; + chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + 1); chartab[c] |= CT_PRINT_CHAR; } } } else if (i == 2) { // (re)set fname flag if (tilde) { - chartab[c] &= ~CT_FNAME_CHAR; + chartab[c] &= (uint8_t)~CT_FNAME_CHAR; } else { chartab[c] |= CT_FNAME_CHAR; } @@ -280,7 +280,7 @@ int buf_init_chartab(buf_T *buf, int global) } } } - chartab_initialized = TRUE; + chartab_initialized = true; return OK; } @@ -333,7 +333,8 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET { char_u *res; char_u *p; - int l, c; + int c; + size_t l; char_u hexbuf[11]; if (has_mbyte) { @@ -343,7 +344,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET p = s; while (*p != NUL) { - if ((l = (*mb_ptr2len)(p)) > 1) { + if ((l = (size_t)(*mb_ptr2len)(p)) > 1) { c = (*mb_ptr2char)(p); p += l; @@ -354,7 +355,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET len += STRLEN(hexbuf); } } else { - l = byte2cells(*p++); + l = (size_t)byte2cells(*p++); if (l > 0) { len += l; @@ -366,14 +367,14 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET } res = xmallocz(len); } else { - res = xmallocz(vim_strsize(s)); + res = xmallocz((size_t)vim_strsize(s)); } *res = NUL; p = s; while (*p != NUL) { - if (has_mbyte && ((l = (*mb_ptr2len)(p)) > 1)) { + if (has_mbyte && ((l = (size_t)(*mb_ptr2len)(p)) > 1)) { c = (*mb_ptr2char)(p); if (vim_isprintc(c)) { @@ -477,9 +478,9 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) i += (*mb_ptr2len)(STR_PTR(i)); } else { if (buf == NULL) { - GA_CHAR(i) = TOLOWER_LOC(GA_CHAR(i)); + GA_CHAR(i) = (char_u)TOLOWER_LOC(GA_CHAR(i)); } else { - buf[i] = TOLOWER_LOC(buf[i]); + buf[i] = (char_u)TOLOWER_LOC(buf[i]); } ++i; } @@ -493,7 +494,7 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) // Catch 22: chartab[] can't be initialized before the options are // initialized, and initializing options may cause transchar() to be called! -// When chartab_initialized == FALSE don't use chartab[]. +// When chartab_initialized == false don't use chartab[]. // Does NOT work for multi-byte characters, c must be <= 255. // Also doesn't work for the first byte of a multi-byte, "c" must be a // character! @@ -518,7 +519,7 @@ char_u* transchar(int c) if ((!chartab_initialized && (((c >= ' ') && (c <= '~')) || F_ischar(c))) || ((c < 256) && vim_isprintc_strict(c))) { // printable character - transchar_buf[i] = c; + transchar_buf[i] = (char_u)c; transchar_buf[i + 1] = NUL; } else { transchar_nonprint(transchar_buf + i, c); @@ -564,7 +565,7 @@ void transchar_nonprint(char_u *buf, int c) // 0x00 - 0x1f and 0x7f buf[0] = '^'; // DEL displayed as ^? - buf[1] = c ^ 0x40; + buf[1] = (char_u)(c ^ 0x40); buf[2] = NUL; } else if (enc_utf8 && (c >= 0x80)) { @@ -572,12 +573,12 @@ void transchar_nonprint(char_u *buf, int c) } else if ((c >= ' ' + 0x80) && (c <= '~' + 0x80)) { // 0xa0 - 0xfe buf[0] = '|'; - buf[1] = c - 0x80; + buf[1] = (char_u)(c - 0x80); buf[2] = NUL; } else { // 0x80 - 0x9f and 0xff buf[0] = '~'; - buf[1] = (c - 0x80) ^ 0x40; + buf[1] = (char_u)((c - 0x80) ^ 0x40); buf[2] = NUL; } } @@ -592,11 +593,11 @@ void transchar_hex(char_u *buf, int c) buf[0] = '<'; if (c > 255) { - buf[++i] = nr2hex((unsigned)c >> 12); - buf[++i] = nr2hex((unsigned)c >> 8); + buf[++i] = (char_u)nr2hex((unsigned)c >> 12); + buf[++i] = (char_u)nr2hex((unsigned)c >> 8); } - buf[++i] = nr2hex((unsigned)c >> 4); - buf[++i] = nr2hex((unsigned)c); + buf[++i] = (char_u)(nr2hex((unsigned)c >> 4)); + buf[++i] = (char_u)(nr2hex((unsigned)c)); buf[++i] = '>'; buf[++i] = NUL; } @@ -734,9 +735,8 @@ int vim_strnsize(char_u *s, int len) /// @return Number of characters. #define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \ - int ts; \ - ts = (buf)->b_p_ts; \ - return (int)(ts - (col % ts)); \ + const int ts = (int) (buf)->b_p_ts; \ + return (ts - (int)(col % ts)); \ } else { \ return ptr2cells(p); \ } @@ -799,32 +799,35 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len) return (unsigned int)col; } -/// Return TRUE if 'c' is a normal identifier character: -/// +/// Check that "c" is a normal identifier character: /// Letters and characters from the 'isident' option. /// -/// @param c -/// -/// @return TRUE if 'c' is a normal identifier character. -int vim_isIDc(int c) +/// @param c character to check +bool vim_isIDc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return c > 0 && c < 0x100 && (chartab[c] & CT_ID_CHAR); } -/// return TRUE if 'c' is a keyword character: Letters and characters from -/// 'iskeyword' option for current buffer. -/// +/// Check that "c" is a keyword character: +/// Letters and characters from 'iskeyword' option for current buffer. /// For multi-byte characters mb_get_class() is used (builtin rules). /// -/// @param c -/// -/// @return TRUE if 'c' is a keyword character. -int vim_iswordc(int c) +/// @param c character to check +bool vim_iswordc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return vim_iswordc_buf(c, curbuf); } -int vim_iswordc_buf(int c, buf_T *buf) +/// Check that "c" is a keyword character: +/// Letters and characters from 'iskeyword' option for given buffer. +/// For multi-byte characters mb_get_class() is used (builtin rules). +/// +/// @param c character to check +/// @param buf buffer whose keywords to use +bool vim_iswordc_buf(int c, buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2) { if (c >= 0x100) { if (enc_dbcs != 0) { @@ -840,10 +843,11 @@ int vim_iswordc_buf(int c, buf_T *buf) /// Just like vim_iswordc() but uses a pointer to the (multi-byte) character. /// -/// @param p +/// @param p pointer to the multi-byte character /// -/// @return TRUE if 'p' points to a keyword character. -int vim_iswordp(char_u *p) +/// @return true if "p" points to a keyword character. +bool vim_iswordp(char_u *p) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) { return mb_get_class(p) >= 2; @@ -851,7 +855,15 @@ int vim_iswordp(char_u *p) return GET_CHARTAB(curbuf, *p) != 0; } -int vim_iswordp_buf(char_u *p, buf_T *buf) +/// Just like vim_iswordc_buf() but uses a pointer to the (multi-byte) +/// character. +/// +/// @param p pointer to the multi-byte character +/// @param buf buffer whose keywords to use +/// +/// @return true if "p" points to a keyword character. +bool vim_iswordp_buf(char_u *p, buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) { return mb_get_class(p) >= 2; @@ -859,26 +871,24 @@ int vim_iswordp_buf(char_u *p, buf_T *buf) return GET_CHARTAB(buf, *p) != 0; } -/// return TRUE if 'c' is a valid file-name character +/// Check that "c" is a valid file-name character. /// Assume characters above 0x100 are valid (multi-byte). /// -/// @param c -/// -/// @return TRUE if 'c' is a valid file name character. -int vim_isfilec(int c) +/// @param c character to check +bool vim_isfilec(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return c >= 0x100 || (c > 0 && (chartab[c] & CT_FNAME_CHAR)); } -/// return TRUE if 'c' is a valid file-name character or a wildcard character +/// Check that "c" is a valid file-name character or a wildcard character /// Assume characters above 0x100 are valid (multi-byte). /// Explicitly interpret ']' as a wildcard character as path_has_wildcard("]") /// returns false. /// -/// @param c -/// -/// @return TRUE if 'c' is a valid file-name character or wildcard character. -int vim_isfilec_or_wc(int c) +/// @param c character to check +bool vim_isfilec_or_wc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { char_u buf[2]; buf[0] = (char_u)c; @@ -886,14 +896,12 @@ int vim_isfilec_or_wc(int c) return vim_isfilec(c) || c == ']' || path_has_wildcard(buf); } -/// return TRUE if 'c' is a printable character -/// Assume characters above 0x100 are printable (multi-byte), except for -/// Unicode. +/// Check that "c" is a printable character. +/// Assume characters above 0x100 are printable for double-byte encodings. /// -/// @param c -/// -/// @return TRUE if 'c' a printable character. -int vim_isprintc(int c) +/// @param c character to check +bool vim_isprintc(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (enc_utf8 && (c >= 0x100)) { return utf_printable(c); @@ -901,16 +909,17 @@ int vim_isprintc(int c) return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR)); } -/// Strict version of vim_isprintc(c), don't return TRUE if "c" is the head +/// Strict version of vim_isprintc(c), don't return true if "c" is the head /// byte of a double-byte character. /// -/// @param c +/// @param c character to check /// -/// @return TRUE if 'c' is a printable character. -int vim_isprintc_strict(int c) +/// @return true if "c" is a printable character. +bool vim_isprintc_strict(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if ((enc_dbcs != 0) && (c < 0x100) && (MB_BYTE2LEN(c) > 1)) { - return FALSE; + return false; } if (enc_utf8 && (c >= 0x100)) { @@ -921,7 +930,7 @@ int vim_isprintc_strict(int c) /// like chartabsize(), but also check for line breaks on the screen /// -/// @param line +/// @param line /// @param s /// @param col /// @@ -1128,7 +1137,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) int n; if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) { - n = wp->w_buffer->b_p_ts; + n = (int)wp->w_buffer->b_p_ts; return n - (col % n); } n = ptr2cells(s); @@ -1144,35 +1153,33 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) return n; } -/// Return TRUE if virtual column "vcol" is in the rightmost column of window -/// "wp". -/// -/// @param wp -/// @param vcol +/// Check that virtual column "vcol" is in the rightmost column of window "wp". /// -/// @return TRUE if the virtual column is in the rightmost column. -int in_win_border(win_T *wp, colnr_T vcol) +/// @param wp window +/// @param vcol column number +bool in_win_border(win_T *wp, colnr_T vcol) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { int width1; // width of first line (after line number) int width2; // width of further lines if (wp->w_width == 0) { // there is no border - return FALSE; + return false; } width1 = wp->w_width - win_col_off(wp); if ((int)vcol < width1 - 1) { - return FALSE; + return false; } if ((int)vcol == width1 - 1) { - return TRUE; + return true; } width2 = width1 + win_col_off2(wp); if (width2 <= 0) { - return FALSE; + return false; } return (vcol - width1) % width2 == width2 - 1; } @@ -1198,11 +1205,11 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, char_u *line; // start of the line int incr; int head; - int ts = wp->w_buffer->b_p_ts; + int ts = (int)wp->w_buffer->b_p_ts; int c; vcol = 0; - line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); if (pos->col == MAXCOL) { // continue until the NUL @@ -1322,7 +1329,7 @@ colnr_T getvcol_nolist(pos_T *posp) int list_save = curwin->w_p_list; colnr_T vcol; - curwin->w_p_list = FALSE; + curwin->w_p_list = false; getvcol(curwin, posp, NULL, &vcol, NULL); curwin->w_p_list = list_save; return vcol; @@ -1351,7 +1358,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, endadd = 0; // Cannot put the cursor on part of a wide character. - ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); if (pos->col < (colnr_T)STRLEN(ptr)) { int c = (*mb_ptr2char)(ptr + pos->col); @@ -1454,6 +1461,24 @@ char_u* skipdigits(char_u *q) return p; } +/// skip over binary digits +/// +/// @param q pointer to string +/// +/// @return Pointer to the character after the skipped digits. +const char* skipbin(const char *q) + FUNC_ATTR_PURE + FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET +{ + const char *p = q; + while (ascii_isbdigit(*p)) { + // skip to next non-digit + p++; + } + return p; +} + /// skip over digits and hex characters /// /// @param q @@ -1485,6 +1510,24 @@ char_u* skiptodigit(char_u *q) return p; } +/// skip to binary character (or NUL after the string) +/// +/// @param q pointer to string +/// +/// @return Pointer to the binary character or (NUL after the string). +const char* skiptobin(const char *q) + FUNC_ATTR_PURE + FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET +{ + const char *p = q; + while (*p != NUL && !ascii_isbdigit(*p)) { + // skip to next digit + p++; + } + return p; +} + /// skip to hex character (or NUL after the string) /// /// @param q @@ -1535,10 +1578,14 @@ static char_u latin1lower[257] = "\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee" "\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; -int vim_islower(int c) +/// Check that the character is lower-case +/// +/// @param c character to check +bool vim_islower(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (c <= '@') { - return FALSE; + return false; } if (c >= 0x80) { @@ -1548,11 +1595,11 @@ int vim_islower(int c) if (c >= 0x100) { if (has_mbyte) { - return iswlower(c); + return iswlower((wint_t)c); } // islower() can't handle these chars and may crash - return FALSE; + return false; } if (enc_latin1like) { @@ -1562,10 +1609,14 @@ int vim_islower(int c) return islower(c); } -int vim_isupper(int c) +/// Check that the character is upper-case +/// +/// @param c character to check +bool vim_isupper(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (c <= '@') { - return FALSE; + return false; } if (c >= 0x80) { @@ -1575,11 +1626,11 @@ int vim_isupper(int c) if (c >= 0x100) { if (has_mbyte) { - return iswupper(c); + return iswupper((wint_t)c); } - // islower() can't handle these chars and may crash - return FALSE; + // isupper() can't handle these chars and may crash + return false; } if (enc_latin1like) { @@ -1602,7 +1653,7 @@ int vim_toupper(int c) if (c >= 0x100) { if (has_mbyte) { - return towupper(c); + return (int)towupper((wint_t)c); } // toupper() can't handle these chars and may crash @@ -1629,7 +1680,7 @@ int vim_tolower(int c) if (c >= 0x100) { if (has_mbyte) { - return towlower(c); + return (int)towlower((wint_t)c); } // tolower() can't handle these chars and may crash @@ -1708,79 +1759,90 @@ long getdigits_long(char_u **pp) return (long)number; } -/// Return TRUE if "lbuf" is empty or only contains blanks. -/// -/// @param lbuf +/// Check that "lbuf" is empty or only contains blanks. /// -/// @return TRUE if `lbuf` is empty or only contains blanks. -int vim_isblankline(char_u *lbuf) +/// @param lbuf line buffer to check +bool vim_isblankline(char_u *lbuf) { char_u *p = skipwhite(lbuf); return *p == NUL || *p == '\r' || *p == '\n'; } /// Convert a string into a long and/or unsigned long, taking care of -/// hexadecimal and octal numbers. Accepts a '-' sign. -/// If "hexp" is not NULL, returns a flag to indicate the type of the number: +/// hexadecimal, octal and binary numbers. Accepts a '-' sign. +/// If "prep" is not NULL, returns a flag to indicate the type of the number: /// 0 decimal /// '0' octal +/// 'B' bin +/// 'b' bin /// 'X' hex /// 'x' hex /// If "len" is not NULL, the length of the number in characters is returned. /// If "nptr" is not NULL, the signed result is returned in it. /// If "unptr" is not NULL, the unsigned result is returned in it. -/// If "dooct" is non-zero recognize octal numbers, when > 1 always assume -/// octal number. -/// If "dohex" is non-zero recognize hex numbers, when > 1 always assume -/// hex number. +/// If "what" contains STR2NR_BIN recognize binary numbers. +/// If "what" contains STR2NR_OCT recognize octal numbers. +/// If "what" contains STR2NR_HEX recognize hex numbers. +/// If "what" contains STR2NR_FORCE always assume bin/oct/hex. +/// If maxlen > 0, check at a maximum maxlen chars. /// /// @param start -/// @param hexp Returns type of number 0 = decimal, 'x' or 'X' is hex, -// '0' = octal +/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex, +/// '0' = octal, 'b' or 'B' is bin /// @param len Returns the detected length of number. -/// @param dooct recognize octal number -/// @param dohex recognize hex number +/// @param what Recognizes what number passed. /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. -void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex, - long *nptr, unsigned long *unptr) +/// @param maxlen Max length of string to check. +void vim_str2nr(const char_u *const start, int *const prep, int *const len, + const int what, long *const nptr, unsigned long *const unptr, + const int maxlen) { - char_u *ptr = start; - int hex = 0; // default is decimal - int negative = FALSE; + const char_u *ptr = start; + int pre = 0; // default is decimal + bool negative = false; unsigned long un = 0; - int n; if (ptr[0] == '-') { - negative = TRUE; - ++ptr; + negative = true; + ptr++; } - // Recognize hex and octal. - if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) { - hex = ptr[1]; + // Recognize hex, octal and bin. + if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9') + && (maxlen == 0 || maxlen > 1)) { + pre = ptr[1]; - if (dohex - && ((hex == 'X') || (hex == 'x')) - && ascii_isxdigit(ptr[2])) { + if ((what & STR2NR_HEX) + && ((pre == 'X') || (pre == 'x')) + && ascii_isxdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) { // hexadecimal ptr += 2; + } else if ((what & STR2NR_BIN) + && ((pre == 'B') || (pre == 'b')) + && ascii_isbdigit(ptr[2]) + && (maxlen == 0 || maxlen > 2)) { + // binary + ptr += 2; } else { - // default is decimal - hex = 0; + // decimal or octal, default is decimal + pre = 0; - if (dooct) { + if (what & STR2NR_OCT) { // Don't interpret "0", "08" or "0129" as octal. - for (n = 1; ascii_isdigit(ptr[n]); ++n) { + for (int n = 1; ascii_isdigit(ptr[n]); ++n) { if (ptr[n] > '7') { // can't be octal - hex = 0; + pre = 0; break; } - if (ptr[n] >= '0') { // assume octal - hex = '0'; + pre = '0'; + } + if (n == maxlen) { + break; } } } @@ -1788,28 +1850,54 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex, } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - if ((hex == '0') || (dooct > 1)) { + int n = 1; + if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) { + // bin + if (pre != 0) { + n += 2; // skip over "0b" + } + while ('0' <= *ptr && *ptr <= '1') { + un = 2 * un + (unsigned long)(*ptr - '0'); + ptr++; + if (n++ == maxlen) { + break; + } + } + } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) { // octal while ('0' <= *ptr && *ptr <= '7') { un = 8 * un + (unsigned long)(*ptr - '0'); ptr++; + if (n++ == maxlen) { + break; + } } - } else if ((hex != 0) || (dohex > 1)) { + } else if ((pre == 'X') || (pre == 'x') + || what == STR2NR_HEX + STR2NR_FORCE) { // hex + if (pre != 0) { + n += 2; // skip over "0x" + } while (ascii_isxdigit(*ptr)) { un = 16 * un + (unsigned long)hex2nr(*ptr); ptr++; + if (n++ == maxlen) { + break; + } } } else { // decimal while (ascii_isdigit(*ptr)) { un = 10 * un + (unsigned long)(*ptr - '0'); ptr++; + if (n++ == maxlen) { + break; + } } } - if (hexp != NULL) { - *hexp = hex; + if (prep != NULL) { + *prep = pre; } if (len != NULL) { @@ -1848,8 +1936,8 @@ int hex2nr(int c) return c - '0'; } -/// Return true if "str" starts with a backslash that should be removed. -/// For WIN32 this is only done when the character after the +/// Check that "str" starts with a backslash that should be removed. +/// For Windows this is only done when the character after the /// backslash is not a normal file name character. /// '$' is a valid file name character, we don't remove the backslash before /// it. This means it is not possible to use an environment variable after a @@ -1860,10 +1948,9 @@ int hex2nr(int c) /// character, assume that all multi-byte characters are valid file name /// characters. /// -/// @param str -/// -/// @return true if `str` starts with a backslash that should be removed. +/// @param str file path string to check bool rem_backslash(const char_u *str) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { #ifdef BACKSLASH_IN_FILENAME return str[0] == '\\' diff --git a/src/nvim/diff.c b/src/nvim/diff.c index d311588ab4..4826e70727 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -29,7 +29,6 @@ #include "nvim/path.h" #include "nvim/screen.h" #include "nvim/strings.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" @@ -658,9 +657,9 @@ void ex_diffupdate(exarg_T *eap) } // We need three temp file names. - char_u *tmp_orig = vim_tempname(); - char_u *tmp_new = vim_tempname(); - char_u *tmp_diff = vim_tempname(); + char *tmp_orig = (char *) vim_tempname(); + char *tmp_new = (char *) vim_tempname(); + char *tmp_diff = (char *) vim_tempname(); if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) { goto theend; @@ -670,11 +669,11 @@ void ex_diffupdate(exarg_T *eap) // are no differences. Can't use the return value, it's non-zero when // there are differences. // May try twice, first with "-a" and then without. - int io_error = FALSE; - int ok = FALSE; + int io_error = false; + bool ok = false; for (;;) { - ok = FALSE; - FILE *fd = mch_fopen((char *)tmp_orig, "w"); + ok = false; + FILE *fd = mch_fopen(tmp_orig, "w"); if (fd == NULL) { io_error = TRUE; @@ -683,7 +682,7 @@ void ex_diffupdate(exarg_T *eap) io_error = TRUE; } fclose(fd); - fd = mch_fopen((char *)tmp_new, "w"); + fd = mch_fopen(tmp_new, "w"); if (fd == NULL) { io_error = TRUE; @@ -693,7 +692,7 @@ void ex_diffupdate(exarg_T *eap) } fclose(fd); diff_file(tmp_orig, tmp_new, tmp_diff); - fd = mch_fopen((char *)tmp_diff, "r"); + fd = mch_fopen(tmp_diff, "r"); if (fd == NULL) { io_error = TRUE; @@ -712,10 +711,10 @@ void ex_diffupdate(exarg_T *eap) } fclose(fd); } - os_remove((char *)tmp_diff); - os_remove((char *)tmp_new); + os_remove(tmp_diff); + os_remove(tmp_new); } - os_remove((char *)tmp_orig); + os_remove(tmp_orig); } // When using 'diffexpr' break here. @@ -756,28 +755,28 @@ void ex_diffupdate(exarg_T *eap) // Write the first buffer to a tempfile. buf_T *buf = curtab->tp_diffbuf[idx_orig]; - if (diff_write(buf, tmp_orig) == FAIL) { + if (diff_write(buf, (char_u *) tmp_orig) == FAIL) { goto theend; } // Make a difference between the first buffer and every other. for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) { buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf == NULL) { - continue; + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + continue; // skip buffer that isn't loaded } - if (diff_write(buf, tmp_new) == FAIL) { + if (diff_write(buf, (char_u *) tmp_new) == FAIL) { continue; } diff_file(tmp_orig, tmp_new, tmp_diff); // Read the diff output and add each entry to the diff list. - diff_read(idx_orig, idx_new, tmp_diff); - os_remove((char *)tmp_diff); - os_remove((char *)tmp_new); + diff_read(idx_orig, idx_new, (char_u *) tmp_diff); + os_remove(tmp_diff); + os_remove(tmp_new); } - os_remove((char *)tmp_orig); + os_remove(tmp_orig); // force updating cursor position on screen curwin->w_valid_cursor.lnum = 0; @@ -795,15 +794,16 @@ theend: /// @param tmp_orig /// @param tmp_new /// @param tmp_diff -static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff) +static void diff_file(const char *const tmp_orig, const char *const tmp_new, + const char *const tmp_diff) { if (*p_dex != NUL) { // Use 'diffexpr' to generate the diff file. eval_diff(tmp_orig, tmp_new, tmp_diff); } else { - size_t len = STRLEN(tmp_orig) + STRLEN(tmp_new) + STRLEN(tmp_diff) - + STRLEN(p_srr) + 27; - char_u *cmd = xmalloc(len); + const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff) + + STRLEN(p_srr) + 27); + char *const cmd = xmalloc(len); /* We don't want $DIFF_OPTIONS to get in the way. */ if (os_getenv("DIFF_OPTIONS")) { @@ -813,19 +813,17 @@ static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff) /* Build the diff command and execute it. Always use -a, binary * differences are of no use. Ignore errors, diff returns * non-zero when differences have been found. */ - vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s", - diff_a_works == FALSE ? "" : "-a ", + vim_snprintf(cmd, len, "diff %s%s%s%s%s %s", + diff_a_works ? "-a " : "", "", (diff_flags & DIFF_IWHITE) ? "-b " : "", (diff_flags & DIFF_ICASE) ? "-i " : "", tmp_orig, tmp_new); - append_redir(cmd, (int)len, p_srr, tmp_diff); - block_autocmds(); /* Avoid ShellCmdPost stuff */ - (void)call_shell( - cmd, - kShellOptFilter | kShellOptSilent | kShellOptDoOut, - NULL - ); + append_redir(cmd, len, (char *) p_srr, tmp_diff); + block_autocmds(); // Avoid ShellCmdPost stuff + (void)call_shell((char_u *) cmd, + kShellOptFilter | kShellOptSilent | kShellOptDoOut, + NULL); unblock_autocmds(); xfree(cmd); } @@ -902,9 +900,11 @@ void ex_diffpatch(exarg_T *eap) if (*p_pex != NUL) { // Use 'patchexpr' to generate the new file. #ifdef UNIX - eval_patch(tmp_orig, fullname != NULL ? fullname : eap->arg, tmp_new); + eval_patch((char *) tmp_orig, + (char *) (fullname != NULL ? fullname : eap->arg), + (char *) tmp_new); #else - eval_patch(tmp_orig, eap->arg, tmp_new); + eval_patch((char *) tmp_orig, (char *) eap->arg, (char *) tmp_new); #endif // ifdef UNIX } else { // Build the patch command and execute it. Ignore errors. Switch to @@ -1007,6 +1007,7 @@ theend: void ex_diffsplit(exarg_T *eap) { win_T *old_curwin = curwin; + buf_T *old_curbuf = curbuf; // don't use a new tab page, each tab page has its own diffs cmdmod.tab = 0; @@ -1020,8 +1021,19 @@ void ex_diffsplit(exarg_T *eap) // split must have worked if (curwin != old_curwin) { // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); - diff_win_options(old_curwin, TRUE); + diff_win_options(curwin, true); + if (win_valid(old_curwin)) { + diff_win_options(old_curwin, true); + + if (buf_valid(old_curbuf)) { + // Move the cursor position to that of the old window. + curwin->w_cursor.lnum = diff_get_corresponding_line( + old_curbuf, + old_curwin->w_cursor.lnum, + curbuf, + curwin->w_cursor.lnum); + } + } } } } @@ -1045,27 +1057,28 @@ void diff_win_options(win_T *wp, int addbuf) newFoldLevel(); curwin = old_curwin; - wp->w_p_diff = TRUE; - // Use 'scrollbind' and 'cursorbind' when available - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_scb_save = wp->w_p_scb; } wp->w_p_scb = TRUE; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_crb_save = wp->w_p_crb; } wp->w_p_crb = TRUE; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_wrap_save = wp->w_p_wrap; } wp->w_p_wrap = FALSE; curwin = wp; curbuf = curwin->w_buffer; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { + if (wp->w_p_diff_saved) { + free_string_option(wp->w_p_fdm_save); + } wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm); } set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff", @@ -1073,7 +1086,7 @@ void diff_win_options(win_T *wp, int addbuf) curwin = old_curwin; curbuf = curwin->w_buffer; - if (!wp->w_p_diff_saved) { + if (!wp->w_p_diff) { wp->w_p_fdc_save = wp->w_p_fdc; wp->w_p_fen_save = wp->w_p_fen; wp->w_p_fdl_save = wp->w_p_fdl; @@ -1092,6 +1105,8 @@ void diff_win_options(win_T *wp, int addbuf) // Saved the current values, to be restored in ex_diffoff(). wp->w_p_diff_saved = TRUE; + wp->w_p_diff = true; + if (addbuf) { diff_buf_add(wp->w_buffer); } @@ -1104,68 +1119,50 @@ void diff_win_options(win_T *wp, int addbuf) /// @param eap void ex_diffoff(exarg_T *eap) { - win_T *old_curwin = curwin; - int diffwin = FALSE; + int diffwin = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (eap->forceit ? wp->w_p_diff : (wp == curwin)) { - // Set 'diff', 'scrollbind' off and 'wrap' on. If option values - // were saved in diff_win_options() restore them. - wp->w_p_diff = FALSE; - - if (wp->w_p_scb) { - wp->w_p_scb = wp->w_p_diff_saved ? wp->w_p_scb_save : FALSE; - } - - if (wp->w_p_crb) { - wp->w_p_crb = wp->w_p_diff_saved ? wp->w_p_crb_save : FALSE; - } - - if (!wp->w_p_wrap) { - wp->w_p_wrap = wp->w_p_diff_saved ? wp->w_p_wrap_save : TRUE; - } - curwin = wp; - curbuf = curwin->w_buffer; + // Set 'diff' off. If option values were saved in + // diff_win_options(), restore the ones whose settings seem to have + // been left over from diff mode. + wp->w_p_diff = false; if (wp->w_p_diff_saved) { - free_string_option(wp->w_p_fdm); - wp->w_p_fdm = wp->w_p_fdm_save; - wp->w_p_fdm_save = empty_option; - } else { - set_string_option_direct((char_u *)"fdm", -1, - (char_u *)"manual", OPT_LOCAL | OPT_FREE, 0); - } - curwin = old_curwin; - curbuf = curwin->w_buffer; + if (wp->w_p_scb) { + wp->w_p_scb = wp->w_p_scb_save; + } - if (wp->w_p_fdc == diff_foldcolumn) { - wp->w_p_fdc = wp->w_p_diff_saved ? wp->w_p_fdc_save : 0; - } + if (wp->w_p_crb) { + wp->w_p_crb = wp->w_p_crb_save; + } - if ((wp->w_p_fdl == 0) - && wp->w_p_diff_saved) { - wp->w_p_fdl = wp->w_p_fdl_save; - } + if (!wp->w_p_wrap) { + wp->w_p_wrap = wp->w_p_wrap_save; + } - if (wp->w_p_fen) { + free_string_option(wp->w_p_fdm); + wp->w_p_fdm = vim_strsave(wp->w_p_fdm_save); + if (wp->w_p_fdc == diff_foldcolumn) { + wp->w_p_fdc = wp->w_p_fdc_save; + } + if (wp->w_p_fdl == 0) { + wp->w_p_fdl = wp->w_p_fdl_save; + } // Only restore 'foldenable' when 'foldmethod' is not // "manual", otherwise we continue to show the diff folds. - if (foldmethodIsManual(wp) || !wp->w_p_diff_saved) { - wp->w_p_fen = FALSE; - } else { - wp->w_p_fen = wp->w_p_fen_save; + if (wp->w_p_fen) { + wp->w_p_fen = foldmethodIsManual(wp) ? false : wp->w_p_fen_save; } - } - foldUpdateAll(wp); + foldUpdateAll(wp); - // make sure topline is not halfway through a fold - changed_window_setting_win(wp); + // make sure topline is not halfway through a fold + changed_window_setting_win(wp); + } // Note: 'sbo' is not restored, it's a global option. diff_buf_adjust(wp); - - wp->w_p_diff_saved = FALSE; } diffwin |= wp->w_p_diff; } @@ -1534,37 +1531,37 @@ int diff_check(win_T *wp, linenr_T lnum) return maxcount - dp->df_count[idx]; } -/// Compare two entries in diff "*dp" and return TRUE if they are equal. +/// Compare two entries in diff "dp" and return true if they are equal. /// -/// @param dp -/// @param idx1 First entry in diff "*dp" -/// @param idx2 Second entry in diff "*dp" +/// @param dp diff +/// @param idx1 first entry in diff "dp" +/// @param idx2 second entry in diff "dp" /// -/// @return return TRUE if two entires are equal. -static int diff_equal_entry(diff_T *dp, int idx1, int idx2) +/// @return true if two entires are equal. +static bool diff_equal_entry(diff_T *dp, int idx1, int idx2) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { if (dp->df_count[idx1] != dp->df_count[idx2]) { - return FALSE; + return false; } if (diff_check_sanity(curtab, dp) == FAIL) { - return FALSE; + return false; } - int i; - for (i = 0; i < dp->df_count[idx1]; ++i) { + for (int i = 0; i < dp->df_count[idx1]; i++) { char_u *line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1], - dp->df_lnum[idx1] + i, FALSE)); + dp->df_lnum[idx1] + i, false)); int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2], - dp->df_lnum[idx2] + i, FALSE)); + dp->df_lnum[idx2] + i, false)); xfree(line); if (cmp != 0) { - return FALSE; + return false; } } - return TRUE; + return true; } /// Compare strings "s1" and "s2" according to 'diffopt'. @@ -1833,28 +1830,30 @@ int diffopt_changed(void) return OK; } -/// Return TRUE if 'diffopt' contains "horizontal". -/// -/// @return TRUE if 'diffopt' contains "horizontal" -int diffopt_horizontal(void) +/// Check that "diffopt" contains "horizontal". +bool diffopt_horizontal(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return (diff_flags & DIFF_HORIZONTAL) != 0; } /// Find the difference within a changed line. /// -/// @param startp first char of the change -/// @param endp last char of the change +/// @param wp window whose current buffer to check +/// @param lnum line number to check within the buffer +/// @param startp first char of the change +/// @param endp last char of the change /// -/// @returns TRUE if the line was added, no other buffer has it. -int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) +/// @return true if the line was added, no other buffer has it. +bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { char_u *line_new; int si_org; int si_new; int ei_org; int ei_new; - int added = TRUE; + bool added = true; // Make a copy of the line, the next ml_get() will invalidate it. char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); @@ -1863,7 +1862,7 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) if (idx == DB_COUNT) { // cannot happen xfree(line_org); - return FALSE; + return false; } // search for a change that includes "lnum" in the list of diffblocks. @@ -1876,7 +1875,7 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) { xfree(line_org); - return FALSE; + return false; } int off = lnum - dp->df_lnum[idx]; @@ -1887,7 +1886,7 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) if (off >= dp->df_count[i]) { continue; } - added = FALSE; + added = false; line_new = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off, FALSE); @@ -1959,21 +1958,22 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) return added; } -/// Return TRUE if line "lnum" is not close to a diff block, this line should +/// Check that line "lnum" is not close to a diff block, this line should /// be in a fold. /// -/// @param wp -/// @param lnum +/// @param wp window containing the buffer to check +/// @param lnum line number to check within the buffer /// -/// @return FALSE if there are no diff blocks at all in this window. -int diff_infold(win_T *wp, linenr_T lnum) +/// @return false if there are no diff blocks at all in this window. +bool diff_infold(win_T *wp, linenr_T lnum) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { - int other = FALSE; + bool other = false; diff_T *dp; // Return if 'diff' isn't set. if (!wp->w_p_diff) { - return FALSE; + return false; } int idx = -1; @@ -1982,13 +1982,13 @@ int diff_infold(win_T *wp, linenr_T lnum) if (curtab->tp_diffbuf[i] == wp->w_buffer) { idx = i; } else if (curtab->tp_diffbuf[i] != NULL) { - other = TRUE; + other = true; } } // return here if there are no diffs in the window if ((idx == -1) || !other) { - return FALSE; + return false; } if (curtab->tp_diff_invalid) { @@ -1998,7 +1998,7 @@ int diff_infold(win_T *wp, linenr_T lnum) // Return if there are no diff blocks. All lines will be folded. if (curtab->tp_first_diff == NULL) { - return TRUE; + return true; } for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { @@ -2009,10 +2009,10 @@ int diff_infold(win_T *wp, linenr_T lnum) // If this change ends before the line we have a match. if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) { - return FALSE; + return false; } } - return TRUE; + return true; } /// "dp" and "do" commands. @@ -2375,12 +2375,11 @@ static void diff_fold_update(diff_T *dp, int skip_idx) } } -/// Checks if the buffer is in diff-mode. -/// -/// @param buf The buffer to check. +/// Checks that the buffer is in diff-mode. /// -/// @return TRUE if buffer "buf" is in diff-mode. +/// @param buf buffer to check. bool diff_mode_buf(buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { FOR_ALL_TABS(tp) { if (diff_buf_idx_tp(buf, tp) != DB_COUNT) { diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 8dc2844d8e..e131da8fe0 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -165,10 +165,6 @@ static int compl_restarting = FALSE; /* don't insert match */ * FALSE the word to be completed must be located. */ static int compl_started = FALSE; -/* Set when doing something for completion that may call edit() recursively, - * which is not allowed. */ -static int compl_busy = FALSE; - static int compl_matches = 0; static char_u *compl_pattern = NULL; static int compl_direction = FORWARD; @@ -203,7 +199,7 @@ typedef struct insert_state { int did_restart_edit; // remember if insert mode was restarted // after a ctrl+o bool nomove; - uint8_t *ptr; + char_u *ptr; } InsertState; @@ -274,8 +270,8 @@ static void insert_enter(InsertState *s) s->ptr = (char_u *)"i"; } - set_vim_var_string(VV_INSERTMODE, s->ptr, 1); - set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */ + set_vim_var_string(VV_INSERTMODE, (char *) s->ptr, 1); + set_vim_var_string(VV_CHAR, NULL, -1); apply_autocmds(EVENT_INSERTENTER, NULL, NULL, false, curbuf); // Make sure the cursor didn't move. Do call check_cursor_col() in @@ -887,7 +883,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_T: // Make indent one shiftwidth greater. if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { - if (has_compl_option(false)) { + if (check_compl_option(false)) { insert_do_complete(s); } break; @@ -1102,7 +1098,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_K: // digraph or keyword completion if (ctrl_x_mode == CTRL_X_DICTIONARY) { - if (has_compl_option(true)) { + if (check_compl_option(true)) { insert_do_complete(s); } break; @@ -1247,7 +1243,7 @@ normalchar: static void insert_do_complete(InsertState *s) { compl_busy = true; - if (ins_complete(s->c) == FAIL) { + if (ins_complete(s->c, true) == FAIL) { compl_cont_status = 0; } compl_busy = false; @@ -1264,30 +1260,28 @@ static void insert_do_cindent(InsertState *s) } } -/* - * edit(): Start inserting text. - * - * "cmdchar" can be: - * 'i' normal insert command - * 'a' normal append command - * 'R' replace command - * 'r' "r<CR>" command: insert one <CR>. Note: count can be > 1, for redo, - * but still only one <CR> is inserted. The <Esc> is not used for redo. - * 'g' "gI" command. - * 'V' "gR" command for Virtual Replace mode. - * 'v' "gr" command for single character Virtual Replace mode. - * - * This function is not called recursively. For CTRL-O commands, it returns - * and lets the caller handle the Normal-mode command. - * - * Return TRUE if a CTRL-O command caused the return (insert mode pending). - */ -int -edit ( - int cmdchar, - int startln, /* if set, insert at start of line */ - long count -) +/// edit(): Start inserting text. +/// +/// "cmdchar" can be: +/// 'i' normal insert command +/// 'a' normal append command +/// 'R' replace command +/// 'r' "r<CR>" command: insert one <CR>. +/// Note: count can be > 1, for redo, but still only one <CR> is inserted. +/// <Esc> is not used for redo. +/// 'g' "gI" command. +/// 'V' "gR" command for Virtual Replace mode. +/// 'v' "gr" command for single character Virtual Replace mode. +/// +/// This function is not called recursively. For CTRL-O commands, it returns +/// and lets the caller handle the Normal-mode command. +/// +/// @param cmdchar command that started the insert +/// @param startln if true, insert at start of line +/// @param count repeat count for the command +/// +/// @return true if a CTRL-O command caused the return (insert mode pending). +bool edit(int cmdchar, bool startln, long count) { if (curbuf->terminal) { if (ex_normal_busy) { @@ -1352,24 +1346,24 @@ ins_redraw ( if (char_avail()) return; - /* Trigger CursorMoved if the cursor moved. Not when the popup menu is - * visible, the command might delete it. */ - if (ready && ( - has_cursormovedI() - || - curwin->w_p_cole > 0 - ) + // Trigger CursorMoved if the cursor moved. Not when the popup menu is + // visible, the command might delete it. + if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0) && !equalpos(last_cursormoved, curwin->w_cursor) - && !pum_visible() - ) { - /* Need to update the screen first, to make sure syntax - * highlighting is correct after making a change (e.g., inserting - * a "(". The autocommand may also require a redraw, so it's done - * again below, unfortunately. */ - if (syntax_present(curwin) && must_redraw) + && !pum_visible()) { + // Need to update the screen first, to make sure syntax + // highlighting is correct after making a change (e.g., inserting + // a "(". The autocommand may also require a redraw, so it's done + // again below, unfortunately. + if (syntax_present(curwin) && must_redraw) { update_screen(0); - if (has_cursormovedI()) - apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, FALSE, curbuf); + } + if (has_event(EVENT_CURSORMOVEDI)) { + // Make sure curswant is correct, an autocommand may call + // getcurpos() + update_curswant(); + apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, false, curbuf); + } if (curwin->w_p_cole > 0) { conceal_old_cursor_line = last_cursormoved.lnum; conceal_new_cursor_line = curwin->w_cursor.lnum; @@ -1378,13 +1372,13 @@ ins_redraw ( last_cursormoved = curwin->w_cursor; } - /* Trigger TextChangedI if b_changedtick differs. */ - if (ready && has_textchangedI() + // Trigger TextChangedI if b_changedtick differs. + if (ready && has_event(EVENT_TEXTCHANGEDI) && last_changedtick != curbuf->b_changedtick - && !pum_visible() - ) { - if (last_changedtick_buf == curbuf) - apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, FALSE, curbuf); + && !pum_visible()) { + if (last_changedtick_buf == curbuf) { + apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf); + } last_changedtick_buf = curbuf; last_changedtick = curbuf->b_changedtick; } @@ -1798,33 +1792,37 @@ void backspace_until_column(int col) } } -/* - * Like del_char(), but make sure not to go before column "limit_col". - * Only matters when there are composing characters. - * Return TRUE when something was deleted. - */ -static int del_char_after_col(int limit_col) +/// Like del_char(), but make sure not to go before column "limit_col". +/// Only matters when there are composing characters. +/// +/// @param limit_col only delete the character if it is after this column +// +/// @return true when something was deleted. +static bool del_char_after_col(int limit_col) { if (enc_utf8 && limit_col >= 0) { colnr_T ecol = curwin->w_cursor.col + 1; - /* Make sure the cursor is at the start of a character, but - * skip forward again when going too far back because of a - * composing character. */ + // Make sure the cursor is at the start of a character, but + // skip forward again when going too far back because of a + // composing character. mb_adjust_cursor(); while (curwin->w_cursor.col < (colnr_T)limit_col) { int l = utf_ptr2len(get_cursor_pos_ptr()); - if (l == 0) /* end of line */ + if (l == 0) { // end of line break; + } curwin->w_cursor.col += l; } - if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) - return FALSE; - del_bytes((long)((int)ecol - curwin->w_cursor.col), FALSE, TRUE); - } else - (void)del_char(FALSE); - return TRUE; + if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) { + return false; + } + del_bytes(ecol - curwin->w_cursor.col, false, true); + } else { + del_char(false); + } + return true; } /* @@ -1849,47 +1847,48 @@ static void ins_ctrl_x(void) } } -/* - * Return TRUE if the 'dict' or 'tsr' option can be used. - */ -static int has_compl_option(int dict_opt) +/// Check that the "dict" or "tsr" option can be used. +/// +/// @param dict_opt check "dict" when true, "tsr" when false. +static bool check_compl_option(bool dict_opt) { - if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL - && !curwin->w_p_spell - ) + if (dict_opt + ? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell) : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) { ctrl_x_mode = 0; edit_submode = NULL; msg_attr(dict_opt ? (char_u *)_("'dictionary' option is empty") - : (char_u *)_("'thesaurus' option is empty"), - hl_attr(HLF_E)); + : (char_u *)_("'thesaurus' option is empty"), hl_attr(HLF_E)); if (emsg_silent == 0) { vim_beep(BO_COMPL); setcursor(); ui_flush(); os_delay(2000L, false); } - return FALSE; + return false; } - return TRUE; + return true; } -/* - * Is the character 'c' a valid key to go to or keep us in CTRL-X mode? - * This depends on the current mode. - */ -int vim_is_ctrl_x_key(int c) +/// Check that the character "c" a valid key to go to or keep us in CTRL-X mode? +/// This depends on the current mode. +/// +/// @param c character to check +bool vim_is_ctrl_x_key(int c) + FUNC_ATTR_WARN_UNUSED_RESULT { - /* Always allow ^R - let it's results then be checked */ - if (c == Ctrl_R) - return TRUE; + // Always allow ^R - let its results then be checked + if (c == Ctrl_R) { + return true; + } - /* Accept <PageUp> and <PageDown> if the popup menu is visible. */ - if (ins_compl_pum_key(c)) - return TRUE; + // Accept <PageUp> and <PageDown> if the popup menu is visible. + if (ins_compl_pum_key(c)) { + return true; + } switch (ctrl_x_mode) { - case 0: /* Not in any CTRL-X mode */ + case 0: // Not in any CTRL-X mode return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X; case CTRL_X_NOT_DEFINED_YET: return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E @@ -1927,35 +1926,37 @@ int vim_is_ctrl_x_key(int c) return (c == Ctrl_P || c == Ctrl_N); } EMSG(_(e_internal)); - return FALSE; + return false; } -/* - * Return TRUE when character "c" is part of the item currently being - * completed. Used to decide whether to abandon complete mode when the menu - * is visible. - */ -static int ins_compl_accept_char(int c) +/// Check that character "c" is part of the item currently being +/// completed. Used to decide whether to abandon complete mode when the menu +/// is visible. +/// +/// @param c character to check +static bool ins_compl_accept_char(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - if (ctrl_x_mode & CTRL_X_WANT_IDENT) - /* When expanding an identifier only accept identifier chars. */ + if (ctrl_x_mode & CTRL_X_WANT_IDENT) { + // When expanding an identifier only accept identifier chars. return vim_isIDc(c); + } switch (ctrl_x_mode) { case CTRL_X_FILES: - /* When expanding file name only accept file name chars. But not - * path separators, so that "proto/<Tab>" expands files in - * "proto", not "proto/" as a whole */ + // When expanding file name only accept file name chars. But not + // path separators, so that "proto/<Tab>" expands files in + // "proto", not "proto/" as a whole return vim_isfilec(c) && !vim_ispathsep(c); case CTRL_X_CMDLINE: case CTRL_X_OMNI: - /* Command line and Omni completion can work with just about any - * printable character, but do stop at white space. */ + // Command line and Omni completion can work with just about any + // printable character, but do stop at white space. return vim_isprintc(c) && !ascii_iswhite(c); case CTRL_X_WHOLE_LINE: - /* For while line completion a space can be part of the line. */ + // For while line completion a space can be part of the line. return vim_isprintc(c); } return vim_iswordc(c); @@ -2200,15 +2201,19 @@ ins_compl_add ( return OK; } -/* - * Return TRUE if "str[len]" matches with match->cp_str, considering - * match->cp_icase. - */ -static int ins_compl_equal(compl_T *match, char_u *str, int len) +/// Check that "str[len]" matches with "match->cp_str", considering +/// "match->cp_icase". +/// +/// @param match completion match +/// @param str character string to check +/// @param len lenth of "str" +static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (match->cp_icase) - return STRNICMP(match->cp_str, str, (size_t)len) == 0; - return STRNCMP(match->cp_str, str, (size_t)len) == 0; + if (match->cp_icase) { + return STRNICMP(match->cp_str, str, len) == 0; + } + return STRNCMP(match->cp_str, str, len) == 0; } /* @@ -2317,6 +2322,22 @@ static int ins_compl_make_cyclic(void) return count; } + +// Set variables that store noselect and noinsert behavior from the +// 'completeopt' value. +void completeopt_was_set(void) +{ + compl_no_insert = false; + compl_no_select = false; + if (strstr((char *)p_cot, "noselect") != NULL) { + compl_no_select = true; + } + if (strstr((char *)p_cot, "noinsert") != NULL) { + compl_no_insert = true; + } +} + + /* * Start completion for the complete() function. * "startcol" is where the matched text starts (1 is first column). @@ -2324,9 +2345,10 @@ static int ins_compl_make_cyclic(void) */ void set_completion(colnr_T startcol, list_T *list) { - /* If already doing completions stop it. */ - if (ctrl_x_mode != 0) + // If already doing completions stop it. + if (ctrl_x_mode != 0) { ins_compl_prep(' '); + } ins_compl_clear(); if (stop_arrow() == FAIL) @@ -2352,16 +2374,23 @@ void set_completion(colnr_T startcol, list_T *list) compl_started = TRUE; compl_used_match = TRUE; compl_cont_status = 0; + int save_w_wrow = curwin->w_wrow; compl_curr_match = compl_first_match; - if (compl_no_insert) { - ins_complete(K_DOWN); - } else { - ins_complete(Ctrl_N); + if (compl_no_insert || compl_no_select) { + ins_complete(K_DOWN, false); if (compl_no_select) { - ins_complete(Ctrl_P); + ins_complete(K_UP, false); } + } else { + ins_complete(Ctrl_N, false); + } + + // Lazily show the popup menu, unless we got interrupted. + if (!compl_interrupted) { + show_pum(save_w_wrow); } + ui_flush(); } @@ -2398,44 +2427,33 @@ static void ins_compl_del_pum(void) } } -/* - * Return TRUE if the popup menu should be displayed. - */ -static int pum_wanted(void) +/// Check if the popup menu should be displayed. +static bool pum_wanted(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* 'completeopt' must contain "menu" or "menuone" */ - if (vim_strchr(p_cot, 'm') == NULL) - return FALSE; - - /* The display looks bad on a B&W display. */ - if (t_colors < 8 - ) - return FALSE; - return TRUE; + // "completeopt" must contain "menu" or "menuone" + return vim_strchr(p_cot, 'm') != NULL; } -/* - * Return TRUE if there are two or more matches to be shown in the popup menu. - * One if 'completopt' contains "menuone". - */ -static int pum_enough_matches(void) +/// Check that there are two or more matches to be shown in the popup menu. +/// One if "completopt" contains "menuone". +static bool pum_enough_matches(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - compl_T *compl; - int i; - - /* Don't display the popup menu if there are no matches or there is only - * one (ignoring the original text). */ - compl = compl_first_match; - i = 0; + // Don't display the popup menu if there are no matches or there is only + // one (ignoring the original text). + compl_T *comp = compl_first_match; + int i = 0; do { - if (compl == NULL - || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) + if (comp == NULL || ((comp->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) { break; - compl = compl->cp_next; - } while (compl != compl_first_match); + } + comp = comp->cp_next; + } while (comp != compl_first_match); - if (strstr((char *)p_cot, "menuone") != NULL) + if (strstr((char *)p_cot, "menuone") != NULL) { return i >= 1; + } return i >= 2; } @@ -2467,6 +2485,14 @@ void ins_compl_show_pum(void) /* Need to build the popup menu list. */ compl_match_arraysize = 0; compl = compl_first_match; + /* + * If it's user complete function and refresh_always, + * not use "compl_leader" as prefix filter. + */ + if (ins_compl_need_restart()){ + xfree(compl_leader); + compl_leader = NULL; + } if (compl_leader != NULL) lead_len = (int)STRLEN(compl_leader); do { @@ -2551,8 +2577,12 @@ void ins_compl_show_pum(void) } } - /* Compute the screen column of the start of the completed text. - * Use the cursor to get all wrapping and other settings right. */ + // In Replace mode when a $ is displayed at the end of the line only + // part of the screen would be updated. We do need to redraw here. + dollar_vcol = -1; + + // Compute the screen column of the start of the completed text. + // Use the cursor to get all wrapping and other settings right. col = curwin->w_cursor.col; curwin->w_cursor.col = compl_col; pum_display(compl_match_array, compl_match_arraysize, cur); @@ -2854,10 +2884,9 @@ static void ins_compl_clear(void) set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); } -/* - * Return TRUE when Insert completion is active. - */ -int ins_compl_active(void) +/// Check that Insert completion is active. +bool ins_compl_active(void) + FUNC_ATTR_PURE { return compl_started; } @@ -2901,14 +2930,13 @@ static int ins_compl_bs(void) return NUL; } -/* - * Return TRUE when we need to find matches again, ins_compl_restart() is to - * be called. - */ -static int ins_compl_need_restart(void) +/// Check that we need to find matches again, ins_compl_restart() is to +/// be called. +static bool ins_compl_need_restart(void) + FUNC_ATTR_PURE { - /* Return TRUE if we didn't complete finding matches or when the - * 'completefunc' returned "always" in the "refresh" dictionary item. */ + // Return true if we didn't complete finding matches or when the + // "completefunc" returned "always" in the "refresh" dictionary item. return compl_was_interrupted || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) && compl_opt_refresh_always); @@ -2926,20 +2954,17 @@ static void ins_compl_new_leader(void) ins_bytes(compl_leader + ins_compl_len()); compl_used_match = FALSE; - if (compl_started) + if (compl_started) { ins_compl_set_original_text(compl_leader); - else { - spell_bad_len = 0; /* need to redetect bad word */ - /* - * Matches were cleared, need to search for them now. First display - * the changed text before the cursor. Set "compl_restarting" to - * avoid that the first match is inserted. - */ - update_screen(0); - compl_restarting = TRUE; - if (ins_complete(Ctrl_N) == FAIL) + } else { + spell_bad_len = 0; // need to redetect bad word + // Matches were cleared, need to search for them now. + // Set "compl_restarting" to avoid that the first match is inserted. + compl_restarting = true; + if (ins_complete(Ctrl_N, true) == FAIL) { compl_cont_status = 0; - compl_restarting = FALSE; + } + compl_restarting = false; } compl_enter_selects = !compl_used_match; @@ -2947,8 +2972,9 @@ static void ins_compl_new_leader(void) /* Show the popup menu with a different set of matches. */ ins_compl_show_pum(); - /* Don't let Enter select the original text when there is no popup menu. */ - if (compl_match_array == NULL) + /* Don't let Enter select the original text when there is no popup menu. + * Don't let Enter select when use user function and refresh_always is set */ + if (compl_match_array == NULL || ins_compl_need_restart()) compl_enter_selects = FALSE; } @@ -2979,27 +3005,18 @@ static void ins_compl_addleader(int c) (*mb_char2bytes)(c, buf); buf[cc] = NUL; ins_char_bytes(buf, cc); - if (compl_opt_refresh_always) - AppendToRedobuff(buf); } else { ins_char(c); - if (compl_opt_refresh_always) - AppendCharToRedobuff(c); } /* If we didn't complete finding matches we must search again. */ if (ins_compl_need_restart()) ins_compl_restart(); - /* When 'always' is set, don't reset compl_leader. While completing, - * cursor doesn't point original position, changing compl_leader would - * break redo. */ - if (!compl_opt_refresh_always) { - xfree(compl_leader); - compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, - (int)(curwin->w_cursor.col - compl_col)); - ins_compl_new_leader(); - } + xfree(compl_leader); + compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, + (int)(curwin->w_cursor.col - compl_col)); + ins_compl_new_leader(); } /* @@ -3008,6 +3025,10 @@ static void ins_compl_addleader(int c) */ static void ins_compl_restart(void) { + /* update screen before restart. + * so if complete is blocked, + * will stay to the last popup menu and reduce flicker */ + update_screen(0); ins_compl_free(); compl_started = FALSE; compl_matches = 0; @@ -3063,16 +3084,16 @@ static void ins_compl_addfrommatch(void) ins_compl_addleader(c); } -/* - * Prepare for Insert mode completion, or stop it. - * Called just after typing a character in Insert mode. - * Returns TRUE when the character is not to be inserted; - */ -static int ins_compl_prep(int c) +/// Prepare for Insert mode completion, or stop it. +/// Called just after typing a character in Insert mode. +/// +/// @param c character that was typed +/// +/// @return true when the character is not to be inserted; +static bool ins_compl_prep(int c) { - char_u *ptr; - int want_cindent; - int retval = FALSE; + char_u *ptr; + bool retval = false; /* Forget any previous 'special' messages if this is actually * a ^X mode key - bar ^R, in which case we wait to see what it gives us. @@ -3082,8 +3103,10 @@ static int ins_compl_prep(int c) /* Ignore end of Select mode mapping and mouse scroll buttons. */ if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSELEFT || c == K_MOUSERIGHT) + || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT + || c == K_FOCUSGAINED || c == K_FOCUSLOST) { return retval; + } /* Set "compl_get_longest" when finding the first matches. */ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET @@ -3093,17 +3116,6 @@ static int ins_compl_prep(int c) } - if (strstr((char *)p_cot, "noselect") != NULL) { - compl_no_insert = FALSE; - compl_no_select = TRUE; - } else if (strstr((char *)p_cot, "noinsert") != NULL) { - compl_no_insert = TRUE; - compl_no_select = FALSE; - } else { - compl_no_insert = FALSE; - compl_no_select = FALSE; - } - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) { /* * We have just typed CTRL-X and aren't quite sure which CTRL-X mode @@ -3237,11 +3249,9 @@ static int ins_compl_prep(int c) ins_compl_fixRedoBufForLeader(ptr); } - want_cindent = (can_cindent && cindent_on()); - /* - * When completing whole lines: fix indent for 'cindent'. - * Otherwise, break line if it's too long. - */ + bool want_cindent = (can_cindent && cindent_on()); + // When completing whole lines: fix indent for 'cindent'. + // Otherwise, break line if it's too long. if (compl_cont_mode == CTRL_X_WHOLE_LINE) { /* re-indent the current line */ if (want_cindent) { @@ -3261,22 +3271,24 @@ static int ins_compl_prep(int c) inc_cursor(); } - /* If the popup menu is displayed pressing CTRL-Y means accepting - * the selection without inserting anything. When - * compl_enter_selects is set the Enter key does the same. */ + // If the popup menu is displayed pressing CTRL-Y means accepting + // the selection without inserting anything. When + // compl_enter_selects is set the Enter key does the same. if ((c == Ctrl_Y || (compl_enter_selects && (c == CAR || c == K_KENTER || c == NL))) - && pum_visible()) - retval = TRUE; + && pum_visible()) { + retval = true; + } /* CTRL-E means completion is Ended, go back to the typed text. */ if (c == Ctrl_E) { ins_compl_delete(); - if (compl_leader != NULL) + if (compl_leader != NULL) { ins_bytes(compl_leader + ins_compl_len()); - else if (compl_first_match != NULL) + } else if (compl_first_match != NULL) { ins_bytes(compl_orig_text + ins_compl_len()); - retval = TRUE; + } + retval = true; } auto_format(FALSE, TRUE); @@ -3294,6 +3306,12 @@ static int ins_compl_prep(int c) showmode(); } + // Avoid the popup menu remains displayed when leaving the + // command line window. + if (c == Ctrl_C && cmdwin_type != 0) { + update_screen(0); + } + /* * Indent now if a key was typed that is in 'cinkeys'. */ @@ -4235,22 +4253,22 @@ void ins_compl_check_keys(int frequency) static int ins_compl_key2dir(int c) { if (c == Ctrl_P || c == Ctrl_L - || (pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP - || c == K_S_UP || c == K_UP))) + || c == K_PAGEUP || c == K_KPAGEUP + || c == K_S_UP || c == K_UP) { return BACKWARD; + } return FORWARD; } -/* - * Return TRUE for keys that are used for completion only when the popup menu - * is visible. - */ -static int ins_compl_pum_key(int c) +/// Check that "c" is a valid completion key only while the popup menu is shown +/// +/// @param c character to check +static bool ins_compl_pum_key(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP - || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == - K_S_DOWN - || c == K_UP || c == K_DOWN); + || c == K_PAGEDOWN || c == K_KPAGEDOWN + || c == K_S_DOWN || c == K_UP || c == K_DOWN); } /* @@ -4270,11 +4288,12 @@ static int ins_compl_key2count(int c) return 1; } -/* - * Return TRUE if completion with "c" should insert the match, FALSE if only - * to change the currently selected completion. - */ -static int ins_compl_use_match(int c) +/// Check that completion with "c" should insert the match, false if only +/// to change the currently selected completion. +/// +/// @param c character to check +static bool ins_compl_use_match(int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { switch (c) { case K_UP: @@ -4285,9 +4304,9 @@ static int ins_compl_use_match(int c) case K_PAGEUP: case K_KPAGEUP: case K_S_UP: - return FALSE; + return false; } - return TRUE; + return true; } /* @@ -4295,7 +4314,7 @@ static int ins_compl_use_match(int c) * Called when character "c" was typed, which has a meaning for completion. * Returns OK if completion was done, FAIL if something failed. */ -static int ins_complete(int c) +static int ins_complete(int c, bool enable_pum) { char_u *line; int startcol = 0; /* column where searched text starts */ @@ -4411,11 +4430,10 @@ static int ins_complete(int c) prefix = (char_u *)""; STRCPY((char *)compl_pattern, prefix); (void)quote_meta(compl_pattern + STRLEN(prefix), - line + compl_col, compl_length); - } else if (--startcol < 0 || - !vim_iswordp(mb_prevptr(line, line + startcol + 1)) - ) { - /* Match any word of at least two chars */ + line + compl_col, compl_length); + } else if (--startcol < 0 + || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { + // Match any word of at least two chars compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); compl_col += curs_col; compl_length = 0; @@ -4778,20 +4796,9 @@ static int ins_complete(int c) } } - /* Show the popup menu, unless we got interrupted. */ - if (!compl_interrupted) { - /* RedrawingDisabled may be set when invoked through complete(). */ - n = RedrawingDisabled; - RedrawingDisabled = 0; - - /* If the cursor moved we need to remove the pum first. */ - setcursor(); - if (save_w_wrow != curwin->w_wrow) - ins_compl_del_pum(); - - ins_compl_show_pum(); - setcursor(); - RedrawingDisabled = n; + // Show the popup menu, unless we got interrupted. + if (enable_pum && !compl_interrupted) { + show_pum(save_w_wrow); } compl_was_interrupted = compl_interrupted; compl_interrupted = FALSE; @@ -4944,15 +4951,10 @@ int get_literal(void) return cc; } -/* - * Insert character, taking care of special keys and mod_mask - */ -static void -insert_special ( - int c, - int allow_modmask, - int ctrlv /* c was typed after CTRL-V */ -) +/// Insert character, taking care of special keys and mod_mask +/// +/// @param ctrlv `c` was typed after CTRL-V +static void insert_special(int c, int allow_modmask, int ctrlv) { char_u *p; int len; @@ -4964,6 +4966,9 @@ insert_special ( * Only use mod_mask for special keys, to avoid things like <S-Space>, * unless 'allow_modmask' is TRUE. */ + if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key. + allow_modmask = true; + } if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) { p = get_special_key_name(c, mod_mask); len = (int)STRLEN(p); @@ -5017,8 +5022,9 @@ insertchar ( int textwidth; char_u *p; int fo_ins_blank; + int force_format = flags & INSCHAR_FORMAT; - textwidth = comp_textwidth(flags & INSCHAR_FORMAT); + textwidth = comp_textwidth(force_format); fo_ins_blank = has_format_option(FO_INS_BLANK); /* @@ -5037,7 +5043,7 @@ insertchar ( * before 'textwidth' */ if (textwidth > 0 - && ((flags & INSCHAR_FORMAT) + && (force_format || (!ascii_iswhite(c) && !((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG) @@ -5051,8 +5057,11 @@ insertchar ( /* Format with 'formatexpr' when it's set. Use internal formatting * when 'formatexpr' isn't set or it returns non-zero. */ int do_internal = TRUE; + colnr_T virtcol = get_nolist_virtcol() + + char2cells(c != NUL ? c : gchar_cursor()); - if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0) { + if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0 + && (force_format || virtcol > (colnr_T)textwidth)) { do_internal = (fex_format(curwin->w_cursor.lnum, 1L, c) != 0); /* It may be required to save for undo again, e.g. when setline() * was called. */ @@ -5120,24 +5129,20 @@ insertchar ( can_si = FALSE; can_si_back = FALSE; - /* - * If there's any pending input, grab up to INPUT_BUFLEN at once. - * This speeds up normal text input considerably. - * Don't do this when 'cindent' or 'indentexpr' is set, because we might - * need to re-indent at a ':', or any other character (but not what - * 'paste' is set).. - * Don't do this when there an InsertCharPre autocommand is defined, - * because we need to fire the event for every character. - */ - - if ( !ISSPECIAL(c) - && (!has_mbyte || (*mb_char2len)(c) == 1) - && vpeekc() != NUL - && !(State & REPLACE_FLAG) - && !cindent_on() - && !p_ri - && !has_insertcharpre() - ) { + // If there's any pending input, grab up to INPUT_BUFLEN at once. + // This speeds up normal text input considerably. + // Don't do this when 'cindent' or 'indentexpr' is set, because we might + // need to re-indent at a ':', or any other character (but not what + // 'paste' is set).. + // Don't do this when there an InsertCharPre autocommand is defined, + // because we need to fire the event for every character. + if (!ISSPECIAL(c) + && (!has_mbyte || (*mb_char2len)(c) == 1) + && vpeekc() != NUL + && !(State & REPLACE_FLAG) + && !cindent_on() + && !p_ri + && !has_event(EVENT_INSERTCHARPRE)) { #define INPUT_BUFLEN 100 char_u buf[INPUT_BUFLEN + 1]; int i; @@ -6316,18 +6321,22 @@ char_u *get_last_insert_save(void) return s; } -/* - * Check the word in front of the cursor for an abbreviation. - * Called when the non-id character "c" has been entered. - * When an abbreviation is recognized it is removed from the text and - * the replacement string is inserted in typebuf.tb_buf[], followed by "c". - */ -static int echeck_abbr(int c) +/// Check the word in front of the cursor for an abbreviation. +/// Called when the non-id character "c" has been entered. +/// When an abbreviation is recognized it is removed from the text and +/// the replacement string is inserted in typebuf.tb_buf[], followed by "c". +/// +/// @param c character +/// +/// @return true if the word is a known abbreviation. +static bool echeck_abbr(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* Don't check for abbreviation in paste mode, when disabled and just - * after moving around with cursor keys. */ - if (p_paste || no_abbr || arrow_used) - return FALSE; + // Don't check for abbreviation in paste mode, when disabled and just + // after moving around with cursor keys. + if (p_paste || no_abbr || arrow_used) { + return false; + } return check_abbr(c, get_cursor_line_ptr(), curwin->w_cursor.col, curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); @@ -6563,13 +6572,11 @@ static void replace_do_bs(int limit_col) (void)del_char_after_col(limit_col); } -/* - * Return TRUE if C-indenting is on. - */ -static int cindent_on(void) { - return !p_paste && (curbuf->b_p_cin - || *curbuf->b_p_inde != NUL - ); +/// Check that C-indenting is on. +static bool cindent_on(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL); } /* @@ -6580,9 +6587,14 @@ static int cindent_on(void) { */ void fixthisline(IndentGetter get_the_indent) { - change_indent(INDENT_SET, get_the_indent(), FALSE, 0, TRUE); - if (linewhite(curwin->w_cursor.lnum)) - did_ai = TRUE; /* delete the indent if the line stays empty */ + int amount = get_the_indent(); + + if (amount >= 0) { + change_indent(INDENT_SET, amount, false, 0, true); + if (linewhite(curwin->w_cursor.lnum)) { + did_ai = true; // delete the indent if the line stays empty + } + } } void fix_indent(void) { @@ -6594,32 +6606,33 @@ void fix_indent(void) { do_c_expr_indent(); } -/* - * return TRUE if 'cinkeys' contains the key "keytyped", - * when == '*': Only if key is preceded with '*' (indent before insert) - * when == '!': Only if key is preceded with '!' (don't insert) - * when == ' ': Only if key is not preceded with '*'(indent afterwards) - * - * "keytyped" can have a few special values: - * KEY_OPEN_FORW - * KEY_OPEN_BACK - * KEY_COMPLETE just finished completion. - * - * If line_is_empty is TRUE accept keys with '0' before them. - */ -int in_cinkeys(int keytyped, int when, int line_is_empty) +/// Check that "cinkeys" contains the key "keytyped", +/// when == '*': Only if key is preceded with '*' (indent before insert) +/// when == '!': Only if key is preceded with '!' (don't insert) +/// when == ' ': Only if key is not preceded with '*' (indent afterwards) +/// +/// "keytyped" can have a few special values: +/// KEY_OPEN_FORW : +/// KEY_OPEN_BACK : +/// KEY_COMPLETE : Just finished completion. +/// +/// @param keytyped key that was typed +/// @param when condition on when to perform the check +/// @param line_is_empty when true, accept keys with '0' before them. +bool in_cinkeys(int keytyped, int when, bool line_is_empty) { - char_u *look; + char_u *look; int try_match; int try_match_word; - char_u *p; - char_u *line; + char_u *p; + char_u *line; int icase; int i; - if (keytyped == NUL) - /* Can happen with CTRL-Y and CTRL-E on a short line. */ - return FALSE; + if (keytyped == NUL) { + // Can happen with CTRL-Y and CTRL-E on a short line. + return false; + } if (*curbuf->b_p_inde != NUL) look = curbuf->b_p_indk; /* 'indentexpr' set: use 'indentkeys' */ @@ -6635,68 +6648,64 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) case '!': try_match = (*look == '!'); break; default: try_match = (*look != '*'); break; } - if (*look == '*' || *look == '!') - ++look; + if (*look == '*' || *look == '!') { + look++; + } - /* - * If there is a '0', only accept a match if the line is empty. - * But may still match when typing last char of a word. - */ + // If there is a '0', only accept a match if the line is empty. + // But may still match when typing last char of a word. if (*look == '0') { try_match_word = try_match; - if (!line_is_empty) - try_match = FALSE; - ++look; - } else - try_match_word = FALSE; + if (!line_is_empty) { + try_match = false; + } + look++; + } else { + try_match_word = false; + } - /* - * does it look like a control character? - */ - if (*look == '^' - && look[1] >= '?' && look[1] <= '_' - ) { - if (try_match && keytyped == Ctrl_chr(look[1])) - return TRUE; + // Does it look like a control character? + if (*look == '^' && look[1] >= '?' && look[1] <= '_') { + if (try_match && keytyped == Ctrl_chr(look[1])) { + return true; + } look += 2; - } - /* - * 'o' means "o" command, open forward. - * 'O' means "O" command, open backward. - */ - else if (*look == 'o') { - if (try_match && keytyped == KEY_OPEN_FORW) - return TRUE; - ++look; + + // 'o' means "o" command, open forward. + // 'O' means "O" command, open backward. + } else if (*look == 'o') { + if (try_match && keytyped == KEY_OPEN_FORW) { + return true; + } + look++; } else if (*look == 'O') { - if (try_match && keytyped == KEY_OPEN_BACK) - return TRUE; - ++look; - } - /* - * 'e' means to check for "else" at start of line and just before the - * cursor. - */ - else if (*look == 'e') { + if (try_match && keytyped == KEY_OPEN_BACK) { + return true; + } + look++; + + // 'e' means to check for "else" at start of line and just before the + // cursor. + } else if (*look == 'e') { if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) { p = get_cursor_line_ptr(); - if (skipwhite(p) == p + curwin->w_cursor.col - 4 && - STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) - return TRUE; + if (skipwhite(p) == p + curwin->w_cursor.col - 4 + && STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) { + return true; + } } - ++look; - } - /* - * ':' only causes an indent if it is at the end of a label or case - * statement, or when it was before typing the ':' (to fix - * class::method for C++). - */ - else if (*look == ':') { + look++; + + // ':' only causes an indent if it is at the end of a label or case + // statement, or when it was before typing the ':' (to fix + // class::method for C++). + } else if (*look == ':') { if (try_match && keytyped == ':') { p = get_cursor_line_ptr(); - if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) - return TRUE; - /* Need to get the line again after cin_islabel(). */ + if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) { + return true; + } + // Need to get the line again after cin_islabel(). p = get_cursor_line_ptr(); if (curwin->w_cursor.col > 2 && p[curwin->w_cursor.col - 1] == ':' @@ -6706,28 +6715,27 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) || cin_islabel()); p = get_cursor_line_ptr(); p[curwin->w_cursor.col - 1] = ':'; - if (i) - return TRUE; + if (i) { + return true; + } } } - ++look; - } - /* - * Is it a key in <>, maybe? - */ - else if (*look == '<') { + look++; + + // Is it a key in <>, maybe? + } else if (*look == '<') { if (try_match) { - /* - * make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>, - * <:> and <!> so that people can re-indent on o, O, e, 0, <, - * >, *, : and ! keys if they really really want to. - */ + // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>, + // <:> and <!> so that people can re-indent on o, O, e, 0, <, + // >, *, : and ! keys if they really really want to. if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL - && keytyped == look[1]) - return TRUE; + && keytyped == look[1]) { + return true; + } - if (keytyped == get_special_key_code(look + 1)) - return TRUE; + if (keytyped == get_special_key_code(look + 1)) { + return true; + } } while (*look && *look != '>') look++; @@ -6798,18 +6806,18 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) (int)(curwin->w_cursor.col - (p - look))) match = FALSE; } - if (match) - return TRUE; + if (match) { + return true; + } } look = p; - } - /* - * ok, it's a boring generic character. - */ - else { - if (try_match && *look == keytyped) - return TRUE; - ++look; + + // Ok, it's a boring generic character. + } else { + if (try_match && *look == keytyped) { + return true; + } + look++; } /* @@ -6817,7 +6825,7 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) */ look = skip_to_option_part(look); } - return FALSE; + return false; } /* @@ -7044,27 +7052,24 @@ static void ins_ctrl_hat(void) status_redraw_curbuf(); } -/* - * Handle ESC in insert mode. - * Returns TRUE when leaving insert mode, FALSE when going to repeat the - * insert. - */ -static int -ins_esc ( - long *count, - int cmdchar, - int nomove /* don't move cursor */ -) +/// Handle ESC in insert mode. +/// +/// @param[in,out] count repeat count of the insert command +/// @param cmdchar command that started the insert +/// @param nomove when true, don't move the cursor +/// +/// @return true when leaving insert mode, false when repeating the insert. +static bool ins_esc(long *count, int cmdchar, bool nomove) + FUNC_ATTR_NONNULL_ARG(1) { - int temp; - static int disabled_redraw = FALSE; + static bool disabled_redraw = false; check_spell_redraw(); - temp = curwin->w_cursor.col; + int temp = curwin->w_cursor.col; if (disabled_redraw) { - --RedrawingDisabled; - disabled_redraw = FALSE; + RedrawingDisabled--; + disabled_redraw = false; } if (!arrow_used) { /* @@ -7094,9 +7099,10 @@ ins_esc ( if (cmdchar == 'r' || cmdchar == 'v') { stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer } - ++RedrawingDisabled; - disabled_redraw = TRUE; - return FALSE; /* repeat the insert */ + RedrawingDisabled++; + disabled_redraw = true; + // Repeat the insert + return false; } stop_insert(&curwin->w_cursor, TRUE, nomove); undisplay_dollar(); @@ -7146,16 +7152,15 @@ ins_esc ( setmouse(); ui_cursor_shape(); /* may show different cursor shape */ - /* - * When recording or for CTRL-O, need to display the new mode. - * Otherwise remove the mode message. - */ - if (Recording || restart_edit != NUL) + // When recording or for CTRL-O, need to display the new mode. + // Otherwise remove the mode message. + if (Recording || restart_edit != NUL) { showmode(); - else if (p_smd) + } else if (p_smd) { MSG(""); - - return TRUE; /* exit Insert mode */ + } + // Exit Insert mode + return true; } /* @@ -7193,14 +7198,16 @@ static void ins_ctrl_(void) showmode(); } -/* - * If 'keymodel' contains "startsel", may start selection. - * Returns TRUE when a CTRL-O and other keys stuffed. - */ -static int ins_start_select(int c) +/// If 'keymodel' contains "startsel", may start selection. +/// +/// @param c character to check +// +/// @return true when a CTRL-O and other keys stuffed. +static bool ins_start_select(int c) + FUNC_ATTR_WARN_UNUSED_RESULT { if (!km_startsel) { - return FALSE; + return false; } switch (c) { case K_KHOME: @@ -7211,32 +7218,27 @@ static int ins_start_select(int c) case K_KPAGEDOWN: if (!(mod_mask & MOD_MASK_SHIFT)) break; - /* FALLTHROUGH */ + // FALLTHROUGH case K_S_LEFT: case K_S_RIGHT: case K_S_UP: case K_S_DOWN: case K_S_END: case K_S_HOME: - /* Start selection right away, the cursor can move with - * CTRL-O when beyond the end of the line. */ + // Start selection right away, the cursor can move with + // CTRL-O when beyond the end of the line. start_selection(); - /* Execute the key in (insert) Select mode. */ + // Execute the key in (insert) Select mode. stuffcharReadbuff(Ctrl_O); if (mod_mask) { - char_u buf[4]; - - buf[0] = K_SPECIAL; - buf[1] = KS_MODIFIER; - buf[2] = mod_mask; - buf[3] = NUL; + char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL }; stuffReadbuff(buf); } stuffcharReadbuff(c); - return TRUE; + return true; } - return FALSE; + return false; } /* @@ -7250,15 +7252,15 @@ static void ins_insert(int replaceState) return; } - set_vim_var_string(VV_INSERTMODE, - (char_u *)((State & REPLACE_FLAG) ? "i" : - replaceState == VREPLACE ? "v" : - "r"), 1); - apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, FALSE, curbuf); - if (State & REPLACE_FLAG) + set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG) ? "i" : + replaceState == VREPLACE ? "v" : + "r"), 1); + apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, false, curbuf); + if (State & REPLACE_FLAG) { State = INSERT | (State & LANGMAP); - else + } else { State = replaceState | (State & LANGMAP); + } AppendCharToRedobuff(K_INS); showmode(); ui_cursor_shape(); /* may show different cursor shape */ @@ -7360,18 +7362,23 @@ static void ins_bs_one(colnr_T *vcolp) (void)del_char(FALSE); } -/* - * Handle Backspace, delete-word and delete-line in Insert mode. - * Return TRUE when backspace was actually used. - */ -static int ins_bs(int c, int mode, int *inserted_space_p) +/// Handle Backspace, delete-word and delete-line in Insert mode. +/// +/// @param c charcter that was typed +/// @param mode backspace mode to use +/// @param[in,out] inserted_space_p whether a space was the last +// character inserted +/// +/// @return true when backspace was actually used. +static bool ins_bs(int c, int mode, int *inserted_space_p) + FUNC_ATTR_NONNULL_ARG(3) { linenr_T lnum; int cc; int temp = 0; /* init for GCC */ colnr_T save_col; colnr_T mincol; - int did_backspace = FALSE; + bool did_backspace = false; int in_indent; int oldState; int cpc[MAX_MCO]; /* composing characters */ @@ -7382,43 +7389,43 @@ static int ins_bs(int c, int mode, int *inserted_space_p) * can't backup past starting point unless 'backspace' > 1 * can backup to a previous line if 'backspace' == 0 */ - if ( bufempty() - || ( - !revins_on && - ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) - || (!can_bs(BS_START) - && (arrow_used - || (curwin->w_cursor.lnum == Insstart_orig.lnum - && curwin->w_cursor.col <= Insstart_orig.col))) - || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 - && curwin->w_cursor.col <= ai_col) - || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) { + if (bufempty() + || (!revins_on + && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) + || (!can_bs(BS_START) + && (arrow_used + || (curwin->w_cursor.lnum == Insstart_orig.lnum + && curwin->w_cursor.col <= Insstart_orig.col))) + || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 + && curwin->w_cursor.col <= ai_col) + || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) { vim_beep(BO_BS); return false; } - if (stop_arrow() == FAIL) - return FALSE; + if (stop_arrow() == FAIL) { + return false; + } in_indent = inindent(0); - if (in_indent) - can_cindent = FALSE; - end_comment_pending = NUL; /* After BS, don't auto-end comment */ - if (revins_on) /* put cursor after last inserted char */ + if (in_indent) { + can_cindent = false; + } + end_comment_pending = NUL; // After BS, don't auto-end comment + if (revins_on) { // put cursor after last inserted char inc_cursor(); - - /* Virtualedit: - * BACKSPACE_CHAR eats a virtual space - * BACKSPACE_WORD eats all coladd - * BACKSPACE_LINE eats all coladd and keeps going - */ + } + // Virtualedit: + // BACKSPACE_CHAR eats a virtual space + // BACKSPACE_WORD eats all coladd + // BACKSPACE_LINE eats all coladd and keeps going if (curwin->w_cursor.coladd > 0) { if (mode == BACKSPACE_CHAR) { - --curwin->w_cursor.coladd; - return TRUE; + curwin->w_cursor.coladd--; + return true; } if (mode == BACKSPACE_WORD) { curwin->w_cursor.coladd = 0; - return TRUE; + return true; } curwin->w_cursor.coladd = 0; } @@ -7427,15 +7434,14 @@ static int ins_bs(int c, int mode, int *inserted_space_p) * delete newline! */ if (curwin->w_cursor.col == 0) { - lnum = Insstart_orig.lnum; + lnum = Insstart.lnum; if (curwin->w_cursor.lnum == lnum || revins_on) { if (u_save((linenr_T)(curwin->w_cursor.lnum - 2), - (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { - return FALSE; + (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { + return false; } - --Insstart_orig.lnum; - Insstart_orig.col = MAXCOL; - Insstart = Insstart_orig; + Insstart.lnum--; + Insstart.col = MAXCOL; } /* * In replace mode: @@ -7585,27 +7591,35 @@ static int ins_bs(int c, int mode, int *inserted_space_p) * happen when using 'sts' and 'linebreak'. */ if (vcol >= start_vcol) ins_bs_one(&vcol); - } - /* - * Delete upto starting point, start of line or previous word. - */ - else do { - if (!revins_on) /* put cursor on char to be deleted */ - dec_cursor(); - /* start of word? */ - if (mode == BACKSPACE_WORD && !ascii_isspace(gchar_cursor())) { - mode = BACKSPACE_WORD_NOT_SPACE; - temp = vim_iswordc(gchar_cursor()); + // Delete upto starting point, start of line or previous word. + } else { + int cclass = 0, prev_cclass = 0; + + if (has_mbyte) { + cclass = mb_get_class(get_cursor_pos_ptr()); + } + do { + if (!revins_on) { // put cursor on char to be deleted + dec_cursor(); + } + cc = gchar_cursor(); + // look multi-byte character class + if (has_mbyte) { + prev_cclass = cclass; + cclass = mb_get_class(get_cursor_pos_ptr()); } - /* end of word? */ - else if (mode == BACKSPACE_WORD_NOT_SPACE - && (ascii_isspace(cc = gchar_cursor()) - || vim_iswordc(cc) != temp)) { - if (!revins_on) + if (mode == BACKSPACE_WORD && !ascii_isspace(cc)) { // start of word? + mode = BACKSPACE_WORD_NOT_SPACE; + temp = vim_iswordc(cc); + } else if (mode == BACKSPACE_WORD_NOT_SPACE + && ((ascii_isspace(cc) || vim_iswordc(cc) != temp) + || prev_cclass != cclass)) { // end of word? + if (!revins_on) { inc_cursor(); - else if (State & REPLACE_FLAG) + } else if (State & REPLACE_FLAG) { dec_cursor(); + } break; } if (State & REPLACE_FLAG) @@ -7630,26 +7644,26 @@ static int ins_bs(int c, int mode, int *inserted_space_p) if (revins_on && gchar_cursor() == NUL) break; } - /* Just a single backspace?: */ - if (mode == BACKSPACE_CHAR) + // Just a single backspace?: + if (mode == BACKSPACE_CHAR) { break; - } while ( - revins_on || - (curwin->w_cursor.col > mincol - && (curwin->w_cursor.lnum != Insstart_orig.lnum - || curwin->w_cursor.col != Insstart_orig.col))); - did_backspace = TRUE; - } - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - if (curwin->w_cursor.col <= 1) - did_ai = FALSE; - /* - * It's a little strange to put backspaces into the redo - * buffer, but it makes auto-indent a lot easier to deal - * with. - */ + } + } while (revins_on + || (curwin->w_cursor.col > mincol + && (curwin->w_cursor.lnum != Insstart_orig.lnum + || curwin->w_cursor.col != Insstart_orig.col))); + } + did_backspace = true; + } + did_si = false; + can_si = false; + can_si_back = false; + if (curwin->w_cursor.col <= 1) { + did_ai = false; + } + // It's a little strange to put backspaces into the redo + // buffer, but it makes auto-indent a lot easier to deal + // with. AppendCharToRedobuff(c); /* If deleted before the insertion point, adjust it */ @@ -7668,12 +7682,12 @@ static int ins_bs(int c, int mode, int *inserted_space_p) if (vim_strchr(p_cpo, CPO_BACKSPACE) != NULL && dollar_vcol == -1) dollar_vcol = curwin->w_virtcol; - /* When deleting a char the cursor line must never be in a closed fold. - * E.g., when 'foldmethod' is indent and deleting the first non-white - * char before a Tab. */ - if (did_backspace) + // When deleting a char the cursor line must never be in a closed fold. + // E.g., when 'foldmethod' is indent and deleting the first non-white + // char before a Tab. + if (did_backspace) { foldOpenCursor(); - + } return did_backspace; } @@ -7739,6 +7753,8 @@ static void ins_mousescroll(int dir) (long)(curwin->w_botline - curwin->w_topline)); else scroll_redraw(dir, 3L); + } else { + mouse_scroll_horiz(dir); } did_scroll = TRUE; } @@ -7992,35 +8008,37 @@ static void ins_pagedown(void) } } -/* - * Handle TAB in Insert or Replace mode. - * Return TRUE when the TAB needs to be inserted like a normal character. - */ -static int ins_tab(void) +/// Handle TAB in Insert or Replace mode. +/// +/// @return true when the TAB needs to be inserted like a normal character. +static bool ins_tab(void) + FUNC_ATTR_WARN_UNUSED_RESULT { - int ind; int i; int temp; - if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) + if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) { Insstart_blank_vcol = get_nolist_virtcol(); - if (echeck_abbr(TAB + ABBR_OFF)) - return FALSE; + } + if (echeck_abbr(TAB + ABBR_OFF)) { + return false; + } - ind = inindent(0); - if (ind) - can_cindent = FALSE; + int ind = inindent(0); + if (ind) { + can_cindent = false; + } - /* - * When nothing special, insert TAB like a normal character - */ + // When nothing special, insert TAB like a normal character if (!curbuf->b_p_et && !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf)) - && get_sts_value() == 0) - return TRUE; + && get_sts_value() == 0) { + return true; + } - if (stop_arrow() == FAIL) - return TRUE; + if (stop_arrow() == FAIL) { + return true; + } did_ai = FALSE; did_si = FALSE; @@ -8028,12 +8046,13 @@ static int ins_tab(void) can_si_back = FALSE; AppendToRedobuff((char_u *)"\t"); - if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */ + if (p_sta && ind) { // insert tab in indent, use "shiftwidth" temp = get_sw_value(curbuf); - else if (curbuf->b_p_sts != 0) /* use 'softtabstop' when set */ + } else if (curbuf->b_p_sts != 0) { // use "softtabstop" when set temp = get_sts_value(); - else /* otherwise use 'tabstop' */ + } else { // otherwise use "tabstop" temp = (int)curbuf->b_p_ts; + } temp -= get_nolist_virtcol() % temp; /* @@ -8173,21 +8192,20 @@ static int ins_tab(void) curwin->w_p_list = save_list; } - return FALSE; + return false; } -/* - * Handle CR or NL in insert mode. - * Return TRUE when it can't undo. - */ -static int ins_eol(int c) +/// Handle CR or NL in insert mode. +/// +/// @return true when it can't undo. +static bool ins_eol(int c) { - int i; - - if (echeck_abbr(c + ABBR_OFF)) - return FALSE; - if (stop_arrow() == FAIL) - return TRUE; + if (echeck_abbr(c + ABBR_OFF)) { + return false; + } + if (stop_arrow() == FAIL) { + return true; + } undisplay_dollar(); /* @@ -8220,9 +8238,9 @@ static int ins_eol(int c) curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr()); AppendToRedobuff(NL_STR); - i = open_line(FORWARD, - has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : - 0, old_indent); + bool i = open_line(FORWARD, + has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0, + old_indent); old_indent = 0; can_cindent = TRUE; /* When inserting a line the cursor line must never be in a closed fold. */ @@ -8476,22 +8494,22 @@ static colnr_T get_nolist_virtcol(void) */ static char_u *do_insert_char_pre(int c) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; - /* Return quickly when there is nothing to do. */ - if (!has_insertcharpre()) + // Return quickly when there is nothing to do. + if (!has_event(EVENT_INSERTCHARPRE)) { return NULL; - - if (has_mbyte) - buf[(*mb_char2bytes)(c, buf)] = NUL; - else { + } + if (has_mbyte) { + buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL; + } else { buf[0] = c; buf[1] = NUL; } - /* Lock the text to avoid weird things from happening. */ - ++textlock; - set_vim_var_string(VV_CHAR, buf, -1); /* set v:char */ + // Lock the text to avoid weird things from happening. + textlock++; + set_vim_var_string(VV_CHAR, buf, -1); char_u *res = NULL; if (apply_autocmds(EVENT_INSERTCHARPRE, NULL, NULL, FALSE, curbuf)) { @@ -8502,8 +8520,25 @@ static char_u *do_insert_char_pre(int c) res = vim_strsave(get_vim_var_str(VV_CHAR)); } - set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */ - --textlock; + set_vim_var_string(VV_CHAR, NULL, -1); + textlock--; return res; } + +static void show_pum(int save_w_wrow) +{ + // RedrawingDisabled may be set when invoked through complete(). + int n = RedrawingDisabled; + RedrawingDisabled = 0; + + // If the cursor moved we need to remove the pum first. + setcursor(); + if (save_w_wrow != curwin->w_wrow) { + ins_compl_del_pum(); + } + + ins_compl_show_pum(); + setcursor(); + RedrawingDisabled = n; +} diff --git a/src/nvim/edit.h b/src/nvim/edit.h index 0289b2c3a6..0d61f26bcc 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -36,12 +36,6 @@ typedef int (*IndentGetter)(void); #define INSCHAR_NO_FEX 8 /* don't use 'formatexpr' */ #define INSCHAR_COM_LIST 16 /* format comments with list/2nd line indent */ -/* direction for nv_mousescroll() and ins_mousescroll() */ -#define MSCR_DOWN 0 /* DOWN must be FALSE */ -#define MSCR_UP 1 -#define MSCR_LEFT -1 -#define MSCR_RIGHT -2 - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "edit.h.generated.h" #endif diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4a2bf2ac7a..712ee06b85 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -66,13 +66,14 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/mouse.h" #include "nvim/terminal.h" #include "nvim/undo.h" #include "nvim/version.h" #include "nvim/window.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/decode.h" #include "nvim/os/os.h" #include "nvim/event/libuv_process.h" #include "nvim/event/pty_process.h" @@ -87,7 +88,6 @@ #include "nvim/os/dl.h" #include "nvim/os/input.h" #include "nvim/event/loop.h" -#include "nvim/lib/kvec.h" #include "nvim/lib/queue.h" #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -142,13 +142,6 @@ typedef struct lval_S { char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ } lval_T; -/// Structure defining state for read_from_list() -typedef struct { - const listitem_T *li; ///< Item currently read. - size_t offset; ///< Byte offset inside the read item. - size_t li_length; ///< Length of the string inside the read item. -} ListReaderState; - static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_listidx = N_("E684: list index out of range: %" PRId64); @@ -160,6 +153,7 @@ static char *e_listdictarg = N_( static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); +static char *e_strreq = N_("E114: String required"); static char *e_toomanyarg = N_("E118: Too many arguments for function: %s"); static char *e_dictkey = N_("E716: Key not present in Dictionary: %s"); static char *e_funcexts = N_( @@ -173,6 +167,7 @@ static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_float_as_string = N_("E806: using Float as a String"); static char_u * const empty_string = (char_u *)""; +static char_u * const namespace_char = (char_u *)"abglstvw"; static dictitem_T globvars_var; /* variable used for g: */ #define globvarht globvardict.dv_hashtab @@ -183,17 +178,7 @@ static dictitem_T globvars_var; /* variable used for g: */ */ static hashtab_T compat_hashtab; -/* - * When recursively copying lists and dicts we need to remember which ones we - * have done to avoid endless recursiveness. This unique ID is used for that. - * The last bit is used for previous_funccal, ignored when comparing. - */ -static int current_copyID = 0; -#define COPYID_INC 2 -#define COPYID_MASK (~0x1) - -/// Abort conversion to string after a recursion error. -static bool did_echo_string_emsg = false; +hashtab_T func_hashtab; /* * Array to hold the hashtab with variables local to each sourced script. @@ -296,92 +281,112 @@ typedef enum { #define VV_RO 2 /* read-only */ #define VV_RO_SBX 4 /* read-only in the sandbox */ -#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}, {0} +#define VV(idx, name, type, flags) \ + [idx] = { \ + .vv_name = name, \ + .vv_di = { \ + .di_tv = { .v_type = type }, \ + .di_flags = 0, \ + }, \ + .vv_filler = { 0 }, \ + .vv_flags = flags, \ + } // Array to hold the value of v: variables. // The value is in a dictitem, so that it can also be used in the v: scope. // The reason to use this table anyway is for very quick access to the // variables with the VV_ defines. static struct vimvar { - char *vv_name; /* name of variable, without v: */ - dictitem_T vv_di; /* value and name for key */ - char vv_filler[16]; /* space for LONGEST name below!!! */ - char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */ -} vimvars[VV_LEN] = -{ - /* - * The order here must match the VV_ defines in eval.h! - * Initializing a union does not work, leave tv.vval empty to get zero's. - */ - {VV_NAME("count", VAR_NUMBER), VV_COMPAT+VV_RO}, - {VV_NAME("count1", VAR_NUMBER), VV_RO}, - {VV_NAME("prevcount", VAR_NUMBER), VV_RO}, - {VV_NAME("errmsg", VAR_STRING), VV_COMPAT}, - {VV_NAME("warningmsg", VAR_STRING), 0}, - {VV_NAME("statusmsg", VAR_STRING), 0}, - {VV_NAME("shell_error", VAR_NUMBER), VV_COMPAT+VV_RO}, - {VV_NAME("this_session", VAR_STRING), VV_COMPAT}, - {VV_NAME("version", VAR_NUMBER), VV_COMPAT+VV_RO}, - {VV_NAME("lnum", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("termresponse", VAR_STRING), VV_RO}, - {VV_NAME("fname", VAR_STRING), VV_RO}, - {VV_NAME("lang", VAR_STRING), VV_RO}, - {VV_NAME("lc_time", VAR_STRING), VV_RO}, - {VV_NAME("ctype", VAR_STRING), VV_RO}, - {VV_NAME("charconvert_from", VAR_STRING), VV_RO}, - {VV_NAME("charconvert_to", VAR_STRING), VV_RO}, - {VV_NAME("fname_in", VAR_STRING), VV_RO}, - {VV_NAME("fname_out", VAR_STRING), VV_RO}, - {VV_NAME("fname_new", VAR_STRING), VV_RO}, - {VV_NAME("fname_diff", VAR_STRING), VV_RO}, - {VV_NAME("cmdarg", VAR_STRING), VV_RO}, - {VV_NAME("foldstart", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("foldend", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("folddashes", VAR_STRING), VV_RO_SBX}, - {VV_NAME("foldlevel", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("progname", VAR_STRING), VV_RO}, - {VV_NAME("servername", VAR_STRING), VV_RO}, - {VV_NAME("dying", VAR_NUMBER), VV_RO}, - {VV_NAME("exception", VAR_STRING), VV_RO}, - {VV_NAME("throwpoint", VAR_STRING), VV_RO}, - {VV_NAME("register", VAR_STRING), VV_RO}, - {VV_NAME("cmdbang", VAR_NUMBER), VV_RO}, - {VV_NAME("insertmode", VAR_STRING), VV_RO}, - {VV_NAME("val", VAR_UNKNOWN), VV_RO}, - {VV_NAME("key", VAR_UNKNOWN), VV_RO}, - {VV_NAME("profiling", VAR_NUMBER), VV_RO}, - {VV_NAME("fcs_reason", VAR_STRING), VV_RO}, - {VV_NAME("fcs_choice", VAR_STRING), 0}, - {VV_NAME("beval_bufnr", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_winnr", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_lnum", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_col", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_text", VAR_STRING), VV_RO}, - {VV_NAME("scrollstart", VAR_STRING), 0}, - {VV_NAME("swapname", VAR_STRING), VV_RO}, - {VV_NAME("swapchoice", VAR_STRING), 0}, - {VV_NAME("swapcommand", VAR_STRING), VV_RO}, - {VV_NAME("char", VAR_STRING), 0}, - {VV_NAME("mouse_win", VAR_NUMBER), 0}, - {VV_NAME("mouse_lnum", VAR_NUMBER), 0}, - {VV_NAME("mouse_col", VAR_NUMBER), 0}, - {VV_NAME("operator", VAR_STRING), VV_RO}, - {VV_NAME("searchforward", VAR_NUMBER), 0}, - {VV_NAME("hlsearch", VAR_NUMBER), 0}, - {VV_NAME("oldfiles", VAR_LIST), 0}, - {VV_NAME("windowid", VAR_NUMBER), VV_RO}, - {VV_NAME("progpath", VAR_STRING), VV_RO}, - {VV_NAME("command_output", VAR_STRING), 0}, - {VV_NAME("completed_item", VAR_DICT), VV_RO}, - {VV_NAME("option_new", VAR_STRING), VV_RO}, - {VV_NAME("option_old", VAR_STRING), VV_RO}, - {VV_NAME("option_type", VAR_STRING), VV_RO}, - {VV_NAME("msgpack_types", VAR_DICT), VV_RO}, + char *vv_name; ///< Name of the variable, without v:. + dictitem_T vv_di; ///< Value of the variable, with name. + char vv_filler[16]; ///< Space for longest name from below. + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. +} vimvars[] = +{ + // VV_ tails differing from upcased string literals: + // VV_CC_FROM "charconvert_from" + // VV_CC_TO "charconvert_to" + // VV_SEND_SERVER "servername" + // VV_REG "register" + // VV_OP "operator" + VV(VV_COUNT, "count", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, VV_COMPAT), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_FALSE, "false", VAR_SPECIAL, VV_RO), + VV(VV_TRUE, "true", VAR_SPECIAL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), }; +#undef VV /* shorthand */ #define vv_type vv_di.di_tv.v_type #define vv_nr vv_di.di_tv.vval.v_number +#define vv_special vv_di.di_tv.vval.v_special #define vv_float vv_di.di_tv.vval.v_float #define vv_str vv_di.di_tv.vval.v_string #define vv_list vv_di.di_tv.vval.v_list @@ -415,29 +420,6 @@ typedef struct dict_watcher { bool busy; // prevent recursion if the dict is changed in the callback } DictWatcher; -/// Structure representing current VimL to messagepack conversion state -typedef struct { - enum { - kMPConvDict, ///< Convert dict_T *dictionary. - kMPConvList, ///< Convert list_T *list. - kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. - } type; - union { - struct { - dict_T *dict; ///< Currently converted dictionary. - hashitem_T *hi; ///< Currently converted dictionary item. - size_t todo; ///< Amount of items left to process. - } d; ///< State of dictionary conversion. - struct { - list_T *list; ///< Currently converted list. - listitem_T *li; ///< Currently converted list item. - } l; ///< State of list or generic mapping conversion. - } data; ///< Data to convert. -} MPConvStackVal; - -/// Stack used to convert VimL values to messagepack. -typedef kvec_t(MPConvStackVal) MPConvStack; - typedef struct { TerminalJobData *data; ufunc_T *callback; @@ -456,17 +438,6 @@ typedef struct { static uint64_t current_job_id = 1; static PMap(uint64_t) *jobs = NULL; -typedef enum { - kMPNil, - kMPBoolean, - kMPInteger, - kMPFloat, - kMPString, - kMPBinary, - kMPArray, - kMPMap, - kMPExt, -} MessagePackType; static const char *const msgpack_type_names[] = { [kMPNil] = "nil", [kMPBoolean] = "boolean", @@ -478,7 +449,7 @@ static const char *const msgpack_type_names[] = { [kMPMap] = "map", [kMPExt] = "ext", }; -static const list_T *msgpack_type_lists[] = { +const list_T *eval_msgpack_type_lists[] = { [kMPNil] = NULL, [kMPBoolean] = NULL, [kMPInteger] = NULL, @@ -495,8 +466,9 @@ static const list_T *msgpack_type_lists[] = { */ void eval_init(void) { + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + jobs = pmap_new(uint64_t)(); - int i; struct vimvar *p; init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); @@ -505,7 +477,7 @@ void eval_init(void) hash_init(&compat_hashtab); hash_init(&func_hashtab); - for (i = 0; i < VV_LEN; ++i) { + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) @@ -534,7 +506,7 @@ void eval_init(void) .v_type = VAR_LIST, .vval = { .v_list = type_list, }, }; - msgpack_type_lists[i] = type_list; + eval_msgpack_type_lists[i] = type_list; if (dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. assert(false); @@ -544,9 +516,19 @@ void eval_init(void) set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + + dict_T *v_event = dict_alloc(); + v_event->dv_lock = VAR_FIXED; + set_vim_var_dict(VV_EVENT, v_event); + set_vim_var_list(VV_ERRORS, list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); - set_reg_var(0); /* default for v:register is not 0 but '"' */ + + set_vim_var_special(VV_FALSE, kSpecialVarFalse); + set_vim_var_special(VV_TRUE, kSpecialVarTrue); + set_vim_var_special(VV_NULL, kSpecialVarNull); + + set_reg_var(0); // default for v:register is not 0 but '"' } #if defined(EXITFREE) @@ -554,7 +536,7 @@ void eval_clear(void) { struct vimvar *p; - for (int i = 0; i < VV_LEN; ++i) { + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; if (p->vv_di.di_tv.v_type == VAR_STRING) { xfree(p->vv_str); @@ -789,45 +771,50 @@ void var_redir_stop(void) redir_varname = NULL; } -int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to) +int eval_charconvert(const char *const enc_from, const char *const enc_to, + const char *const fname_from, const char *const fname_to) { - int err = FALSE; + int err = false; set_vim_var_string(VV_CC_FROM, enc_from, -1); set_vim_var_string(VV_CC_TO, enc_to, -1); set_vim_var_string(VV_FNAME_IN, fname_from, -1); set_vim_var_string(VV_FNAME_OUT, fname_to, -1); - if (eval_to_bool(p_ccv, &err, NULL, FALSE)) - err = TRUE; + if (eval_to_bool(p_ccv, &err, NULL, false)) { + err = true; + } set_vim_var_string(VV_CC_FROM, NULL, -1); set_vim_var_string(VV_CC_TO, NULL, -1); set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_FNAME_OUT, NULL, -1); - if (err) + if (err) { return FAIL; + } return OK; } -int eval_printexpr(char_u *fname, char_u *args) +int eval_printexpr(const char *const fname, const char *const args) { - int err = FALSE; + int err = false; set_vim_var_string(VV_FNAME_IN, fname, -1); set_vim_var_string(VV_CMDARG, args, -1); - if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) - err = TRUE; + if (eval_to_bool(p_pexpr, &err, NULL, false)) { + err = true; + } set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_CMDARG, NULL, -1); if (err) { - os_remove((char *)fname); + os_remove(fname); return FAIL; } return OK; } -void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile) +void eval_diff(const char *const origfile, const char *const newfile, + const char *const outfile) { int err = FALSE; @@ -840,7 +827,8 @@ void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile) set_vim_var_string(VV_FNAME_OUT, NULL, -1); } -void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile) +void eval_patch(const char *const origfile, const char *const difffile, + const char *const outfile) { int err; @@ -1111,19 +1099,17 @@ typval_T *eval_expr(char_u *arg, char_u **nextcmd) } -/* - * Call some vimL function and return the result in "*rettv". - * Uses argv[argc] for the function arguments. Only Number and String - * arguments are currently supported. - * Returns OK or FAIL. - */ -int -call_vim_function ( +// Call some vimL function and return the result in "*rettv". +// Uses argv[argc] for the function arguments. Only Number and String +// arguments are currently supported. +// +// Return OK or FAIL. +int call_vim_function( char_u *func, int argc, char_u **argv, - int safe, /* use the sandbox */ - int str_arg_only, /* all arguments are strings */ + int safe, // use the sandbox + int str_arg_only, // all arguments are strings typval_T *rettv ) { @@ -1136,18 +1122,19 @@ call_vim_function ( typval_T *argvars = xmalloc((argc + 1) * sizeof(typval_T)); for (int i = 0; i < argc; i++) { - /* Pass a NULL or empty argument as an empty string */ + // Pass a NULL or empty argument as an empty string if (argv[i] == NULL || *argv[i] == NUL) { argvars[i].v_type = VAR_STRING; argvars[i].vval.v_string = (char_u *)""; continue; } - if (str_arg_only) + if (str_arg_only) { len = 0; - else - /* Recognize a number argument, the others must be strings. */ - vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL); + } else { + // Recognize a number argument, the others must be strings. + vim_str2nr(argv[i], NULL, &len, STR2NR_ALL, &n, NULL, 0); + } if (len != 0 && len == (int)STRLEN(argv[i])) { argvars[i].v_type = VAR_NUMBER; argvars[i].vval.v_number = n; @@ -1164,16 +1151,17 @@ call_vim_function ( rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */ ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, TRUE, NULL); + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &doesrange, true, NULL); if (safe) { --sandbox; restore_funccal(save_funccalp); } xfree(argvars); - if (ret == FAIL) + if (ret == FAIL) { clear_tv(rettv); + } return ret; } @@ -1698,12 +1686,13 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first) } error = TRUE; } else { - if (tofree != NULL) + if (tofree != NULL) { name = tofree; - if (get_var_tv(name, len, &tv, TRUE, FALSE) == FAIL) - error = TRUE; - else { - /* handle d.key, l[idx], f(expr) */ + } + if (get_var_tv(name, len, &tv, NULL, true, false) == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) arg_subsc = arg; if (handle_subscript(&arg, &tv, TRUE, TRUE) == FAIL) error = TRUE; @@ -1723,7 +1712,7 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first) } else { int c; - char_u *s = (char_u *) echo_string(&tv, NULL); + char_u *s = (char_u *) encode_tv2echo(&tv, NULL); c = *arg; *arg = NUL; list_one_var_a((char_u *)"", @@ -2174,10 +2163,10 @@ get_lval ( if (len == -1) clear_tv(&var1); break; - } - /* existing variable, need to check if it can be changed */ - else if (var_check_ro(lp->ll_di->di_flags, name)) + } else if (var_check_ro(lp->ll_di->di_flags, name, false)) { + // existing variable, need to check if it can be changed return NULL; + } if (len == -1) clear_tv(&var1); @@ -2272,11 +2261,16 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch if (op != NULL && *op != '=') { typval_T tv; - /* handle +=, -= and .= */ + // handle +=, -= and .= + di = NULL; if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), - &tv, TRUE, FALSE) == OK) { - if (tv_op(&tv, rettv, op) == OK) - set_var(lp->ll_name, &tv, FALSE); + &tv, &di, true, false) == OK) { + if ((di == NULL + || (!var_check_ro(di->di_flags, lp->ll_name, false) + && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false))) + && tv_op(&tv, rettv, op) == OK) { + set_var(lp->ll_name, &tv, false); + } clear_tv(&tv); } } else @@ -2284,16 +2278,17 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch *endp = cc; } } else if (tv_check_lock(lp->ll_newkey == NULL - ? lp->ll_tv->v_lock - : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name)) - ; - else if (lp->ll_range) { + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, false)) { + } else if (lp->ll_range) { listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; // Check whether any of the list items is locked - for (listitem_T *ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) { + for (listitem_T *ri = rettv->vval.v_list->lv_first; + ri != NULL && ll_li != NULL; ) { + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) { return; } ri = ri->li_next; @@ -2401,11 +2396,12 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) char_u numbuf[NUMBUFLEN]; char_u *s; - /* Can't do anything with a Funcref or a Dict on the right. */ + // Can't do anything with a Funcref, a Dict or special value on the right. if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { switch (tv1->v_type) { case VAR_DICT: case VAR_FUNC: + case VAR_SPECIAL: break; case VAR_LIST: @@ -2473,6 +2469,9 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) tv1->vval.v_float -= f; } return OK; + + case VAR_UNKNOWN: + assert(false); } } @@ -2888,16 +2887,19 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) else if (do_unlet(lp->ll_name, forceit) == FAIL) ret = FAIL; *name_end = cc; - } else if (tv_check_lock(lp->ll_tv->v_lock, lp->ll_name)) + } else if ((lp->ll_list != NULL + && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, false)) + || (lp->ll_dict != NULL + && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, false))) { return FAIL; - else if (lp->ll_range) { + } else if (lp->ll_range) { listitem_T *li; listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) { + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) { return false; } ll_li = li; @@ -2951,17 +2953,30 @@ int do_unlet(char_u *name, int forceit) hashtab_T *ht; hashitem_T *hi; char_u *varname; + dict_T *d; dictitem_T *di; dict_T *dict; ht = find_var_ht_dict(name, &varname, &dict); if (ht != NULL && *varname != NUL) { + if (ht == &globvarht) { + d = &globvardict; + } else if (current_funccal != NULL + && ht == ¤t_funccal->l_vars.dv_hashtab) { + d = ¤t_funccal->l_vars; + } else { + di = find_var_in_ht(ht, *name, (char_u *)"", false); + d = di->di_tv.vval.v_dict; + } + hi = hash_find(ht, varname); if (!HASHITEM_EMPTY(hi)) { di = HI2DI(hi); - if (var_check_fixed(di->di_flags, name) - || var_check_ro(di->di_flags, name)) + if (var_check_fixed(di->di_flags, name, false) + || var_check_ro(di->di_flags, name, false) + || tv_check_lock(d->dv_lock, name, false)) { return FAIL; + } typval_T oldtv; bool watched = is_watched(dict); @@ -3027,12 +3042,13 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) li = li->li_next; ++lp->ll_n1; } - } else if (lp->ll_list != NULL) - /* (un)lock a List item. */ + } else if (lp->ll_list != NULL) { + // (un)lock a List item. item_lock(&lp->ll_li->li_tv, deep, lock); - else - /* un(lock) a Dictionary item. */ + } else { + // (un)lock a Dictionary item. item_lock(&lp->ll_di->di_tv, deep, lock); + } return ret; } @@ -3093,6 +3109,15 @@ static void item_lock(typval_T *tv, int deep, int lock) } } } + break; + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_STRING: + case VAR_FUNC: + case VAR_SPECIAL: + break; + case VAR_UNKNOWN: + assert(false); } --recurse; } @@ -3171,7 +3196,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) static size_t bdone; static size_t wdone; static size_t tdone; - static int vidx; + static size_t vidx; static hashitem_T *hi; hashtab_T *ht; @@ -3233,9 +3258,10 @@ char_u *get_user_var_name(expand_T *xp, int idx) return cat_prefix_varname('t', hi->hi_key); } - /* v: variables */ - if (vidx < VV_LEN) + // v: variables + if (vidx < ARRAY_SIZE(vimvars)) { return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name); + } xfree(varnamebuf); varnamebuf = NULL; @@ -3554,9 +3580,10 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) type = TYPE_SEQUAL; break; case 'i': if (p[1] == 's') { - if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') + if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') { len = 5; - if (!vim_isIDc(p[len])) { + } + if (!isalnum(p[len]) && p[len] != '_') { type = len == 2 ? TYPE_EQUAL : TYPE_NEQUAL; type_is = TRUE; } @@ -3983,12 +4010,22 @@ eval6 ( * When either side is a float the result is a float. */ if (use_float) { - if (op == '*') + if (op == '*') { f1 = f1 * f2; - else if (op == '/') { - /* We rely on the floating point library to handle divide - * by zero to result in "inf" and not a crash. */ - f1 = f2 != 0 ? f1 / f2 : INFINITY; + } else if (op == '/') { + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 + ? ( +#ifdef NAN + f1 == 0 + ? NAN + : +#endif + (f1 > 0 + ? INFINITY + : -INFINITY) + ) + : f1 / f2); } else { EMSG(_("E804: Cannot use '%' with Float")); return FAIL; @@ -4023,38 +4060,35 @@ eval6 ( return OK; } -/* - * Handle sixth level expression: - * number number constant - * "string" string constant - * 'string' literal string constant - * &option-name option value - * @r register contents - * identifier variable value - * function() function call - * $VAR environment variable - * (expression) nested expression - * [expr, expr] List - * {key: val, key: val} Dictionary - * - * Also handle: - * ! in front logical NOT - * - in front unary minus - * + in front unary plus (ignored) - * trailing [] subscript in String or List - * trailing .name entry in Dictionary - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ -static int -eval7 ( +// Handle sixth level expression: +// number number constant +// "string" string constant +// 'string' literal string constant +// &option-name option value +// @r register contents +// identifier variable value +// function() function call +// $VAR environment variable +// (expression) nested expression +// [expr, expr] List +// {key: val, key: val} Dictionary +// +// Also handle: +// ! in front logical NOT +// - in front unary minus +// + in front unary plus (ignored) +// trailing [] subscript in String or List +// trailing .name entry in Dictionary +// +// "arg" must point to the first non-white of the expression. +// "arg" is advanced to the next non-white after the recognized expression. +// +// Return OK or FAIL. +static int eval7( char_u **arg, typval_T *rettv, int evaluate, - int want_string /* after "." operator */ + int want_string // after "." operator ) { long n; @@ -4064,24 +4098,19 @@ eval7 ( int ret = OK; char_u *alias; - /* - * Initialise variable so that clear_tv() can't mistake this for a - * string and free a string that isn't there. - */ + // Initialise variable so that clear_tv() can't mistake this for a + // string and free a string that isn't there. rettv->v_type = VAR_UNKNOWN; - /* - * Skip '!' and '-' characters. They are handled later. - */ + // Skip '!' and '-' characters. They are handled later. start_leader = *arg; - while (**arg == '!' || **arg == '-' || **arg == '+') + while (**arg == '!' || **arg == '-' || **arg == '+') { *arg = skipwhite(*arg + 1); + } end_leader = *arg; switch (**arg) { - /* - * Number constant. - */ + // Number constant. case '0': case '1': case '2': @@ -4094,38 +4123,41 @@ eval7 ( case '9': { char_u *p = skipdigits(*arg + 1); - int get_float = FALSE; + int get_float = false; - /* We accept a float when the format matches - * "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very - * strict to avoid backwards compatibility problems. - * Don't look for a float after the "." operator, so that - * ":let vers = 1.2.3" doesn't fail. */ + // We accept a float when the format matches + // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very + // strict to avoid backwards compatibility problems. + // Don't look for a float after the "." operator, so that + // ":let vers = 1.2.3" doesn't fail. if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) { - get_float = TRUE; + get_float = true; p = skipdigits(p + 2); if (*p == 'e' || *p == 'E') { ++p; - if (*p == '-' || *p == '+') + if (*p == '-' || *p == '+') { ++p; - if (!ascii_isdigit(*p)) - get_float = FALSE; - else + } + if (!ascii_isdigit(*p)) { + get_float = false; + } else { p = skipdigits(p + 1); + } + } + if (ASCII_ISALPHA(*p) || *p == '.') { + get_float = false; } - if (ASCII_ISALPHA(*p) || *p == '.') - get_float = FALSE; } if (get_float) { float_T f; - *arg += string2float(*arg, &f); + *arg += string2float((char *) *arg, &f); if (evaluate) { rettv->v_type = VAR_FLOAT; rettv->vval.v_float = f; } } else { - vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL); + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0); *arg += len; if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -4135,62 +4167,47 @@ eval7 ( break; } - /* - * String constant: "string". - */ + // String constant: "string". case '"': ret = get_string_tv(arg, rettv, evaluate); break; - /* - * Literal string constant: 'str''ing'. - */ + // Literal string constant: 'str''ing'. case '\'': ret = get_lit_string_tv(arg, rettv, evaluate); break; - /* - * List: [expr, expr] - */ + // List: [expr, expr] case '[': ret = get_list_tv(arg, rettv, evaluate); break; - /* - * Dictionary: {key: val, key: val} - */ + // Dictionary: {key: val, key: val} case '{': ret = get_dict_tv(arg, rettv, evaluate); break; - /* - * Option value: &name - */ + // Option value: &name case '&': ret = get_option_tv(arg, rettv, evaluate); break; - /* - * Environment variable: $VAR. - */ + // Environment variable: $VAR. case '$': ret = get_env_tv(arg, rettv, evaluate); break; - /* - * Register contents: @r. - */ + // Register contents: @r. case '@': ++*arg; if (evaluate) { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc); } - if (**arg != NUL) + if (**arg != NUL) { ++*arg; + } break; - /* - * nested expression: (expression). - */ + // nested expression: (expression). case '(': *arg = skipwhite(*arg + 1); - ret = eval1(arg, rettv, evaluate); /* recursive! */ - if (**arg == ')') + ret = eval1(arg, rettv, evaluate); // recursive! + if (**arg == ')') { ++*arg; - else if (ret == OK) { + } else if (ret == OK) { EMSG(_("E110: Missing ')'")); clear_tv(rettv); ret = FAIL; @@ -4202,71 +4219,72 @@ eval7 ( } if (ret == NOTDONE) { - /* - * Must be a variable or function name. - * Can also be a curly-braces kind of name: {expr}. - */ + // Must be a variable or function name. + // Can also be a curly-braces kind of name: {expr}. s = *arg; - len = get_name_len(arg, &alias, evaluate, TRUE); - if (alias != NULL) + len = get_name_len(arg, &alias, evaluate, true); + if (alias != NULL) { s = alias; + } - if (len <= 0) + if (len <= 0) { ret = FAIL; - else { - if (**arg == '(') { /* recursive! */ - /* If "s" is the name of a variable of type VAR_FUNC - * use its contents. */ + } else { + if (**arg == '(') { // recursive! + // If "s" is the name of a variable of type VAR_FUNC + // use its contents. s = deref_func_name(s, &len, !evaluate); - /* Invoke the function. */ + // Invoke the function. ret = get_func_tv(s, len, rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, NULL); - /* If evaluate is FALSE rettv->v_type was not set in - * get_func_tv, but it's needed in handle_subscript() to parse - * what follows. So set it here. */ + // If evaluate is false rettv->v_type was not set in + // get_func_tv, but it's needed in handle_subscript() to parse + // what follows. So set it here. if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { rettv->vval.v_string = empty_string; rettv->v_type = VAR_FUNC; } - /* Stop the expression evaluation when immediately - * aborting on error, or when an interrupt occurred or - * an exception was thrown but not caught. */ + // Stop the expression evaluation when immediately + // aborting on error, or when an interrupt occurred or + // an exception was thrown but not caught. if (aborting()) { - if (ret == OK) + if (ret == OK) { clear_tv(rettv); + } ret = FAIL; } - } else if (evaluate) - ret = get_var_tv(s, len, rettv, TRUE, FALSE); - else + } else if (evaluate) { + ret = get_var_tv(s, len, rettv, NULL, true, false); + } else { ret = OK; + } } xfree(alias); } *arg = skipwhite(*arg); - /* Handle following '[', '(' and '.' for expr[expr], expr.name, - * expr(expr). */ - if (ret == OK) - ret = handle_subscript(arg, rettv, evaluate, TRUE); + // Handle following '[', '(' and '.' for expr[expr], expr.name, + // expr(expr). + if (ret == OK) { + ret = handle_subscript(arg, rettv, evaluate, true); + } - /* - * Apply logical NOT and unary '-', from right to left, ignore '+'. - */ + // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - int error = FALSE; + int error = false; int val = 0; float_T f = 0.0; - if (rettv->v_type == VAR_FLOAT) + if (rettv->v_type == VAR_FLOAT) { f = rettv->vval.v_float; - else + } else { val = get_tv_number_chk(rettv, &error); + } if (error) { clear_tv(rettv); ret = FAIL; @@ -4274,15 +4292,17 @@ eval7 ( while (end_leader > start_leader) { --end_leader; if (*end_leader == '!') { - if (rettv->v_type == VAR_FLOAT) + if (rettv->v_type == VAR_FLOAT) { f = !f; - else + } else { val = !val; + } } else if (*end_leader == '-') { - if (rettv->v_type == VAR_FLOAT) + if (rettv->v_type == VAR_FLOAT) { f = -f; - else + } else { val = -val; + } } } if (rettv->v_type == VAR_FLOAT) { @@ -4320,14 +4340,37 @@ eval_index ( char_u *s; char_u *key = NULL; - if (rettv->v_type == VAR_FUNC) { - if (verbose) - EMSG(_("E695: Cannot index a Funcref")); - return FAIL; - } else if (rettv->v_type == VAR_FLOAT) { - if (verbose) - EMSG(_(e_float_as_string)); - return FAIL; + switch (rettv->v_type) { + case VAR_FUNC: { + if (verbose) { + EMSG(_("E695: Cannot index a Funcref")); + } + return FAIL; + } + case VAR_FLOAT: { + if (verbose) { + EMSG(_(e_float_as_string)); + } + return FAIL; + } + case VAR_SPECIAL: { + if (verbose) { + EMSG(_("E909: Cannot index a special variable")); + } + return FAIL; + } + case VAR_UNKNOWN: { + if (evaluate) { + return FAIL; + } + // fallthrough + } + case VAR_STRING: + case VAR_NUMBER: + case VAR_LIST: + case VAR_DICT: { + break; + } } init_tv(&var1); @@ -4518,6 +4561,11 @@ eval_index ( *rettv = var1; } break; + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_UNKNOWN: + break; // Not evaluating, skipping over subscript } } @@ -4653,10 +4701,13 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) int n, nr; int c = toupper(*p); - if (c == 'X') + if (c == 'X') { n = 2; - else + } else if (*p == 'u') { n = 4; + } else { + n = 8; + } nr = 0; while (--n >= 0 && ascii_isxdigit(p[1])) { ++p; @@ -4689,13 +4740,14 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) ++name; break; - /* Special key, e.g.: "\<C-W>" */ - case '<': extra = trans_special(&p, name, TRUE); + // Special key, e.g.: "\<C-W>" + case '<': + extra = trans_special((const char_u **) &p, STRLEN(p), name, true); if (extra != 0) { name += extra; break; } - /* FALLTHROUGH */ + // FALLTHROUGH default: MB_COPY_CHAR(p, name); break; @@ -5059,10 +5111,18 @@ tv_equal ( s1 = get_tv_string_buf(tv1, buf1); s2 = get_tv_string_buf(tv2, buf2); return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; + + case VAR_SPECIAL: + return tv1->vval.v_special == tv2->vval.v_special; + + case VAR_UNKNOWN: + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does + // not equal anything, not even self. + return false; } - EMSG2(_(e_intern2), "tv_equal()"); - return TRUE; + assert(false); + return false; } /* @@ -5463,9 +5523,10 @@ static int list_join_inner(garray_T *const gap, list_T *const l, for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { char *s; size_t len; - s = echo_string(&item->li_tv, &len); - if (s == NULL) + s = encode_tv2echo(&item->li_tv, &len); + if (s == NULL) { return FAIL; + } sumlen += (int) len; @@ -5473,9 +5534,6 @@ static int list_join_inner(garray_T *const gap, list_T *const l, p->tofree = p->s = (char_u *) s; line_breakcheck(); - if (did_echo_string_emsg) { // recursion error, bail out - break; - } } /* Allocate result buffer with its total size, avoid re-allocation and @@ -5527,6 +5585,22 @@ static int list_join(garray_T *const gap, list_T *const l, return retval; } +/// Get next (unique) copy ID +/// +/// Used for traversing nested structures e.g. when serializing them or garbage +/// collecting. +int get_copyID(void) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + // CopyID for recursively traversing lists and dicts + // + // This value is needed to avoid endless recursiveness. Last bit is used for + // previous_funccal and normally ignored when comparing. + static int current_copyID = 0; + current_copyID += COPYID_INC; + return current_copyID; +} + /* * Garbage collection for lists and dictionaries. * @@ -5562,8 +5636,7 @@ bool garbage_collect(void) // We advance by two because we add one for items referenced through // previous_funccal. - current_copyID += COPYID_INC; - int copyID = current_copyID; + const int copyID = get_copyID(); // 1. Go through all accessible variables and mark all lists and dicts // with copyID. @@ -5651,6 +5724,14 @@ bool garbage_collect(void) ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL); } + // Jobs + { + TerminalJobData *data; + map_foreach_value(jobs, data, { + ABORTING(set_ref_dict)(data->self, copyID); + }) + } + // v: vars ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL); @@ -5728,8 +5809,7 @@ static int free_unref_items(int copyID) // Go through the list of dicts and free items without the copyID. // Don't free dicts that are referenced internally. for (dict_T *dd = first_dict; dd != NULL; ) { - if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && !dd->internal_refcount) { + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list // of dicts or list of lists. */ @@ -5901,6 +5981,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, } break; } + + case VAR_FUNC: + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_NUMBER: + case VAR_STRING: { + break; + } } return abort; } @@ -5970,7 +6059,6 @@ dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET d->dv_scope = 0; d->dv_refcount = 0; d->dv_copyID = 0; - d->internal_refcount = 0; QUEUE_INIT(&d->watchers); return d; @@ -5988,6 +6076,27 @@ static void rettv_dict_alloc(typval_T *rettv) ++d->dv_refcount; } +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void dict_clear(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + size_t todo = d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + dictitem_free(HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + todo--; + } + } + + hash_unlock(&d->dv_hashtab); +} + /* * Unreference a Dictionary: decrement the reference count and free it when it @@ -6061,7 +6170,7 @@ dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET #ifndef __clang_analyzer__ STRCPY(di->di_key, key); #endif - di->di_flags = 0; + di->di_flags = DI_FLAGS_ALLOC; return di; } @@ -6073,7 +6182,7 @@ static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key)); STRCPY(di->di_key, org->di_key); - di->di_flags = 0; + di->di_flags = DI_FLAGS_ALLOC; copy_tv(&org->di_tv, &di->di_tv); return di; @@ -6101,7 +6210,9 @@ static void dictitem_remove(dict_T *dict, dictitem_T *item) void dictitem_free(dictitem_T *item) { clear_tv(&item->di_tv); - xfree(item); + if (item->di_flags & DI_FLAGS_ALLOC) { + xfree(item); + } } /// Make a copy of dictionary @@ -6227,6 +6338,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list) return OK; } +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen +void dict_set_keys_readonly(dict_T *dict) + FUNC_ATTR_NONNULL_ALL +{ + size_t todo = dict->dv_hashtab.ht_used; + for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { + if (HASHITEM_EMPTY(hi)) { + continue; + } + todo--; + HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + } +} + /* * Get the number of items in a Dictionary. */ @@ -6452,577 +6581,22 @@ failret: return OK; } -#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ - do { \ - if ((val)->copyID_attr == copyID) { \ - CONV_RECURSE((val), conv_type); \ - } \ - (val)->copyID_attr = copyID; \ - } while (0) - -/// Define functions which convert VimL value to something else -/// -/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const -/// tv)` which returns OK or FAIL and helper functions. +/// Convert the string to a floating point number /// -/// @param firstargtype Type of the first argument. It will be used to return -/// the results. -/// @param firstargname Name of the first argument. -/// @param name Name of the target converter. -#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \ -static int name##_convert_one_value(firstargtype firstargname, \ - MPConvStack *const mpstack, \ - typval_T *const tv, \ - const int copyID) \ - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - switch (tv->v_type) { \ - case VAR_STRING: { \ - CONV_STRING(tv->vval.v_string, STRLEN(tv->vval.v_string)); \ - break; \ - } \ - case VAR_NUMBER: { \ - CONV_NUMBER(tv->vval.v_number); \ - break; \ - } \ - case VAR_FLOAT: { \ - CONV_FLOAT(tv->vval.v_float); \ - break; \ - } \ - case VAR_FUNC: { \ - CONV_FUNC(tv->vval.v_string); \ - break; \ - } \ - case VAR_LIST: { \ - if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ - CONV_EMPTY_LIST(); \ - break; \ - } \ - CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \ - CONV_LIST_START(tv->vval.v_list); \ - kv_push( \ - MPConvStackVal, \ - *mpstack, \ - ((MPConvStackVal) { \ - .type = kMPConvList, \ - .data = { \ - .l = { \ - .list = tv->vval.v_list, \ - .li = tv->vval.v_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case VAR_DICT: { \ - if (tv->vval.v_dict == NULL \ - || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ - CONV_EMPTY_DICT(); \ - break; \ - } \ - const dictitem_T *type_di; \ - const dictitem_T *val_di; \ - if (CONV_ALLOW_SPECIAL \ - && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ - && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ - (char_u *) "_TYPE", -1)) != NULL \ - && type_di->di_tv.v_type == VAR_LIST \ - && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ - (char_u *) "_VAL", -1)) != NULL) { \ - size_t i; \ - for (i = 0; i < ARRAY_SIZE(msgpack_type_lists); i++) { \ - if (type_di->di_tv.vval.v_list == msgpack_type_lists[i]) { \ - break; \ - } \ - } \ - if (i == ARRAY_SIZE(msgpack_type_lists)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - switch ((MessagePackType) i) { \ - case kMPNil: { \ - CONV_SPECIAL_NIL(); \ - break; \ - } \ - case kMPBoolean: { \ - if (val_di->di_tv.v_type != VAR_NUMBER) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_SPECIAL_BOOL(val_di->di_tv.vval.v_number); \ - break; \ - } \ - case kMPInteger: { \ - const list_T *val_list; \ - varnumber_T sign; \ - varnumber_T highest_bits; \ - varnumber_T high_bits; \ - varnumber_T low_bits; \ - /* List of 4 integers; first is signed (should be 1 or -1, but */ \ - /* this is not checked), second is unsigned and have at most */ \ - /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ - /* bits is not checked), other unsigned and have at most 31 */ \ - /* non-zero bits (number of bits is not checked).*/ \ - if (val_di->di_tv.v_type != VAR_LIST \ - || (val_list = val_di->di_tv.vval.v_list) == NULL \ - || val_list->lv_len != 4 \ - || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ - || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ - || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ - || (highest_bits = \ - val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ - || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ - || (high_bits = \ - val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ - || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ - || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ - | (uint64_t) (((uint64_t) high_bits) << 31) \ - | (uint64_t) low_bits); \ - if (sign > 0) { \ - CONV_UNSIGNED_NUMBER(number); \ - } else { \ - CONV_NUMBER(-number); \ - } \ - break; \ - } \ - case kMPFloat: { \ - if (val_di->di_tv.v_type != VAR_FLOAT) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_FLOAT(val_di->di_tv.vval.v_float); \ - break; \ - } \ - case kMPString: \ - case kMPBinary: { \ - const bool is_string = ((MessagePackType) i == kMPString); \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - size_t len; \ - char *buf; \ - if (!vim_list_to_buf(val_di->di_tv.vval.v_list, &len, &buf)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - if (is_string) { \ - CONV_STR_STRING(buf, len); \ - } else { \ - CONV_STRING(buf, len); \ - } \ - xfree(buf); \ - break; \ - } \ - case kMPArray: { \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \ - kMPConvList); \ - CONV_LIST_START(val_di->di_tv.vval.v_list); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvList, \ - .data = { \ - .l = { \ - .list = val_di->di_tv.vval.v_list, \ - .li = val_di->di_tv.vval.v_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case kMPMap: { \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - if (val_di->di_tv.vval.v_list == NULL) { \ - CONV_EMPTY_DICT(); \ - break; \ - } \ - list_T *const val_list = val_di->di_tv.vval.v_list; \ - for (const listitem_T *li = val_list->lv_first; li != NULL; \ - li = li->li_next) { \ - if (li->li_tv.v_type != VAR_LIST \ - || li->li_tv.vval.v_list->lv_len != 2) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - } \ - CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \ - CONV_SPECIAL_MAP_START(val_list); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvPairs, \ - .data = { \ - .l = { \ - .list = val_list, \ - .li = val_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case kMPExt: { \ - const list_T *val_list; \ - varnumber_T type; \ - if (val_di->di_tv.v_type != VAR_LIST \ - || (val_list = val_di->di_tv.vval.v_list) == NULL \ - || val_list->lv_len != 2 \ - || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ - || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ - || type < INT8_MIN \ - || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - size_t len; \ - char *buf; \ - if (!vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ - &len, &buf)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_EXT_STRING(buf, len, type); \ - xfree(buf); \ - break; \ - } \ - } \ - break; \ - } \ -name##_convert_one_value_regular_dict: \ - CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \ - CONV_DICT_START(tv->vval.v_dict); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvDict, \ - .data = { \ - .d = { \ - .dict = tv->vval.v_dict, \ - .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ - .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ - }, \ - }, \ - })); \ - break; \ - } \ - default: { \ - EMSG2(_(e_intern2), #name "_convert_one_value()"); \ - return FAIL; \ - } \ - } \ - return OK; \ -} \ -\ -scope int vim_to_##name(firstargtype firstargname, typval_T *const tv) \ - FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - current_copyID += COPYID_INC; \ - const int copyID = current_copyID; \ - MPConvStack mpstack; \ - kv_init(mpstack); \ - if (name##_convert_one_value(firstargname, &mpstack, tv, copyID) == FAIL) { \ - goto vim_to_msgpack_error_ret; \ - } \ - while (kv_size(mpstack)) { \ - MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \ - typval_T *cur_tv = NULL; \ - switch (cur_mpsv->type) { \ - case kMPConvDict: { \ - if (!cur_mpsv->data.d.todo) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ - CONV_DICT_END(cur_mpsv->data.d.dict); \ - continue; \ - } else if (cur_mpsv->data.d.todo \ - != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ - CONV_DICT_BETWEEN_ITEMS(cur_mpsv->data.d.dict); \ - } \ - while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ - cur_mpsv->data.d.hi++; \ - } \ - dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ - cur_mpsv->data.d.todo--; \ - cur_mpsv->data.d.hi++; \ - CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \ - CONV_DICT_AFTER_KEY(cur_mpsv->data.d.dict); \ - cur_tv = &di->di_tv; \ - break; \ - } \ - case kMPConvList: { \ - if (cur_mpsv->data.l.li == NULL) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ - CONV_LIST_END(cur_mpsv->data.l.list); \ - continue; \ - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ - CONV_LIST_BETWEEN_ITEMS(cur_mpsv->data.l.list); \ - } \ - cur_tv = &cur_mpsv->data.l.li->li_tv; \ - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ - break; \ - } \ - case kMPConvPairs: { \ - if (cur_mpsv->data.l.li == NULL) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ - continue; \ - } \ - const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ - if (name##_convert_one_value(firstargname, &mpstack, \ - &kv_pair->lv_first->li_tv, copyID) \ - == FAIL) { \ - goto vim_to_msgpack_error_ret; \ - } \ - cur_tv = &kv_pair->lv_last->li_tv; \ - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ - break; \ - } \ - } \ - if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID) \ - == FAIL) { \ - goto vim_to_msgpack_error_ret; \ - } \ - } \ - kv_destroy(mpstack); \ - return OK; \ -vim_to_msgpack_error_ret: \ - kv_destroy(mpstack); \ - return FAIL; \ -} - -#define CONV_STRING(buf, len) \ - do { \ - const char *const buf_ = (const char *) buf; \ - if (buf == NULL) { \ - ga_concat(gap, (char_u *) "''"); \ - } else { \ - const size_t len_ = (len); \ - size_t num_quotes = 0; \ - for (size_t i = 0; i < len_; i++) { \ - if (buf_[i] == '\'') { \ - num_quotes++; \ - } \ - } \ - ga_grow(gap, 2 + len_ + num_quotes); \ - ga_append(gap, '\''); \ - for (size_t i = 0; i < len_; i++) { \ - if (buf_[i] == '\'') { \ - num_quotes++; \ - ga_append(gap, '\''); \ - } \ - ga_append(gap, buf_[i]); \ - } \ - ga_append(gap, '\''); \ - } \ - } while (0) - -#define CONV_STR_STRING(buf, len) \ - CONV_STRING(buf, len) - -#define CONV_EXT_STRING(buf, len, type) - -#define CONV_NUMBER(num) \ - do { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, NUMBUFLEN - 1, "%" PRId64, (int64_t) (num)); \ - ga_concat(gap, (char_u *) numbuf); \ - } while (0) - -#define CONV_FLOAT(flt) \ - do { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", (flt)); \ - ga_concat(gap, (char_u *) numbuf); \ - } while (0) - -#define CONV_FUNC(fun) \ - do { \ - ga_concat(gap, (char_u *) "function("); \ - CONV_STRING(fun, STRLEN(fun)); \ - ga_append(gap, ')'); \ - } while (0) - -#define CONV_EMPTY_LIST() \ - ga_concat(gap, (char_u *) "[]") - -#define CONV_LIST_START(lst) \ - ga_append(gap, '[') - -#define CONV_EMPTY_DICT() \ - ga_concat(gap, (char_u *) "{}") - -#define CONV_SPECIAL_NIL() - -#define CONV_SPECIAL_BOOL(num) - -#define CONV_UNSIGNED_NUMBER(num) - -#define CONV_SPECIAL_MAP_START(lst) - -#define CONV_DICT_START(dct) \ - ga_append(gap, '{') - -#define CONV_DICT_END(dct) \ - ga_append(gap, '}') - -#define CONV_DICT_AFTER_KEY(dct) \ - ga_concat(gap, (char_u *) ": ") - -#define CONV_DICT_BETWEEN_ITEMS(dct) \ - ga_concat(gap, (char_u *) ", ") - -#define CONV_LIST_END(lst) \ - ga_append(gap, ']') - -#define CONV_LIST_BETWEEN_ITEMS(lst) \ - CONV_DICT_BETWEEN_ITEMS(NULL) - -#define CONV_RECURSE(val, conv_type) \ - do { \ - if (!did_echo_string_emsg) { \ - /* Only give this message once for a recursive call to avoid */ \ - /* flooding the user with errors. */ \ - did_echo_string_emsg = true; \ - EMSG(_("E724: unable to correctly dump variable " \ - "with self-referencing container")); \ - } \ - char ebuf[NUMBUFLEN + 7]; \ - size_t backref = 0; \ - for (; backref < kv_size(*mpstack); backref++) { \ - const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \ - if (mpval.type == conv_type) { \ - if (conv_type == kMPConvDict) { \ - if ((void *) mpval.data.d.dict == (void *) (val)) { \ - break; \ - } \ - } else if (conv_type == kMPConvList) { \ - if ((void *) mpval.data.l.list == (void *) (val)) { \ - break; \ - } \ - } \ - } \ - } \ - vim_snprintf(ebuf, NUMBUFLEN + 6, "{E724@%zu}", backref); \ - ga_concat(gap, (char_u *) &ebuf[0]); \ - return OK; \ - } while (0) - -#define CONV_ALLOW_SPECIAL false - -DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap) - -#undef CONV_RECURSE -#define CONV_RECURSE(val, conv_type) \ - do { \ - char ebuf[NUMBUFLEN + 7]; \ - size_t backref = 0; \ - for (; backref < kv_size(*mpstack); backref++) { \ - const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \ - if (mpval.type == conv_type) { \ - if (conv_type == kMPConvDict) { \ - if ((void *) mpval.data.d.dict == (void *) val) { \ - break; \ - } \ - } else if (conv_type == kMPConvList) { \ - if ((void *) mpval.data.l.list == (void *) val) { \ - break; \ - } \ - } \ - } \ - } \ - if (conv_type == kMPConvDict) { \ - vim_snprintf(ebuf, NUMBUFLEN + 6, "{...@%zu}", backref); \ - } else { \ - vim_snprintf(ebuf, NUMBUFLEN + 6, "[...@%zu]", backref); \ - } \ - ga_concat(gap, (char_u *) &ebuf[0]); \ - return OK; \ - } while (0) - -DEFINE_VIML_CONV_FUNCTIONS(static, echo, garray_T *const, gap) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_SPECIAL_NIL -#undef CONV_SPECIAL_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_SPECIAL_MAP_START -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL - -/// Return a string with the string representation of a variable. -/// Puts quotes around strings, so that they can be parsed back by eval(). -/// -/// @param[in] tv typval_T to convert. -/// @param[out] len Location where length of the result will be saved. -/// -/// @return String representation of the variable or NULL. -static char *tv2string(typval_T *tv, size_t *len) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC -{ - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - vim_to_string(&ga, tv); - did_echo_string_emsg = false; - if (len != NULL) { - *len = (size_t) ga.ga_len; - } - ga_append(&ga, '\0'); - return (char *) ga.ga_data; -} - -/// Return a string with the string representation of a variable. -/// Does not put quotes around strings, as ":echo" displays values. +/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to +/// make sure this always uses a decimal point. /// -/// @param[in] tv typval_T to convert. -/// @param[out] len Location where length of the result will be saved. +/// @param[in] text String to convert. +/// @param[out] ret_value Location where conversion result is saved. /// -/// @return String representation of the variable or NULL. -static char *echo_string(typval_T *tv, size_t *len) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC -{ - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) { - if (tv->vval.v_string != NULL) { - ga_concat(&ga, tv->vval.v_string); - } - } else { - vim_to_echo(&ga, tv); - did_echo_string_emsg = false; - } - if (len != NULL) { - *len = (size_t) ga.ga_len; - } - ga_append(&ga, '\0'); - return (char *) ga.ga_data; -} - -/* - * Convert the string "text" to a floating point number. - * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure - * this always uses a decimal point. - * Returns the length of the text that was consumed. - */ -static int -string2float ( - char_u *text, - float_T *value /* result stored here */ -) +/// @return Length of the text that was consumed. +size_t string2float(const char *const text, float_T *const ret_value) + FUNC_ATTR_NONNULL_ALL { - char *s = (char *)text; - float_T f; + char *s = NULL; - f = strtod(s, &s); - *value = f; - return (int)((char_u *)s - text); + *ret_value = strtod(text, &s); + return (size_t) (s - text); } /// Get the value of an environment variable. @@ -7081,295 +6655,304 @@ static struct fst { /* implementation of function */ } functions[] = { - {"abs", 1, 1, f_abs}, - {"acos", 1, 1, f_acos}, /* WJMc */ - {"add", 2, 2, f_add}, - {"and", 2, 2, f_and}, - {"append", 2, 2, f_append}, - {"argc", 0, 0, f_argc}, - {"argidx", 0, 0, f_argidx}, - {"arglistid", 0, 2, f_arglistid}, - {"argv", 0, 1, f_argv}, - {"asin", 1, 1, f_asin}, /* WJMc */ - {"atan", 1, 1, f_atan}, - {"atan2", 2, 2, f_atan2}, - {"browse", 4, 4, f_browse}, - {"browsedir", 2, 2, f_browsedir}, - {"bufexists", 1, 1, f_bufexists}, - {"buffer_exists", 1, 1, f_bufexists}, /* obsolete */ - {"buffer_name", 1, 1, f_bufname}, /* obsolete */ - {"buffer_number", 1, 1, f_bufnr}, /* obsolete */ - {"buflisted", 1, 1, f_buflisted}, - {"bufloaded", 1, 1, f_bufloaded}, - {"bufname", 1, 1, f_bufname}, - {"bufnr", 1, 2, f_bufnr}, - {"bufwinnr", 1, 1, f_bufwinnr}, - {"byte2line", 1, 1, f_byte2line}, - {"byteidx", 2, 2, f_byteidx}, - {"byteidxcomp", 2, 2, f_byteidxcomp}, - {"call", 2, 3, f_call}, - {"ceil", 1, 1, f_ceil}, - {"changenr", 0, 0, f_changenr}, - {"char2nr", 1, 2, f_char2nr}, - {"cindent", 1, 1, f_cindent}, - {"clearmatches", 0, 0, f_clearmatches}, - {"col", 1, 1, f_col}, - {"complete", 2, 2, f_complete}, - {"complete_add", 1, 1, f_complete_add}, - {"complete_check", 0, 0, f_complete_check}, - {"confirm", 1, 4, f_confirm}, - {"copy", 1, 1, f_copy}, - {"cos", 1, 1, f_cos}, - {"cosh", 1, 1, f_cosh}, - {"count", 2, 4, f_count}, - {"cscope_connection",0,3, f_cscope_connection}, - {"cursor", 1, 3, f_cursor}, - {"deepcopy", 1, 2, f_deepcopy}, - {"delete", 1, 1, f_delete}, - {"dictwatcheradd", 3, 3, f_dictwatcheradd}, - {"dictwatcherdel", 3, 3, f_dictwatcherdel}, - {"did_filetype", 0, 0, f_did_filetype}, - {"diff_filler", 1, 1, f_diff_filler}, - {"diff_hlID", 2, 2, f_diff_hlID}, - {"empty", 1, 1, f_empty}, - {"escape", 2, 2, f_escape}, - {"eval", 1, 1, f_eval}, - {"eventhandler", 0, 0, f_eventhandler}, - {"executable", 1, 1, f_executable}, - {"exepath", 1, 1, f_exepath}, - {"exists", 1, 1, f_exists}, - {"exp", 1, 1, f_exp}, - {"expand", 1, 3, f_expand}, - {"extend", 2, 3, f_extend}, - {"feedkeys", 1, 2, f_feedkeys}, - {"file_readable", 1, 1, f_filereadable}, /* obsolete */ - {"filereadable", 1, 1, f_filereadable}, - {"filewritable", 1, 1, f_filewritable}, - {"filter", 2, 2, f_filter}, - {"finddir", 1, 3, f_finddir}, - {"findfile", 1, 3, f_findfile}, - {"float2nr", 1, 1, f_float2nr}, - {"floor", 1, 1, f_floor}, - {"fmod", 2, 2, f_fmod}, - {"fnameescape", 1, 1, f_fnameescape}, - {"fnamemodify", 2, 2, f_fnamemodify}, - {"foldclosed", 1, 1, f_foldclosed}, - {"foldclosedend", 1, 1, f_foldclosedend}, - {"foldlevel", 1, 1, f_foldlevel}, - {"foldtext", 0, 0, f_foldtext}, - {"foldtextresult", 1, 1, f_foldtextresult}, - {"foreground", 0, 0, f_foreground}, - {"function", 1, 1, f_function}, - {"garbagecollect", 0, 1, f_garbagecollect}, - {"get", 2, 3, f_get}, - {"getbufline", 2, 3, f_getbufline}, - {"getbufvar", 2, 3, f_getbufvar}, - {"getchar", 0, 1, f_getchar}, - {"getcharmod", 0, 0, f_getcharmod}, - {"getcharsearch", 0, 0, f_getcharsearch}, - {"getcmdline", 0, 0, f_getcmdline}, - {"getcmdpos", 0, 0, f_getcmdpos}, - {"getcmdtype", 0, 0, f_getcmdtype}, - {"getcmdwintype", 0, 0, f_getcmdwintype}, - {"getcurpos", 0, 0, f_getcurpos}, - {"getcwd", 0, 0, f_getcwd}, - {"getfontname", 0, 1, f_getfontname}, - {"getfperm", 1, 1, f_getfperm}, - {"getfsize", 1, 1, f_getfsize}, - {"getftime", 1, 1, f_getftime}, - {"getftype", 1, 1, f_getftype}, - {"getline", 1, 2, f_getline}, - {"getloclist", 1, 1, f_getqflist}, - {"getmatches", 0, 0, f_getmatches}, - {"getpid", 0, 0, f_getpid}, - {"getpos", 1, 1, f_getpos}, - {"getqflist", 0, 0, f_getqflist}, - {"getreg", 0, 3, f_getreg}, - {"getregtype", 0, 1, f_getregtype}, - {"gettabvar", 2, 3, f_gettabvar}, - {"gettabwinvar", 3, 4, f_gettabwinvar}, - {"getwinposx", 0, 0, f_getwinposx}, - {"getwinposy", 0, 0, f_getwinposy}, - {"getwinvar", 2, 3, f_getwinvar}, - {"glob", 1, 3, f_glob}, - {"glob2regpat", 1, 1, f_glob2regpat}, - {"globpath", 2, 4, f_globpath}, - {"has", 1, 1, f_has}, - {"has_key", 2, 2, f_has_key}, - {"haslocaldir", 0, 0, f_haslocaldir}, - {"hasmapto", 1, 3, f_hasmapto}, - {"highlightID", 1, 1, f_hlID}, /* obsolete */ - {"highlight_exists",1, 1, f_hlexists}, /* obsolete */ - {"histadd", 2, 2, f_histadd}, - {"histdel", 1, 2, f_histdel}, - {"histget", 1, 2, f_histget}, - {"histnr", 1, 1, f_histnr}, - {"hlID", 1, 1, f_hlID}, - {"hlexists", 1, 1, f_hlexists}, - {"hostname", 0, 0, f_hostname}, - {"iconv", 3, 3, f_iconv}, - {"indent", 1, 1, f_indent}, - {"index", 2, 4, f_index}, - {"input", 1, 3, f_input}, - {"inputdialog", 1, 3, f_inputdialog}, - {"inputlist", 1, 1, f_inputlist}, - {"inputrestore", 0, 0, f_inputrestore}, - {"inputsave", 0, 0, f_inputsave}, - {"inputsecret", 1, 2, f_inputsecret}, - {"insert", 2, 3, f_insert}, - {"invert", 1, 1, f_invert}, - {"isdirectory", 1, 1, f_isdirectory}, - {"islocked", 1, 1, f_islocked}, - {"items", 1, 1, f_items}, - {"jobclose", 1, 2, f_jobclose}, - {"jobresize", 3, 3, f_jobresize}, - {"jobsend", 2, 2, f_jobsend}, - {"jobstart", 1, 2, f_jobstart}, - {"jobstop", 1, 1, f_jobstop}, - {"jobwait", 1, 2, f_jobwait}, - {"join", 1, 2, f_join}, - {"keys", 1, 1, f_keys}, - {"last_buffer_nr", 0, 0, f_last_buffer_nr}, /* obsolete */ - {"len", 1, 1, f_len}, - {"libcall", 3, 3, f_libcall}, - {"libcallnr", 3, 3, f_libcallnr}, - {"line", 1, 1, f_line}, - {"line2byte", 1, 1, f_line2byte}, - {"lispindent", 1, 1, f_lispindent}, - {"localtime", 0, 0, f_localtime}, - {"log", 1, 1, f_log}, - {"log10", 1, 1, f_log10}, - {"map", 2, 2, f_map}, - {"maparg", 1, 4, f_maparg}, - {"mapcheck", 1, 3, f_mapcheck}, - {"match", 2, 4, f_match}, - {"matchadd", 2, 4, f_matchadd}, - {"matchaddpos", 2, 4, f_matchaddpos}, - {"matcharg", 1, 1, f_matcharg}, - {"matchdelete", 1, 1, f_matchdelete}, - {"matchend", 2, 4, f_matchend}, - {"matchlist", 2, 4, f_matchlist}, - {"matchstr", 2, 4, f_matchstr}, - {"max", 1, 1, f_max}, - {"min", 1, 1, f_min}, - {"mkdir", 1, 3, f_mkdir}, - {"mode", 0, 1, f_mode}, - {"msgpackdump", 1, 1, f_msgpackdump}, - {"msgpackparse", 1, 1, f_msgpackparse}, - {"nextnonblank", 1, 1, f_nextnonblank}, - {"nr2char", 1, 2, f_nr2char}, - {"or", 2, 2, f_or}, - {"pathshorten", 1, 1, f_pathshorten}, - {"pow", 2, 2, f_pow}, - {"prevnonblank", 1, 1, f_prevnonblank}, - {"printf", 2, 19, f_printf}, - {"pumvisible", 0, 0, f_pumvisible}, - {"py3eval", 1, 1, f_py3eval}, - {"pyeval", 1, 1, f_pyeval}, - {"range", 1, 3, f_range}, - {"readfile", 1, 3, f_readfile}, - {"reltime", 0, 2, f_reltime}, - {"reltimestr", 1, 1, f_reltimestr}, - {"remove", 2, 3, f_remove}, - {"rename", 2, 2, f_rename}, - {"repeat", 2, 2, f_repeat}, - {"resolve", 1, 1, f_resolve}, - {"reverse", 1, 1, f_reverse}, - {"round", 1, 1, f_round}, - {"rpcnotify", 2, 64, f_rpcnotify}, - {"rpcrequest", 2, 64, f_rpcrequest}, - {"rpcstart", 1, 2, f_rpcstart}, - {"rpcstop", 1, 1, f_rpcstop}, - {"screenattr", 2, 2, f_screenattr}, - {"screenchar", 2, 2, f_screenchar}, - {"screencol", 0, 0, f_screencol}, - {"screenrow", 0, 0, f_screenrow}, - {"search", 1, 4, f_search}, - {"searchdecl", 1, 3, f_searchdecl}, - {"searchpair", 3, 7, f_searchpair}, - {"searchpairpos", 3, 7, f_searchpairpos}, - {"searchpos", 1, 4, f_searchpos}, - {"serverlist", 0, 0, f_serverlist}, - {"serverstart", 0, 1, f_serverstart}, - {"serverstop", 1, 1, f_serverstop}, - {"setbufvar", 3, 3, f_setbufvar}, - {"setcharsearch", 1, 1, f_setcharsearch}, - {"setcmdpos", 1, 1, f_setcmdpos}, - {"setline", 2, 2, f_setline}, - {"setloclist", 2, 3, f_setloclist}, - {"setmatches", 1, 1, f_setmatches}, - {"setpos", 2, 2, f_setpos}, - {"setqflist", 1, 2, f_setqflist}, - {"setreg", 2, 3, f_setreg}, - {"settabvar", 3, 3, f_settabvar}, - {"settabwinvar", 4, 4, f_settabwinvar}, - {"setwinvar", 3, 3, f_setwinvar}, - {"sha256", 1, 1, f_sha256}, - {"shellescape", 1, 2, f_shellescape}, - {"shiftwidth", 0, 0, f_shiftwidth}, - {"simplify", 1, 1, f_simplify}, - {"sin", 1, 1, f_sin}, - {"sinh", 1, 1, f_sinh}, - {"sort", 1, 3, f_sort}, - {"soundfold", 1, 1, f_soundfold}, - {"spellbadword", 0, 1, f_spellbadword}, - {"spellsuggest", 1, 3, f_spellsuggest}, - {"split", 1, 3, f_split}, - {"sqrt", 1, 1, f_sqrt}, - {"str2float", 1, 1, f_str2float}, - {"str2nr", 1, 2, f_str2nr}, - {"strchars", 1, 1, f_strchars}, - {"strdisplaywidth", 1, 2, f_strdisplaywidth}, - {"strftime", 1, 2, f_strftime}, - {"stridx", 2, 3, f_stridx}, - {"string", 1, 1, f_string}, - {"strlen", 1, 1, f_strlen}, - {"strpart", 2, 3, f_strpart}, - {"strridx", 2, 3, f_strridx}, - {"strtrans", 1, 1, f_strtrans}, - {"strwidth", 1, 1, f_strwidth}, - {"submatch", 1, 2, f_submatch}, - {"substitute", 4, 4, f_substitute}, - {"synID", 3, 3, f_synID}, - {"synIDattr", 2, 3, f_synIDattr}, - {"synIDtrans", 1, 1, f_synIDtrans}, - {"synconcealed", 2, 2, f_synconcealed}, - {"synstack", 2, 2, f_synstack}, - {"system", 1, 2, f_system}, - {"systemlist", 1, 3, f_systemlist}, - {"tabpagebuflist", 0, 1, f_tabpagebuflist}, - {"tabpagenr", 0, 1, f_tabpagenr}, - {"tabpagewinnr", 1, 2, f_tabpagewinnr}, - {"tagfiles", 0, 0, f_tagfiles}, - {"taglist", 1, 1, f_taglist}, - {"tan", 1, 1, f_tan}, - {"tanh", 1, 1, f_tanh}, - {"tempname", 0, 0, f_tempname}, - {"termopen", 1, 2, f_termopen}, - {"test", 1, 1, f_test}, - {"tolower", 1, 1, f_tolower}, - {"toupper", 1, 1, f_toupper}, - {"tr", 3, 3, f_tr}, - {"trunc", 1, 1, f_trunc}, - {"type", 1, 1, f_type}, - {"undofile", 1, 1, f_undofile}, - {"undotree", 0, 0, f_undotree}, - {"uniq", 1, 3, f_uniq}, - {"values", 1, 1, f_values}, - {"virtcol", 1, 1, f_virtcol}, - {"visualmode", 0, 1, f_visualmode}, - {"wildmenumode", 0, 0, f_wildmenumode}, - {"winbufnr", 1, 1, f_winbufnr}, - {"wincol", 0, 0, f_wincol}, - {"winheight", 1, 1, f_winheight}, - {"winline", 0, 0, f_winline}, - {"winnr", 0, 1, f_winnr}, - {"winrestcmd", 0, 0, f_winrestcmd}, - {"winrestview", 1, 1, f_winrestview}, - {"winsaveview", 0, 0, f_winsaveview}, - {"winwidth", 1, 1, f_winwidth}, - {"writefile", 2, 3, f_writefile}, - {"xor", 2, 2, f_xor}, + { "abs", 1, 1, f_abs }, + { "acos", 1, 1, f_acos }, // WJMc + { "add", 2, 2, f_add }, + { "and", 2, 2, f_and }, + { "append", 2, 2, f_append }, + { "argc", 0, 0, f_argc }, + { "argidx", 0, 0, f_argidx }, + { "arglistid", 0, 2, f_arglistid }, + { "argv", 0, 1, f_argv }, + { "asin", 1, 1, f_asin }, // WJMc + { "assert_equal", 2, 3, f_assert_equal }, + { "assert_exception", 1, 2, f_assert_exception }, + { "assert_false", 1, 2, f_assert_false }, + { "assert_true", 1, 2, f_assert_true }, + { "atan", 1, 1, f_atan }, + { "atan2", 2, 2, f_atan2 }, + { "browse", 4, 4, f_browse }, + { "browsedir", 2, 2, f_browsedir }, + { "bufexists", 1, 1, f_bufexists }, + { "buffer_exists", 1, 1, f_bufexists }, // obsolete + { "buffer_name", 1, 1, f_bufname }, // obsolete + { "buffer_number", 1, 1, f_bufnr }, // obsolete + { "buflisted", 1, 1, f_buflisted }, + { "bufloaded", 1, 1, f_bufloaded }, + { "bufname", 1, 1, f_bufname }, + { "bufnr", 1, 2, f_bufnr }, + { "bufwinnr", 1, 1, f_bufwinnr }, + { "byte2line", 1, 1, f_byte2line }, + { "byteidx", 2, 2, f_byteidx }, + { "byteidxcomp", 2, 2, f_byteidxcomp }, + { "call", 2, 3, f_call }, + { "ceil", 1, 1, f_ceil }, + { "changenr", 0, 0, f_changenr }, + { "char2nr", 1, 2, f_char2nr }, + { "cindent", 1, 1, f_cindent }, + { "clearmatches", 0, 0, f_clearmatches }, + { "col", 1, 1, f_col }, + { "complete", 2, 2, f_complete }, + { "complete_add", 1, 1, f_complete_add }, + { "complete_check", 0, 0, f_complete_check }, + { "confirm", 1, 4, f_confirm }, + { "copy", 1, 1, f_copy }, + { "cos", 1, 1, f_cos }, + { "cosh", 1, 1, f_cosh }, + { "count", 2, 4, f_count }, + { "cscope_connection", 0, 3, f_cscope_connection }, + { "cursor", 1, 3, f_cursor }, + { "deepcopy", 1, 2, f_deepcopy }, + { "delete", 1, 2, f_delete }, + { "dictwatcheradd", 3, 3, f_dictwatcheradd }, + { "dictwatcherdel", 3, 3, f_dictwatcherdel }, + { "did_filetype", 0, 0, f_did_filetype }, + { "diff_filler", 1, 1, f_diff_filler }, + { "diff_hlID", 2, 2, f_diff_hlID }, + { "empty", 1, 1, f_empty }, + { "escape", 2, 2, f_escape }, + { "eval", 1, 1, f_eval }, + { "eventhandler", 0, 0, f_eventhandler }, + { "executable", 1, 1, f_executable }, + { "exepath", 1, 1, f_exepath }, + { "exists", 1, 1, f_exists }, + { "exp", 1, 1, f_exp }, + { "expand", 1, 3, f_expand }, + { "extend", 2, 3, f_extend }, + { "feedkeys", 1, 2, f_feedkeys }, + { "file_readable", 1, 1, f_filereadable }, // obsolete + { "filereadable", 1, 1, f_filereadable }, + { "filewritable", 1, 1, f_filewritable }, + { "filter", 2, 2, f_filter }, + { "finddir", 1, 3, f_finddir }, + { "findfile", 1, 3, f_findfile }, + { "float2nr", 1, 1, f_float2nr }, + { "floor", 1, 1, f_floor }, + { "fmod", 2, 2, f_fmod }, + { "fnameescape", 1, 1, f_fnameescape }, + { "fnamemodify", 2, 2, f_fnamemodify }, + { "foldclosed", 1, 1, f_foldclosed }, + { "foldclosedend", 1, 1, f_foldclosedend }, + { "foldlevel", 1, 1, f_foldlevel }, + { "foldtext", 0, 0, f_foldtext }, + { "foldtextresult", 1, 1, f_foldtextresult }, + { "foreground", 0, 0, f_foreground }, + { "function", 1, 1, f_function }, + { "garbagecollect", 0, 1, f_garbagecollect }, + { "get", 2, 3, f_get }, + { "getbufline", 2, 3, f_getbufline }, + { "getbufvar", 2, 3, f_getbufvar }, + { "getchar", 0, 1, f_getchar }, + { "getcharmod", 0, 0, f_getcharmod }, + { "getcharsearch", 0, 0, f_getcharsearch }, + { "getcmdline", 0, 0, f_getcmdline }, + { "getcmdpos", 0, 0, f_getcmdpos }, + { "getcmdtype", 0, 0, f_getcmdtype }, + { "getcmdwintype", 0, 0, f_getcmdwintype }, + { "getcurpos", 0, 0, f_getcurpos }, + { "getcwd", 0, 2, f_getcwd }, + { "getfontname", 0, 1, f_getfontname }, + { "getfperm", 1, 1, f_getfperm }, + { "getfsize", 1, 1, f_getfsize }, + { "getftime", 1, 1, f_getftime }, + { "getftype", 1, 1, f_getftype }, + { "getline", 1, 2, f_getline }, + { "getloclist", 1, 1, f_getqflist }, + { "getmatches", 0, 0, f_getmatches }, + { "getpid", 0, 0, f_getpid }, + { "getpos", 1, 1, f_getpos }, + { "getqflist", 0, 0, f_getqflist }, + { "getreg", 0, 3, f_getreg }, + { "getregtype", 0, 1, f_getregtype }, + { "gettabvar", 2, 3, f_gettabvar }, + { "gettabwinvar", 3, 4, f_gettabwinvar }, + { "getwinposx", 0, 0, f_getwinposx }, + { "getwinposy", 0, 0, f_getwinposy }, + { "getwinvar", 2, 3, f_getwinvar }, + { "glob", 1, 4, f_glob }, + { "glob2regpat", 1, 1, f_glob2regpat }, + { "globpath", 2, 5, f_globpath }, + { "has", 1, 1, f_has }, + { "has_key", 2, 2, f_has_key }, + { "haslocaldir", 0, 2, f_haslocaldir }, + { "hasmapto", 1, 3, f_hasmapto }, + { "highlightID", 1, 1, f_hlID }, // obsolete + { "highlight_exists", 1, 1, f_hlexists }, // obsolete + { "histadd", 2, 2, f_histadd }, + { "histdel", 1, 2, f_histdel }, + { "histget", 1, 2, f_histget }, + { "histnr", 1, 1, f_histnr }, + { "hlID", 1, 1, f_hlID }, + { "hlexists", 1, 1, f_hlexists }, + { "hostname", 0, 0, f_hostname }, + { "iconv", 3, 3, f_iconv }, + { "indent", 1, 1, f_indent }, + { "index", 2, 4, f_index }, + { "input", 1, 3, f_input }, + { "inputdialog", 1, 3, f_inputdialog }, + { "inputlist", 1, 1, f_inputlist }, + { "inputrestore", 0, 0, f_inputrestore }, + { "inputsave", 0, 0, f_inputsave }, + { "inputsecret", 1, 2, f_inputsecret }, + { "insert", 2, 3, f_insert }, + { "invert", 1, 1, f_invert }, + { "isdirectory", 1, 1, f_isdirectory }, + { "islocked", 1, 1, f_islocked }, + { "items", 1, 1, f_items }, + { "jobclose", 1, 2, f_jobclose }, + { "jobpid", 1, 1, f_jobpid }, + { "jobresize", 3, 3, f_jobresize }, + { "jobsend", 2, 2, f_jobsend }, + { "jobstart", 1, 2, f_jobstart }, + { "jobstop", 1, 1, f_jobstop }, + { "jobwait", 1, 2, f_jobwait }, + { "join", 1, 2, f_join }, + { "json_decode", 1, 1, f_json_decode }, + { "json_encode", 1, 1, f_json_encode }, + { "keys", 1, 1, f_keys }, + { "last_buffer_nr", 0, 0, f_last_buffer_nr }, // obsolete + { "len", 1, 1, f_len }, + { "libcall", 3, 3, f_libcall }, + { "libcallnr", 3, 3, f_libcallnr }, + { "line", 1, 1, f_line }, + { "line2byte", 1, 1, f_line2byte }, + { "lispindent", 1, 1, f_lispindent }, + { "localtime", 0, 0, f_localtime }, + { "log", 1, 1, f_log }, + { "log10", 1, 1, f_log10 }, + { "map", 2, 2, f_map }, + { "maparg", 1, 4, f_maparg }, + { "mapcheck", 1, 3, f_mapcheck }, + { "match", 2, 4, f_match }, + { "matchadd", 2, 5, f_matchadd }, + { "matchaddpos", 2, 5, f_matchaddpos }, + { "matcharg", 1, 1, f_matcharg }, + { "matchdelete", 1, 1, f_matchdelete }, + { "matchend", 2, 4, f_matchend }, + { "matchlist", 2, 4, f_matchlist }, + { "matchstr", 2, 4, f_matchstr }, + { "max", 1, 1, f_max }, + { "min", 1, 1, f_min }, + { "mkdir", 1, 3, f_mkdir }, + { "mode", 0, 1, f_mode }, + { "msgpackdump", 1, 1, f_msgpackdump }, + { "msgpackparse", 1, 1, f_msgpackparse }, + { "nextnonblank", 1, 1, f_nextnonblank }, + { "nr2char", 1, 2, f_nr2char }, + { "or", 2, 2, f_or }, + { "pathshorten", 1, 1, f_pathshorten }, + { "pow", 2, 2, f_pow }, + { "prevnonblank", 1, 1, f_prevnonblank }, + { "printf", 2, MAX_FUNC_ARGS, f_printf }, + { "pumvisible", 0, 0, f_pumvisible }, + { "py3eval", 1, 1, f_py3eval }, + { "pyeval", 1, 1, f_pyeval }, + { "range", 1, 3, f_range }, + { "readfile", 1, 3, f_readfile }, + { "reltime", 0, 2, f_reltime }, + { "reltimefloat", 1, 1, f_reltimefloat }, + { "reltimestr", 1, 1, f_reltimestr }, + { "remove", 2, 3, f_remove }, + { "rename", 2, 2, f_rename }, + { "repeat", 2, 2, f_repeat }, + { "resolve", 1, 1, f_resolve }, + { "reverse", 1, 1, f_reverse }, + { "round", 1, 1, f_round }, + { "rpcnotify", 2, MAX_FUNC_ARGS, f_rpcnotify }, + { "rpcrequest", 2, MAX_FUNC_ARGS, f_rpcrequest }, + { "rpcstart", 1, 2, f_rpcstart }, + { "rpcstop", 1, 1, f_rpcstop }, + { "screenattr", 2, 2, f_screenattr }, + { "screenchar", 2, 2, f_screenchar }, + { "screencol", 0, 0, f_screencol }, + { "screenrow", 0, 0, f_screenrow }, + { "search", 1, 4, f_search }, + { "searchdecl", 1, 3, f_searchdecl }, + { "searchpair", 3, 7, f_searchpair }, + { "searchpairpos", 3, 7, f_searchpairpos }, + { "searchpos", 1, 4, f_searchpos }, + { "serverlist", 0, 0, f_serverlist }, + { "serverstart", 0, 1, f_serverstart }, + { "serverstop", 1, 1, f_serverstop }, + { "setbufvar", 3, 3, f_setbufvar }, + { "setcharsearch", 1, 1, f_setcharsearch }, + { "setcmdpos", 1, 1, f_setcmdpos }, + { "setline", 2, 2, f_setline }, + { "setloclist", 2, 4, f_setloclist }, + { "setmatches", 1, 1, f_setmatches }, + { "setpos", 2, 2, f_setpos }, + { "setqflist", 1, 3, f_setqflist }, + { "setreg", 2, 3, f_setreg }, + { "settabvar", 3, 3, f_settabvar }, + { "settabwinvar", 4, 4, f_settabwinvar }, + { "setwinvar", 3, 3, f_setwinvar }, + { "sha256", 1, 1, f_sha256 }, + { "shellescape", 1, 2, f_shellescape }, + { "shiftwidth", 0, 0, f_shiftwidth }, + { "simplify", 1, 1, f_simplify }, + { "sin", 1, 1, f_sin }, + { "sinh", 1, 1, f_sinh }, + { "sort", 1, 3, f_sort }, + { "soundfold", 1, 1, f_soundfold }, + { "spellbadword", 0, 1, f_spellbadword }, + { "spellsuggest", 1, 3, f_spellsuggest }, + { "split", 1, 3, f_split }, + { "sqrt", 1, 1, f_sqrt }, + { "str2float", 1, 1, f_str2float }, + { "str2nr", 1, 2, f_str2nr }, + { "strchars", 1, 2, f_strchars }, + { "strdisplaywidth", 1, 2, f_strdisplaywidth }, + { "strftime", 1, 2, f_strftime }, + { "stridx", 2, 3, f_stridx }, + { "string", 1, 1, f_string }, + { "strlen", 1, 1, f_strlen }, + { "strpart", 2, 3, f_strpart }, + { "strridx", 2, 3, f_strridx }, + { "strtrans", 1, 1, f_strtrans }, + { "strwidth", 1, 1, f_strwidth }, + { "submatch", 1, 2, f_submatch }, + { "substitute", 4, 4, f_substitute }, + { "synID", 3, 3, f_synID }, + { "synIDattr", 2, 3, f_synIDattr }, + { "synIDtrans", 1, 1, f_synIDtrans }, + { "synconcealed", 2, 2, f_synconcealed }, + { "synstack", 2, 2, f_synstack }, + { "system", 1, 2, f_system }, + { "systemlist", 1, 3, f_systemlist }, + { "tabpagebuflist", 0, 1, f_tabpagebuflist }, + { "tabpagenr", 0, 1, f_tabpagenr }, + { "tabpagewinnr", 1, 2, f_tabpagewinnr }, + { "tagfiles", 0, 0, f_tagfiles }, + { "taglist", 1, 1, f_taglist }, + { "tan", 1, 1, f_tan }, + { "tanh", 1, 1, f_tanh }, + { "tempname", 0, 0, f_tempname }, + { "termopen", 1, 2, f_termopen }, + { "test", 1, 1, f_test }, + { "tolower", 1, 1, f_tolower }, + { "toupper", 1, 1, f_toupper }, + { "tr", 3, 3, f_tr }, + { "trunc", 1, 1, f_trunc }, + { "type", 1, 1, f_type }, + { "undofile", 1, 1, f_undofile }, + { "undotree", 0, 0, f_undotree }, + { "uniq", 1, 3, f_uniq }, + { "values", 1, 1, f_values }, + { "virtcol", 1, 1, f_virtcol }, + { "visualmode", 0, 1, f_visualmode }, + { "wildmenumode", 0, 0, f_wildmenumode }, + { "winbufnr", 1, 1, f_winbufnr }, + { "wincol", 0, 0, f_wincol }, + { "winheight", 1, 1, f_winheight }, + { "winline", 0, 0, f_winline }, + { "winnr", 0, 1, f_winnr }, + { "winrestcmd", 0, 0, f_winrestcmd }, + { "winrestview", 1, 1, f_winrestview }, + { "winsaveview", 0, 0, f_winsaveview }, + { "winwidth", 1, 1, f_winwidth }, + { "wordcount", 0, 0, f_wordcount }, + { "writefile", 2, 3, f_writefile }, + { "xor", 2, 2, f_xor }, }; @@ -7836,7 +7419,8 @@ static void f_add(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = 1; /* Default: Failed */ if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("add() argument"))) { + && !tv_check_lock(l->lv_lock, + (char_u *)N_("add() argument"), true)) { list_append_tv(l, &argvars[1]); copy_tv(&argvars[0], rettv); } @@ -7972,6 +7556,136 @@ static void f_argv(typval_T *argvars, typval_T *rettv) } } +// Prepare "gap" for an assert error and add the sourcing position. +static void prepare_assert_error(garray_T *gap) +{ + char buf[NUMBUFLEN]; + + ga_init(gap, 1, 100); + if (sourcing_name != NULL) { + ga_concat(gap, sourcing_name); + if (sourcing_lnum > 0) { + ga_concat(gap, (char_u *)" "); + } + } + if (sourcing_lnum > 0) { + vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum); + ga_concat(gap, (char_u *)buf); + } + if (sourcing_name != NULL || sourcing_lnum > 0) { + ga_concat(gap, (char_u *)": "); + } +} + +// Fill "gap" with information about an assert error. +static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, + char_u *exp_str, typval_T *exp_tv, + typval_T *got_tv) +{ + char_u *tofree; + + if (opt_msg_tv->v_type != VAR_UNKNOWN) { + tofree = (char_u *) encode_tv2string(opt_msg_tv, NULL); + ga_concat(gap, tofree); + xfree(tofree); + } else { + ga_concat(gap, (char_u *)"Expected "); + if (exp_str == NULL) { + tofree = (char_u *) encode_tv2string(exp_tv, NULL); + ga_concat(gap, tofree); + xfree(tofree); + } else { + ga_concat(gap, exp_str); + } + tofree = (char_u *) encode_tv2string(got_tv, NULL); + ga_concat(gap, (char_u *)" but got "); + ga_concat(gap, tofree); + xfree(tofree); + } +} + +// Add an assert error to v:errors. +static void assert_error(garray_T *gap) +{ + struct vimvar *vp = &vimvars[VV_ERRORS]; + + if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { + // Make sure v:errors is a list. + set_vim_var_list(VV_ERRORS, list_alloc()); + } + list_append_string(vimvars[VV_ERRORS].vv_list, + gap->ga_data, gap->ga_len); +} + +// "assert_equal(expected, actual[, msg])" function +static void f_assert_equal(typval_T *argvars, typval_T *rettv) +{ + garray_T ga; + + if (!tv_equal(&argvars[0], &argvars[1], false, false)) { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[2], NULL, + &argvars[0], &argvars[1]); + assert_error(&ga); + ga_clear(&ga); + } +} + +/// "assert_exception(string[, msg])" function +static void f_assert_exception(typval_T *argvars, typval_T *rettv) +{ + garray_T ga; + + char *error = (char *)get_tv_string_chk(&argvars[0]); + if (vimvars[VV_EXCEPTION].vv_str == NULL) { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"v:exception is not set"); + assert_error(&ga); + ga_clear(&ga); + } else if (strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], + &vimvars[VV_EXCEPTION].vv_tv); + assert_error(&ga); + ga_clear(&ga); + } +} + +// Common for assert_true() and assert_false(). +static void assert_bool(typval_T *argvars, bool is_true) +{ + int error = (int)false; + garray_T ga; + + if ((argvars[0].v_type != VAR_NUMBER + || (get_tv_number_chk(&argvars[0], &error) == 0) == is_true + || error) + && (argvars[0].v_type != VAR_SPECIAL + || (argvars[0].vval.v_special + != (SpecialVarValue) (is_true + ? kSpecialVarTrue + : kSpecialVarFalse)))) { + prepare_assert_error(&ga); + fill_assert_error(&ga, &argvars[1], + (char_u *)(is_true ? "True" : "False"), + NULL, &argvars[0]); + assert_error(&ga); + ga_clear(&ga); + } +} + +// "assert_false(actual[, msg])" function +static void f_assert_false(typval_T *argvars, typval_T *rettv) +{ + assert_bool(argvars, false); +} + +// "assert_true(actual[, msg])" function +static void f_assert_true(typval_T *argvars, typval_T *rettv) +{ + assert_bool(argvars, true); +} + /* * "asin()" function */ @@ -8615,16 +8329,17 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = cs_connection(num, dbpath, prepend); } -/* - * "cursor(lnum, col)" function - * - * Moves the cursor to the specified line and column. - * Returns 0 when the position could be set, -1 otherwise. - */ +/// "cursor(lnum, col)" function, or +/// "cursor(list)" +/// +/// Moves the cursor to the specified line and column. +/// +/// @returns 0 when the position could be set, -1 otherwise. static void f_cursor(typval_T *argvars, typval_T *rettv) { long line, col; long coladd = 0; + bool set_curswant = true; rettv->vval.v_number = -1; if (argvars[1].v_type == VAR_UNKNOWN) { @@ -8632,37 +8347,44 @@ static void f_cursor(typval_T *argvars, typval_T *rettv) colnr_T curswant = -1; if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) { + EMSG(_(e_invarg)); return; } + line = pos.lnum; col = pos.col; coladd = pos.coladd; if (curswant >= 0) { curwin->w_curswant = curswant - 1; + set_curswant = false; } } else { line = get_tv_lnum(argvars); col = get_tv_number_chk(&argvars[1], NULL); - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { coladd = get_tv_number_chk(&argvars[2], NULL); + } } if (line < 0 || col < 0 - || coladd < 0 - ) - return; /* type error; errmsg already given */ - if (line > 0) + || coladd < 0) { + return; // type error; errmsg already given + } + if (line > 0) { curwin->w_cursor.lnum = line; - if (col > 0) + } + if (col > 0) { curwin->w_cursor.col = col - 1; + } curwin->w_cursor.coladd = coladd; - /* Make sure the cursor is in a valid position. */ + // Make sure the cursor is in a valid position. check_cursor(); - /* Correct cursor for multi-byte character. */ - if (has_mbyte) + // Correct cursor for multi-byte character. + if (has_mbyte) { mb_adjust_cursor(); + } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = set_curswant; rettv->vval.v_number = 0; } @@ -8675,25 +8397,51 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv) if (argvars[1].v_type != VAR_UNKNOWN) noref = get_tv_number_chk(&argvars[1], NULL); - if (noref < 0 || noref > 1) + if (noref < 0 || noref > 1) { EMSG(_(e_invarg)); - else { - current_copyID += COPYID_INC; + } else { var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 - ? current_copyID + ? get_copyID() : 0)); } } -/* - * "delete()" function - */ +// "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv) { - if (check_restricted() || check_secure()) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = os_remove((char *)get_tv_string(&argvars[0])); + char_u nbuf[NUMBUFLEN]; + char_u *name; + char_u *flags; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) { + return; + } + + name = get_tv_string(&argvars[0]); + if (name == NULL || *name == NUL) { + EMSG(_(e_invarg)); + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + flags = get_tv_string_buf(&argvars[1], nbuf); + } else { + flags = (char_u *)""; + } + + if (*flags == NUL) { + // delete a file + rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1; + } else if (STRCMP(flags, "d") == 0) { + // delete an empty directory + rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1; + } else if (STRCMP(flags, "rf") == 0) { + // delete a directory recursively + rettv->vval.v_number = delete_recursive(name); + } else { + EMSG2(_(e_invexpr2), flags); + } } // dictwatcheradd(dict, key, funcref) function @@ -8870,7 +8618,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv) */ static void f_empty(typval_T *argvars, typval_T *rettv) { - int n; + bool n = true; switch (argvars[0].v_type) { case VAR_STRING: @@ -8892,9 +8640,12 @@ static void f_empty(typval_T *argvars, typval_T *rettv) n = argvars[0].vval.v_dict == NULL || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; break; - default: - EMSG2(_(e_intern2), "f_empty()"); - n = 0; + case VAR_SPECIAL: + n = argvars[0].vval.v_special != kSpecialVarTrue; + break; + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); + break; } rettv->vval.v_number = n; @@ -8949,7 +8700,11 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv) */ static void f_executable(typval_T *argvars, typval_T *rettv) { - rettv->vval.v_number = os_can_exe(get_tv_string(&argvars[0]), NULL); + char_u *name = get_tv_string(&argvars[0]); + + // Check in $PATH and also check directly if there is a directory name + rettv->vval.v_number = os_can_exe(name, NULL, true) + || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); } /// "exepath()" function @@ -8958,7 +8713,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv) char_u *arg = get_tv_string(&argvars[0]); char_u *path = NULL; - (void)os_can_exe(arg, &path); + (void)os_can_exe(arg, &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = path; @@ -9007,9 +8762,10 @@ static void f_exists(typval_T *argvars, typval_T *rettv) name = p; len = get_name_len(&p, &tofree, TRUE, FALSE); if (len > 0) { - if (tofree != NULL) + if (tofree != NULL) { name = tofree; - n = (get_var_tv(name, len, &tv, FALSE, TRUE) == OK); + } + n = (get_var_tv(name, len, &tv, NULL, false, true) == OK); if (n) { /* handle d.key, l[idx], f(expr) */ n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK); @@ -9107,6 +8863,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) hashitem_T *hi2; int todo; bool watched = is_watched(d1); + char_u *arg_errmsg = (char_u *)N_("extend() argument"); todo = (int)d2->dv_hashtab.ht_used; for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { @@ -9140,6 +8897,11 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) } else if (*action == 'f' && HI2DI(hi2) != di1) { typval_T oldtv; + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, true) + || var_check_ro(di1->di_flags, arg_errmsg, true)) { + break; + } + if (watched) { copy_tv(&di1->di_tv, &oldtv); } @@ -9162,7 +8924,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) */ static void f_extend(typval_T *argvars, typval_T *rettv) { - char *arg_errmsg = N_("extend() argument"); + char_u *arg_errmsg = (char_u *)N_("extend() argument"); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { list_T *l1, *l2; @@ -9172,7 +8934,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv) l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, (char_u *)_(arg_errmsg)) + if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, true) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); @@ -9202,7 +8964,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv) d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, (char_u *)_(arg_errmsg)) + if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, true) && d2 != NULL) { /* Check the third argument. */ if (argvars[2].v_type != VAR_UNKNOWN) { @@ -9348,19 +9110,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) int rem; int todo; char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); - char *arg_errmsg = (map ? N_("map() argument") - : N_("filter() argument")); + char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") + : N_("filter() argument")); int save_did_emsg; int idx = 0; if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, true))) { return; + } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL - || tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) + || (!map && tv_check_lock(d->dv_lock, arg_errmsg, true))) { return; + } } else { EMSG2(_(e_listdictarg), ermsg); return; @@ -9389,17 +9153,26 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) for (hi = ht->ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { --todo; + di = HI2DI(hi); - if (tv_check_lock(di->di_tv.v_lock, - (char_u *)_(arg_errmsg))) + if (map + && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, true) + || var_check_ro(di->di_flags, arg_errmsg, true))) { break; + } + vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); clear_tv(&vimvars[VV_KEY].vv_tv); if (r == FAIL || did_emsg) break; - if (!map && rem) + if (!map && rem) { + if (var_check_fixed(di->di_flags, arg_errmsg, true) + || var_check_ro(di->di_flags, arg_errmsg, true)) { + break; + } dictitem_remove(d, di); + } } } hash_unlock(ht); @@ -9407,8 +9180,9 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_type = VAR_NUMBER; for (li = l->lv_first; li != NULL; li = nli) { - if (tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) + if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, true)) { break; + } nli = li->li_next; vimvars[VV_KEY].vv_nr = idx; if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL @@ -10047,22 +9821,130 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv) rettv->vval.v_string[0] = cmdwin_type; } -/* - * "getcwd()" function - */ +/// `getcwd([{win}[, {tab}]])` function +/// +/// Every scope not specified implies the currently selected scope object. +/// +/// @pre The arguments must be of type number. +/// @pre There may not be more than two arguments. +/// @pre An argument may not be -1 if preceding arguments are not all -1. +/// +/// @post The return value will be a string. static void f_getcwd(typval_T *argvars, typval_T *rettv) { - char_u *cwd; + // Possible scope of working directory to return. + CdScope scope = MIN_CD_SCOPE; + + // Numbers of the scope objects (window, tab) we want the working directory + // of. A `-1` means to skip this scope, a `0` means the current object. + int scope_number[] = { + [kCdScopeWindow] = 0, // Number of window to look at. + [kCdScopeTab ] = 0, // Number of tab to look at. + }; + + char_u *cwd = NULL; // Current working directory to print + char_u *from = NULL; // The original string to copy + + tabpage_T *tp = curtab; // The tabpage to look at. + win_T *win = curwin; // The window to look at. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + + // Pre-conditions and scope extraction together + for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) { + // If there is no argument there are no more scopes after it, break out. + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + if (argvars[i].v_type != VAR_NUMBER) { + EMSG(_(e_invarg)); + return; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + // It is an error for the scope number to be less than `-1`. + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + return; + } + } + + // Normalize scope, the number of the new scope will be 0. + if (scope_number[scope] < 0) { + // Arguments to `getcwd` always end at second-highest scope, so scope will + // always be <= `MAX_CD_SCOPE`. + scope++; + } + + // Find the tabpage by number + if (scope_number[kCdScopeTab] == -1) { + tp = NULL; + } else if (scope_number[kCdScopeTab] > 0) { + tp = find_tabpage(scope_number[kCdScopeTab]); + if (!tp) { + EMSG(_("E5000: Cannot find tab number.")); + return; + } + } + + // Find the window in `tp` by number, `NULL` if none. + if (scope_number[kCdScopeWindow] == -1) { + win = NULL; + } else if (scope_number[kCdScopeWindow] >= 0) { + if (!tp) { + EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); + return; + } + + if (scope_number[kCdScopeWindow] > 0) { + win = find_win_by_nr(&argvars[0], curtab); + if (!win) { + EMSG(_("E5002: Cannot find window number.")); + return; + } + } + } + cwd = xmalloc(MAXPATHL); - if (os_dirname(cwd, MAXPATHL) != FAIL) { - rettv->vval.v_string = vim_strsave(cwd); + + switch (scope) { + case kCdScopeWindow: + assert(win); + from = win->w_localdir; + if (from) { + break; + } + case kCdScopeTab: // FALLTHROUGH + assert(tp); + from = tp->localdir; + if (from) { + break; + } + case kCdScopeGlobal: // FALLTHROUGH + // The `globaldir` variable is not always set. + if (globaldir) { + from = globaldir; + } else { + // We have to copy the OS path directly into output string. + if (os_dirname(cwd, MAXPATHL) == FAIL) { + EMSG(_("E41: Could not display path.")); + goto end; + } + } + break; + } + + if (from) { + xstrlcpy((char *)cwd, (char *)from, MAXPATHL); + } + + rettv->vval.v_string = vim_strsave(cwd); #ifdef BACKSLASH_IN_FILENAME - slash_adjust(rettv->vval.v_string); + slash_adjust(rettv->vval.v_string); #endif - } +end: xfree(cwd); } @@ -10271,6 +10153,14 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv) dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); dict_add_nr_str(dict, "id", (long)cur->id, NULL); + + if (cur->conceal_char) { + char_u buf[MB_MAXBYTES + 1]; + + buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; + dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); + } + list_append_dict(rettv->vval.v_list, dict); cur = cur->next; } @@ -10305,6 +10195,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { + update_curswant(); list_append_number(l, curwin->w_curswant == MAXCOL ? (varnumber_T)MAXCOL : (varnumber_T)curwin->w_curswant + 1); @@ -10377,9 +10268,10 @@ static void f_getreg(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_LIST; rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); - if (rettv->vval.v_list != NULL) { - rettv->vval.v_list->lv_refcount++; + if (rettv->vval.v_list == NULL) { + rettv->vval.v_list = list_alloc(); } + rettv->vval.v_list->lv_refcount++; } else { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0); @@ -10393,8 +10285,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) { char_u *strregname; int regname; - char_u buf[NUMBUFLEN + 2]; - long reglen = 0; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = get_tv_string_chk(&argvars[0]); @@ -10411,18 +10301,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) if (regname == 0) regname = '"'; - buf[0] = NUL; - buf[1] = NUL; - switch (get_reg_type(regname, ®len)) { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; - case MBLOCK: - buf[0] = Ctrl_V; - sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1)); - break; - } + colnr_T reglen = 0; + char buf[NUMBUFLEN + 2]; + MotionType reg_type = get_reg_type(regname, ®len); + format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf)); + rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = (char_u *)xstrdup(buf); } /* @@ -10442,9 +10327,10 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv) varname = get_tv_string_chk(&argvars[1]); tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { - /* Set tp to be our tabpage, temporarily. Also set the window to the - * first window in the tabpage, otherwise the window is not valid. */ - if (switch_win(&oldcurwin, &oldtabpage, tp->tp_firstwin, tp, TRUE) == OK) { + // Set tp to be our tabpage, temporarily. Also set the window to the + // first window in the tabpage, otherwise the window is not valid. + win_T *window = tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; + if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) { // look up the variable // Let gettabvar({nr}, "") return the "t:" dictionary. v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, FALSE); @@ -10537,11 +10423,11 @@ getwinvar ( int off /* 1 for gettabwinvar() */ ) { - win_T *win, *oldcurwin; - char_u *varname; - dictitem_T *v; - tabpage_T *tp = NULL; - tabpage_T *oldtabpage = NULL; + win_T *win, *oldcurwin; + char_u *varname; + dictitem_T *v; + tabpage_T *tp = NULL; + tabpage_T *oldtabpage = NULL; bool done = false; if (off == 1) @@ -10556,12 +10442,16 @@ getwinvar ( rettv->vval.v_string = NULL; if (win != NULL && varname != NULL) { - /* Set curwin to be our win, temporarily. Also set the tabpage, - * otherwise the window is not valid. */ - if (switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) { - if (*varname == '&') { /* window-local-option */ - if (get_option_tv(&varname, rettv, 1) == OK) + // Set curwin to be our win, temporarily. Also set the tabpage, + // otherwise the window is not valid. Only do this when needed, + // autocommands get blocked. + bool need_switch_win = tp != curtab || win != curwin; + if (!need_switch_win + || switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) { + if (*varname == '&') { // window-local-option + if (get_option_tv(&varname, rettv, 1) == OK) { done = true; + } } else { // Look up the variable. // Let getwinvar({nr}, "") return the "w:" dictionary. @@ -10573,8 +10463,10 @@ getwinvar ( } } - /* restore previous notion of curwin */ - restore_win(oldcurwin, oldtabpage, TRUE); + if (need_switch_win) { + // restore previous notion of curwin + restore_win(oldcurwin, oldtabpage, true); + } } if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) @@ -10599,10 +10491,15 @@ static void f_glob(typval_T *argvars, typval_T *rettv) if (argvars[1].v_type != VAR_UNKNOWN) { if (get_tv_number_chk(&argvars[1], &error)) options |= WILD_KEEP_ALL; - if (argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error)) { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; + if (argvars[2].v_type != VAR_UNKNOWN) { + if (get_tv_number_chk(&argvars[2], &error)) { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + if (argvars[3].v_type != VAR_UNKNOWN + && get_tv_number_chk(&argvars[3], &error)) { + options |= WILD_ALLLINKS; + } } } if (!error) { @@ -10641,10 +10538,15 @@ static void f_globpath(typval_T *argvars, typval_T *rettv) flags |= WILD_KEEP_ALL; } - if (argvars[3].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[3], &error)) { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; + if (argvars[3].v_type != VAR_UNKNOWN) { + if (get_tv_number_chk(&argvars[3], &error)) { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + if (argvars[4].v_type != VAR_UNKNOWN + && get_tv_number_chk(&argvars[4], &error)) { + flags |= WILD_ALLLINKS; + } } } @@ -10671,15 +10573,15 @@ static void f_globpath(typval_T *argvars, typval_T *rettv) } } -/* - * "glob2regpat()" function - */ +// "glob2regpat()" function static void f_glob2regpat(typval_T *argvars, typval_T *rettv) { - char_u *pat = get_tv_string_chk(&argvars[0]); + char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error - rettv->v_type = VAR_STRING; - rettv->vval.v_string = file_pat_to_reg_pat(pat, NULL, NULL, FALSE); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (pat == NULL) + ? NULL + : file_pat_to_reg_pat(pat, NULL, NULL, false); } /* @@ -10779,8 +10681,10 @@ static void f_has(typval_T *argvars, typval_T *rettv) #if !defined(UNIX) "system", // TODO(SplinterOfChaos): This IS defined for UNIX! #endif + "tablineat", "tag_binary", "tag_old_static", + "termguicolors", "termresponse", "textobjects", "title", @@ -10836,8 +10740,6 @@ static void f_has(typval_T *argvars, typval_T *rettv) #endif } else if (STRICMP(name, "syntax_items") == 0) { n = syntax_present(curwin); - } else if (STRICMP(name, "gui_running") == 0) { - n = ui_rgb_attached(); } } @@ -10864,12 +10766,103 @@ static void f_has_key(typval_T *argvars, typval_T *rettv) get_tv_string(&argvars[1]), -1) != NULL; } -/* - * "haslocaldir()" function - */ +/// `haslocaldir([{win}[, {tab}]])` function +/// +/// Returns `1` if the scope object has a local directory, `0` otherwise. If a +/// scope object is not specified the current one is implied. This function +/// share a lot of code with `f_getcwd`. +/// +/// @pre The arguments must be of type number. +/// @pre There may not be more than two arguments. +/// @pre An argument may not be -1 if preceding arguments are not all -1. +/// +/// @post The return value will be either the number `1` or `0`. static void f_haslocaldir(typval_T *argvars, typval_T *rettv) { - rettv->vval.v_number = (curwin->w_localdir != NULL); + // Possible scope of working directory to return. + CdScope scope = MIN_CD_SCOPE; + + // Numbers of the scope objects (window, tab) we want the working directory + // of. A `-1` means to skip this scope, a `0` means the current object. + int scope_number[] = { + [kCdScopeWindow] = 0, // Number of window to look at. + [kCdScopeTab ] = 0, // Number of tab to look at. + }; + + tabpage_T *tp = curtab; // The tabpage to look at. + win_T *win = curwin; // The window to look at. + + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + // Pre-conditions and scope extraction together + for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) { + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + if (argvars[i].v_type != VAR_NUMBER) { + EMSG(_(e_invarg)); + return; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + return; + } + } + + // Normalize scope, the number of the new scope will be 0. + if (scope_number[scope] < 0) { + // Arguments to `haslocaldir` always end at second-highest scope, so scope + // will always be <= `MAX_CD_SCOPE`. + scope++; + } + + // Find the tabpage by number + if (scope_number[kCdScopeTab] == -1) { + tp = NULL; + } else if (scope_number[kCdScopeTab] > 0) { + tp = find_tabpage(scope_number[kCdScopeTab]); + if (!tp) { + EMSG(_("5000: Cannot find tab number.")); + return; + } + } + + // Find the window in `tp` by number, `NULL` if none. + if (scope_number[kCdScopeWindow] == -1) { + win = NULL; + } else if (scope_number[kCdScopeWindow] >= 0) { + if (!tp) { + EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); + return; + } + + if (scope_number[kCdScopeWindow] > 0) { + win = find_win_by_nr(&argvars[0], curtab); + if (!win) { + EMSG(_("E5002: Cannot find window number.")); + return; + } + } + } + + switch (scope) { + case kCdScopeWindow: + assert(win); + rettv->vval.v_number = win->w_localdir ? 1 : 0; + break; + case kCdScopeTab: + assert(tp); + rettv->vval.v_number = tp->localdir ? 1 : 0; + break; + case kCdScopeGlobal: + // The global scope never has a local directory + rettv->vval.v_number = 0; + break; + } } /* @@ -10902,21 +10895,22 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv) */ static void f_histadd(typval_T *argvars, typval_T *rettv) { - int histype; + HistoryType histype; char_u *str; char_u buf[NUMBUFLEN]; - rettv->vval.v_number = FALSE; - if (check_restricted() || check_secure()) + rettv->vval.v_number = false; + if (check_restricted() || check_secure()) { return; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - histype = str != NULL ? get_histtype(str) : -1; - if (histype >= 0) { + } + str = get_tv_string_chk(&argvars[0]); // NULL on type error + histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; + if (histype != HIST_INVALID) { str = get_tv_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); - add_to_history(histype, str, FALSE, NUL); - rettv->vval.v_number = TRUE; + add_to_history(histype, str, false, NUL); + rettv->vval.v_number = true; return; } } @@ -10931,20 +10925,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv) char_u buf[NUMBUFLEN]; char_u *str; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) + str = get_tv_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { n = 0; - else if (argvars[1].v_type == VAR_UNKNOWN) - /* only one argument: clear entire history */ - n = clr_history(get_histtype(str)); - else if (argvars[1].v_type == VAR_NUMBER) - /* index given: remove that entry */ - n = del_history_idx(get_histtype(str), - (int)get_tv_number(&argvars[1])); - else - /* string given: remove all matching entries */ - n = del_history_entry(get_histtype(str), - get_tv_string_buf(&argvars[1], buf)); + } else if (argvars[1].v_type == VAR_UNKNOWN) { + // only one argument: clear entire history + n = clr_history(get_histtype(str, STRLEN(str), false)); + } else if (argvars[1].v_type == VAR_NUMBER) { + // index given: remove that entry + n = del_history_idx(get_histtype(str, STRLEN(str), false), + (int) get_tv_number(&argvars[1])); + } else { + // string given: remove all matching entries + n = del_history_entry(get_histtype(str, STRLEN(str), false), + get_tv_string_buf(&argvars[1], buf)); + } rettv->vval.v_number = n; } @@ -10953,20 +10948,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv) */ static void f_histget(typval_T *argvars, typval_T *rettv) { - int type; + HistoryType type; int idx; char_u *str; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) + str = get_tv_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { rettv->vval.v_string = NULL; - else { - type = get_histtype(str); - if (argvars[1].v_type == VAR_UNKNOWN) + } else { + type = get_histtype(str, STRLEN(str), false); + if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); - else + } else { idx = (int)get_tv_number_chk(&argvars[1], NULL); - /* -1 on type error */ + } + // -1 on type error rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); } rettv->v_type = VAR_STRING; @@ -10981,11 +10977,13 @@ static void f_histnr(typval_T *argvars, typval_T *rettv) char_u *history = get_tv_string_chk(&argvars[0]); - i = history == NULL ? HIST_CMD - 1 : get_histtype(history); - if (i >= HIST_CMD && i < HIST_COUNT) + i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history), + false); + if (i != HIST_INVALID) { i = get_history_idx(i); - else + } else { i = -1; + } rettv->vval.v_number = i; } @@ -11292,14 +11290,18 @@ static void f_insert(typval_T *argvars, typval_T *rettv) list_T *l; int error = FALSE; - if (argvars[0].v_type != VAR_LIST) + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("insert() argument"))) { - if (argvars[2].v_type != VAR_UNKNOWN) + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("insert() argument"), true)) { + if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ + } + if (error) { + // type error; errmsg already given + return; + } if (before == l->lv_len) item = NULL; @@ -11493,6 +11495,31 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv) } } +// "jobpid(id)" function +static void f_jobpid(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER) { + EMSG(_(e_invarg)); + return; + } + + TerminalJobData *data = find_job(argvars[0].vval.v_number); + if (!data) { + EMSG(_(e_invjob)); + return; + } + + Process *proc = (Process *)&data->proc; + rettv->vval.v_number = proc->pid; +} + // "jobsend()" function static void f_jobsend(typval_T *argvars, typval_T *rettv) { @@ -11592,7 +11619,7 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd) assert(argl->lv_first); const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv); - if (!exe || !os_can_exe(exe, NULL)) { + if (!exe || !os_can_exe(exe, NULL, true)) { // String is not executable if (exe) { EMSG2(e_jobexe, exe); @@ -11653,8 +11680,9 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv) } bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0; + bool detach = job_opts && get_dict_number(job_opts, (uint8_t *)"detach") != 0; TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - job_opts, pty); + job_opts, pty, detach); Process *proc = (Process *)&data->proc; if (pty) { @@ -11847,6 +11875,47 @@ static void f_join(typval_T *argvars, typval_T *rettv) rettv->vval.v_string = NULL; } +/// json_decode() function +static void f_json_decode(typval_T *argvars, typval_T *rettv) +{ + char numbuf[NUMBUFLEN]; + char *s = NULL; + char *tofree = NULL; + size_t len; + if (argvars[0].v_type == VAR_LIST) { + if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) { + EMSG(_("E474: Failed to convert list to string")); + return; + } + tofree = s; + if (s == NULL) { + assert(len == 0); + s = ""; + } + } else { + s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf); + if (s) { + len = strlen(s); + } else { + return; + } + } + if (json_decode_string(s, len, rettv) == FAIL) { + emsgf(_("E474: Failed to parse %.*s"), (int) len, s); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + } + assert(rettv->v_type != VAR_UNKNOWN); + xfree(tofree); +} + +/// json_encode() function +static void f_json_encode(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *) encode_tv2json(&argvars[0], NULL); +} + /* * "keys()" function */ @@ -11888,7 +11957,10 @@ static void f_len(typval_T *argvars, typval_T *rettv) case VAR_DICT: rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); break; - default: + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_FUNC: EMSG(_("E701: Invalid type for len()")); break; } @@ -12048,8 +12120,9 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mode = get_map_mode(&which, 0); - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); - rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, + CPO_TO_CPO_FLAGS); + rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local); xfree(keys_buf); if (!get_dict) { @@ -12211,9 +12284,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) break; } xfree(tofree); - tofree = str = (char_u *) echo_string(&li->li_tv, NULL); - if (str == NULL) + tofree = str = (char_u *) encode_tv2echo(&li->li_tv, NULL); + if (str == NULL) { break; + } } match = vim_regexec_nl(®match, str, (colnr_T)startcol); @@ -12296,7 +12370,8 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv) char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ int prio = 10; /* default priority */ int id = -1; - int error = FALSE; + int error = false; + char_u *conceal_char = NULL; rettv->vval.v_number = -1; @@ -12304,17 +12379,31 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv) return; if (argvars[2].v_type != VAR_UNKNOWN) { prio = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) + if (argvars[3].v_type != VAR_UNKNOWN) { id = get_tv_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + if (dict_find(argvars[4].vval.v_dict, + (char_u *)"conceal", -1) != NULL) { + conceal_char = get_dict_string(argvars[4].vval.v_dict, + (char_u *)"conceal", false); + } + } + } } - if (error == TRUE) + if (error == true) { return; + } if (id >= 1 && id <= 3) { EMSGN("E798: ID is reserved for \":match\": %" PRId64, id); return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL); + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, + conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -12342,12 +12431,24 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ int error = false; int prio = 10; int id = -1; + char_u *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + prio = get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = get_tv_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + if (dict_find(argvars[4].vval.v_dict, + (char_u *)"conceal", -1) != NULL) { + conceal_char = get_dict_string(argvars[4].vval.v_dict, + (char_u *)"conceal", false); + } } + } } if (error == true) { return; @@ -12359,7 +12460,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ return; } - rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l); + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, + conceal_char); } /* @@ -12583,220 +12685,6 @@ static void f_mode(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_STRING; } -/// Msgpack callback for writing to readfile()-style list -static int msgpack_list_write(void *data, const char *buf, size_t len) -{ - if (len == 0) { - return 0; - } - list_T *const list = (list_T *) data; - const char *const end = buf + len; - const char *line_end = buf; - if (list->lv_last == NULL) { - list_append_string(list, NULL, 0); - } - listitem_T *li = list->lv_last; - do { - const char *line_start = line_end; - line_end = xmemscan(line_start, NL, end - line_start); - if (line_end == line_start) { - list_append_allocated_string(list, NULL); - } else { - const size_t line_length = line_end - line_start; - char *str; - if (li == NULL) { - str = xmemdupz(line_start, line_length); - } else { - const size_t li_len = (li->li_tv.vval.v_string == NULL - ? 0 - : STRLEN(li->li_tv.vval.v_string)); - li->li_tv.vval.v_string = xrealloc(li->li_tv.vval.v_string, - li_len + line_length + 1); - str = (char *) li->li_tv.vval.v_string + li_len; - memmove(str, line_start, line_length); - str[line_length] = 0; - } - for (size_t i = 0; i < line_length; i++) { - if (str[i] == NUL) { - str[i] = NL; - } - } - if (li == NULL) { - list_append_allocated_string(list, str); - } else { - li = NULL; - } - if (line_end == end - 1) { - list_append_allocated_string(list, NULL); - } - } - line_end++; - } while (line_end < end); - return 0; -} - -/// Convert readfile()-style list to a char * buffer with length -/// -/// @param[in] list Converted list. -/// @param[out] ret_len Resulting buffer length. -/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is -/// zero. -/// -/// @return true in case of success, false in case of failure. -static inline bool vim_list_to_buf(const list_T *const list, - size_t *const ret_len, char **const ret_buf) - FUNC_ATTR_NONNULL_ARG(2,3) FUNC_ATTR_WARN_UNUSED_RESULT -{ - size_t len = 0; - if (list != NULL) { - for (const listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { - return false; - } - len++; - if (li->li_tv.vval.v_string != 0) { - len += STRLEN(li->li_tv.vval.v_string); - } - } - if (len) { - len--; - } - } - *ret_len = len; - if (len == 0) { - *ret_buf = NULL; - return true; - } - ListReaderState lrstate = init_lrstate(list); - char *const buf = xmalloc(len); - size_t read_bytes; - if (read_from_list(&lrstate, buf, len, &read_bytes) != OK) { - assert(false); - } - assert(len == read_bytes); - *ret_buf = buf; - return true; -} - -#define CONV_STRING(buf, len) \ - do { \ - if (buf == NULL) { \ - msgpack_pack_bin(packer, 0); \ - } else { \ - const size_t len_ = (len); \ - msgpack_pack_bin(packer, len_); \ - msgpack_pack_bin_body(packer, buf, len_); \ - } \ - } while (0) - -#define CONV_STR_STRING(buf, len) \ - do { \ - if (buf == NULL) { \ - msgpack_pack_str(packer, 0); \ - } else { \ - const size_t len_ = (len); \ - msgpack_pack_str(packer, len_); \ - msgpack_pack_str_body(packer, buf, len_); \ - } \ - } while (0) - -#define CONV_EXT_STRING(buf, len, type) \ - do { \ - if (buf == NULL) { \ - msgpack_pack_ext(packer, 0, type); \ - } else { \ - const size_t len_ = (len); \ - msgpack_pack_ext(packer, len_, (int8_t) type); \ - msgpack_pack_ext_body(packer, buf, len_); \ - } \ - } while (0) - -#define CONV_NUMBER(num) \ - msgpack_pack_int64(packer, (int64_t) (num)) - -#define CONV_FLOAT(flt) \ - msgpack_pack_double(packer, (double) (flt)) - -#define CONV_FUNC(fun) \ - do { \ - EMSG2(_(e_invarg2), "attempt to dump function reference"); \ - return FAIL; \ - } while (0) - -#define CONV_EMPTY_LIST() \ - msgpack_pack_array(packer, 0) - -#define CONV_LIST_START(lst) \ - msgpack_pack_array(packer, (lst)->lv_len) - -#define CONV_EMPTY_DICT() \ - msgpack_pack_map(packer, 0) - -#define CONV_SPECIAL_NIL() \ - msgpack_pack_nil(packer) - -#define CONV_SPECIAL_BOOL(num) \ - do { \ - if ((num)) { \ - msgpack_pack_true(packer); \ - } else { \ - msgpack_pack_false(packer); \ - } \ - } while (0) - -#define CONV_UNSIGNED_NUMBER(num) \ - msgpack_pack_uint64(packer, (num)) - -#define CONV_SPECIAL_MAP_START(lst) \ - msgpack_pack_map(packer, (lst)->lv_len) - -#define CONV_DICT_START(dct) \ - msgpack_pack_map(packer, (dct)->dv_hashtab.ht_used) - -#define CONV_DICT_END(dct) - -#define CONV_DICT_AFTER_KEY(dct) - -#define CONV_DICT_BETWEEN_ITEMS(dct) - -#define CONV_LIST_END(lst) - -#define CONV_LIST_BETWEEN_ITEMS(lst) - -#define CONV_RECURSE(val, conv_type) \ - do { \ - EMSG2(_(e_invarg2), "container references itself"); \ - return FAIL; \ - } while (0) - -#define CONV_ALLOW_SPECIAL true - -DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_SPECIAL_NIL -#undef CONV_SPECIAL_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_SPECIAL_MAP_START -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL - /// "msgpackdump()" function static void f_msgpackdump(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -12810,309 +12698,21 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv) if (list == NULL) { return; } - msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write); + msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); + const char *const msg = _("msgpackdump() argument, index %i"); + // Assume that translation will not take more then 4 times more space + char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; + int idx = 0; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - if (vim_to_msgpack(lpacker, &li->li_tv) == FAIL) { + vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx); + idx++; + if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) { break; } } msgpack_packer_free(lpacker); } -/// Read bytes from list -/// -/// @param[in,out] state Structure describing position in list from which -/// reading should start. Is updated to reflect position -/// at which reading ended. -/// @param[out] buf Buffer to write to. -/// @param[in] nbuf Buffer length. -/// @param[out] read_bytes Is set to amount of bytes read. -/// -/// @return OK when reading was finished, FAIL in case of error (i.e. list item -/// was not a string), NOTDONE if reading was successfull, but there are -/// more bytes to read. -static int read_from_list(ListReaderState *const state, char *const buf, - const size_t nbuf, size_t *const read_bytes) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - char *const buf_end = buf + nbuf; - char *p = buf; - while (p < buf_end) { - for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { - const char ch = state->li->li_tv.vval.v_string[state->offset++]; - *p++ = (ch == NL ? NUL : ch); - } - if (p < buf_end) { - state->li = state->li->li_next; - if (state->li == NULL) { - *read_bytes = (size_t) (p - buf); - return OK; - } - *p++ = NL; - if (state->li->li_tv.v_type != VAR_STRING) { - *read_bytes = (size_t) (p - buf); - return FAIL; - } - state->offset = 0; - state->li_length = (state->li->li_tv.vval.v_string == NULL - ? 0 - : STRLEN(state->li->li_tv.vval.v_string)); - } - } - *read_bytes = nbuf; - return (state->offset < state->li_length || state->li->li_next != NULL - ? NOTDONE - : OK); -} - -/// Initialize ListReaderState structure -static inline ListReaderState init_lrstate(const list_T *const list) - FUNC_ATTR_NONNULL_ALL -{ - return (ListReaderState) { - .li = list->lv_first, - .offset = 0, - .li_length = (list->lv_first->li_tv.vval.v_string == NULL - ? 0 - : STRLEN(list->lv_first->li_tv.vval.v_string)), - }; -} - -/// Convert msgpack object to a VimL one -int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ -#define INIT_SPECIAL_DICT(tv, type, val) \ - do { \ - dict_T *const dict = dict_alloc(); \ - dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); \ - type_di->di_tv.v_type = VAR_LIST; \ - type_di->di_tv.v_lock = 0; \ - type_di->di_tv.vval.v_list = (list_T *) msgpack_type_lists[type]; \ - type_di->di_tv.vval.v_list->lv_refcount++; \ - dict_add(dict, type_di); \ - dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); \ - val_di->di_tv = val; \ - dict_add(dict, val_di); \ - tv->v_type = VAR_DICT; \ - dict->dv_refcount++; \ - tv->vval.v_dict = dict; \ - } while (0) - switch (mobj.type) { - case MSGPACK_OBJECT_NIL: { - INIT_SPECIAL_DICT(rettv, kMPNil, ((typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { .v_number = 0 }, - })); - break; - } - case MSGPACK_OBJECT_BOOLEAN: { - INIT_SPECIAL_DICT(rettv, kMPBoolean, - ((typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { - .v_number = (varnumber_T) mobj.via.boolean, - }, - })); - break; - } - case MSGPACK_OBJECT_POSITIVE_INTEGER: { - if (mobj.via.u64 <= VARNUMBER_MAX) { - *rettv = (typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { .v_number = (varnumber_T) mobj.via.u64 }, - }; - } else { - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPInteger, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - uint64_t n = mobj.via.u64; - list_append_number(list, 1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); - } - break; - } - case MSGPACK_OBJECT_NEGATIVE_INTEGER: { - if (mobj.via.i64 >= VARNUMBER_MIN) { - *rettv = (typval_T) { - .v_type = VAR_NUMBER, - .v_lock = 0, - .vval = { .v_number = (varnumber_T) mobj.via.i64 }, - }; - } else { - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPInteger, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - uint64_t n = -((uint64_t) mobj.via.i64); - list_append_number(list, -1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); - } - break; - } - case MSGPACK_OBJECT_FLOAT: { - *rettv = (typval_T) { - .v_type = VAR_FLOAT, - .v_lock = 0, - .vval = { .v_float = mobj.via.f64 }, - }; - break; - } - case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPString, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - if (msgpack_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size) - == -1) { - return FAIL; - } - break; - } - case MSGPACK_OBJECT_BIN: { - if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) { - *rettv = (typval_T) { - .v_type = VAR_STRING, - .v_lock = 0, - .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) }, - }; - break; - } - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPBinary, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - if (msgpack_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size) - == -1) { - return FAIL; - } - break; - } - case MSGPACK_OBJECT_ARRAY: { - list_T *const list = list_alloc(); - list->lv_refcount++; - *rettv = (typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - }; - for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = listitem_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; - list_append(list, li); - if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { - return FAIL; - } - } - break; - } - case MSGPACK_OBJECT_MAP: { - for (size_t i = 0; i < mobj.via.map.size; i++) { - if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR - || mobj.via.map.ptr[i].key.via.str.size == 0 - || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL, - mobj.via.map.ptr[i].key.via.str.size) != NULL) { - goto msgpack_to_vim_generic_map; - } - } - dict_T *const dict = dict_alloc(); - dict->dv_refcount++; - *rettv = (typval_T) { - .v_type = VAR_DICT, - .v_lock = 0, - .vval = { .v_dict = dict }, - }; - for (size_t i = 0; i < mobj.via.map.size; i++) { - dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key) - + mobj.via.map.ptr[i].key.via.str.size); - memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, - mobj.via.map.ptr[i].key.via.str.size); - di->di_tv.v_type = VAR_UNKNOWN; - if (dict_add(dict, di) == FAIL) { - // Duplicate key: fallback to generic map - clear_tv(rettv); - xfree(di); - goto msgpack_to_vim_generic_map; - } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) { - return FAIL; - } - } - break; -msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); - list->lv_refcount++; - INIT_SPECIAL_DICT(rettv, kMPMap, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = list_alloc(); - list_append_list(list, kv_pair); - listitem_T *const key_li = listitem_alloc(); - key_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); - val_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, val_li); - if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { - return FAIL; - } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { - return FAIL; - } - } - break; - } - case MSGPACK_OBJECT_EXT: { - list_T *const list = list_alloc(); - list->lv_refcount++; - list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = list_alloc(); - list_append_list(list, ext_val_list); - INIT_SPECIAL_DICT(rettv, kMPExt, - ((typval_T) { - .v_type = VAR_LIST, - .v_lock = 0, - .vval = { .v_list = list }, - })); - if (msgpack_list_write((void *) ext_val_list, mobj.via.ext.ptr, - mobj.via.ext.size) == -1) { - return FAIL; - } - break; - } - } -#undef INIT_SPECIAL_DICT - return OK; -} - /// "msgpackparse" function static void f_msgpackparse(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -13130,7 +12730,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv) EMSG2(_(e_invarg2), "List item is not a string"); return; } - ListReaderState lrstate = init_lrstate(list); + ListReaderState lrstate = encode_init_lrstate(list); msgpack_unpacker *const unpacker = msgpack_unpacker_new(IOSIZE); if (unpacker == NULL) { EMSG(_(e_outofmem)); @@ -13144,7 +12744,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv) goto f_msgpackparse_exit; } size_t read_bytes; - const int rlret = read_from_list( + const int rlret = encode_read_from_list( &lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes); if (rlret == FAIL) { EMSG2(_(e_invarg2), "List item is not a string"); @@ -13652,19 +13252,20 @@ static void f_remove(typval_T *argvars, typval_T *rettv) char_u *key; dict_T *d; dictitem_T *di; - char *arg_errmsg = N_("remove() argument"); + char_u *arg_errmsg = (char_u *)N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { EMSG2(_(e_toomanyarg), "remove()"); - else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) { + } else if ((d = argvars[0].vval.v_dict) != NULL + && !tv_check_lock(d->dv_lock, arg_errmsg, true)) { key = get_tv_string_chk(&argvars[1]); if (key != NULL) { di = dict_find(d, key, -1); - if (di == NULL) + if (di == NULL) { EMSG2(_(e_dictkey), key); - else { + } else if (!var_check_fixed(di->di_flags, arg_errmsg, true) + && !var_check_ro(di->di_flags, arg_errmsg, true)) { *rettv = di->di_tv; init_tv(&di->di_tv); dictitem_remove(d, di); @@ -13674,11 +13275,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv) } } } - } else if (argvars[0].v_type != VAR_LIST) + } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) { - int error = FALSE; + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, arg_errmsg, true)) { + int error = (int)false; idx = get_tv_number_chk(&argvars[1], &error); if (error) @@ -13952,10 +13553,11 @@ static void f_reverse(typval_T *argvars, typval_T *rettv) list_T *l; listitem_T *li, *ni; - if (argvars[0].v_type != VAR_LIST) + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("reverse() argument"))) { + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("reverse() argument"), true)) { li = l->lv_last; l->lv_first = l->lv_last = NULL; l->lv_len = 0; @@ -13971,14 +13573,14 @@ static void f_reverse(typval_T *argvars, typval_T *rettv) } } -#define SP_NOMOVE 0x01 /* don't move cursor */ -#define SP_REPEAT 0x02 /* repeat to find outer pair */ -#define SP_RETCOUNT 0x04 /* return matchcount */ -#define SP_SETPCMARK 0x08 /* set previous context mark */ -#define SP_START 0x10 /* accept match at start position */ -#define SP_SUBPAT 0x20 /* return nr of matching sub-pattern */ -#define SP_END 0x40 /* leave cursor at end of match */ - +#define SP_NOMOVE 0x01 ///< don't move cursor +#define SP_REPEAT 0x02 ///< repeat to find outer pair +#define SP_RETCOUNT 0x04 ///< return matchcount +#define SP_SETPCMARK 0x08 ///< set previous context mark +#define SP_START 0x10 ///< accept match at start position +#define SP_SUBPAT 0x20 ///< return nr of matching sub-pattern +#define SP_END 0x40 ///< leave cursor at end of match +#define SP_COLUMN 0x80 ///< start at cursor column /* * Get flags for a search function. @@ -14004,13 +13606,14 @@ static int get_search_arg(typval_T *varp, int *flagsp) default: mask = 0; if (flagsp != NULL) switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; } if (mask == 0) { EMSG2(_(e_invarg2), flags); @@ -14026,9 +13629,7 @@ static int get_search_arg(typval_T *varp, int *flagsp) return dir; } -/* - * Shared by search() and searchpos() functions - */ +// Shared by search() and searchpos() functions. static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; @@ -14049,10 +13650,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) if (dir == 0) goto theend; flags = *flagsp; - if (flags & SP_START) + if (flags & SP_START) { options |= SEARCH_START; - if (flags & SP_END) + } + if (flags & SP_END) { options |= SEARCH_END; + } + if (flags & SP_COLUMN) { + options |= SEARCH_COL; + } /* Optional arguments: line number to stop searching and timeout. */ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { @@ -14904,33 +14510,64 @@ static void f_setline(typval_T *argvars, typval_T *rettv) appended_lines_mark(lcount, added); } - -/* - * Used by "setqflist()" and "setloclist()" functions - */ -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv) +/// Create quickfix/location list from VimL values +/// +/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid +/// list_arg, action_arg and title_arg arguments in which case errors out, +/// including VAR_UNKNOWN parameters. +/// +/// @param[in,out] wp Window to create location list for. May be NULL in +/// which case quickfix list will be created. +/// @param[in] list_arg Quickfix list contents. +/// @param[in] action_arg Action to perform: append to an existing list, +/// replace its content or create a new one. +/// @param[in] title_arg New list title. Defaults to caller function name. +/// @param[out] rettv Return value: 0 in case of success, -1 otherwise. +static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(2, 3) { - char_u *act; + char_u *title = NULL; int action = ' '; - rettv->vval.v_number = -1; - if (list_arg->v_type != VAR_LIST) + typval_T *list_arg = &args[0]; + if (list_arg->v_type != VAR_LIST) { EMSG(_(e_listreq)); - else { - list_T *l = list_arg->vval.v_list; + return; + } - if (action_arg->v_type == VAR_STRING) { - act = get_tv_string_chk(action_arg); - if (act == NULL) - return; /* type error; errmsg already given */ - if (*act == 'a' || *act == 'r') - action = *act; - } + typval_T *action_arg = &args[1]; + if (action_arg->v_type == VAR_UNKNOWN) { + // Option argument was not given. + goto skip_args; + } else if (action_arg->v_type != VAR_STRING) { + EMSG(_(e_strreq)); + return; + } + char_u *act = get_tv_string_chk(action_arg); + if (*act == 'a' || *act == 'r') { + action = *act; + } - if (l != NULL && set_errorlist(wp, l, action, - (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK) - rettv->vval.v_number = 0; + typval_T *title_arg = &args[2]; + if (title_arg->v_type == VAR_UNKNOWN) { + // Option argument was not given. + goto skip_args; + } + title = get_tv_string_chk(title_arg); + if (!title) { + // Type error. Error already printed by get_tv_string_chk(). + return; + } + +skip_args: + if (!title) { + title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + } + + list_T *l = list_arg->vval.v_list; + if (l && set_errorlist(wp, l, action, title) == OK) { + rettv->vval.v_number = 0; } } @@ -14944,8 +14581,9 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = -1; win = find_win_by_nr(&argvars[0], NULL); - if (win != NULL) - set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); + if (win != NULL) { + set_qf_ll_list(win, &argvars[1], rettv); + } } /* @@ -14956,6 +14594,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) list_T *l; listitem_T *li; dict_T *d; + list_T *s = NULL; rettv->vval.v_number = -1; if (argvars[0].v_type != VAR_LIST) { @@ -14974,7 +14613,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) return; } if (!(dict_find(d, (char_u *)"group", -1) != NULL - && dict_find(d, (char_u *)"pattern", -1) != NULL + && (dict_find(d, (char_u *)"pattern", -1) != NULL + || dict_find(d, (char_u *)"pos1", -1) != NULL) && dict_find(d, (char_u *)"priority", -1) != NULL && dict_find(d, (char_u *)"id", -1) != NULL)) { EMSG(_(e_invarg)); @@ -14986,11 +14626,51 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) clear_matches(curwin); li = l->lv_first; while (li != NULL) { + int i = 0; + char_u buf[5]; + dictitem_T *di; + d = li->li_tv.vval.v_dict; - match_add(curwin, get_dict_string(d, (char_u *)"group", FALSE), - get_dict_string(d, (char_u *)"pattern", FALSE), - (int)get_dict_number(d, (char_u *)"priority"), - (int)get_dict_number(d, (char_u *)"id"), NULL); + if (dict_find(d, (char_u *)"pattern", -1) == NULL) { + if (s == NULL) { + s = list_alloc(); + if (s == NULL) { + return; + } + } + + // match from matchaddpos() + for (i = 1; i < 9; ++i) { + snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i); + if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) { + if (di->di_tv.v_type != VAR_LIST) { + return; + } + + list_append_tv(s, &di->di_tv); + s->lv_refcount++; + } else { + break; + } + } + } + + char_u *group = get_dict_string(d, (char_u *)"group", false); + int priority = get_dict_number(d, (char_u *)"priority"); + int id = get_dict_number(d, (char_u *)"id"); + char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL + ? get_dict_string(d, (char_u *)"conceal", + false) + : NULL; + if (i == 0) { + match_add(curwin, group, + get_dict_string(d, (char_u *)"pattern", false), + priority, id, NULL, conceal); + } else { + match_add(curwin, group, NULL, priority, id, s, conceal); + list_unref(s); + s = NULL; + } li = li->li_next; } rettv->vval.v_number = 0; @@ -15011,25 +14691,30 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) name = get_tv_string_chk(argvars); if (name != NULL) { if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) { - if (--pos.col < 0) + if (--pos.col < 0) { pos.col = 0; + } if (name[0] == '.' && name[1] == NUL) { - /* set cursor */ + // set cursor if (fnum == curbuf->b_fnum) { curwin->w_cursor = pos; if (curswant >= 0) { curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = false; } check_cursor(); rettv->vval.v_number = 0; - } else + } else { EMSG(_(e_invarg)); + } } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { - /* set mark */ - if (setmark_pos(name[1], &pos, fnum) == OK) + // set mark + if (setmark_pos(name[1], &pos, fnum) == OK) { rettv->vval.v_number = 0; - } else + } + } else { EMSG(_(e_invarg)); + } } } } @@ -15039,7 +14724,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) */ static void f_setqflist(typval_T *argvars, typval_T *rettv) { - set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv); + set_qf_ll_list(NULL, argvars, rettv); } /* @@ -15051,11 +14736,11 @@ static void f_setreg(typval_T *argvars, typval_T *rettv) char_u *strregname; char_u *stropt; bool append = false; - char_u yank_type; + MotionType yank_type; long block_len; block_len = -1; - yank_type = MAUTO; + yank_type = kMTUnknown; strregname = get_tv_string_chk(argvars); rettv->vval.v_number = 1; /* FAIL is default */ @@ -15072,17 +14757,17 @@ static void f_setreg(typval_T *argvars, typval_T *rettv) return; /* type error */ for (; *stropt != NUL; ++stropt) switch (*stropt) { - case 'a': case 'A': /* append */ + case 'a': case 'A': // append append = true; break; - case 'v': case 'c': /* character-wise selection */ - yank_type = MCHAR; + case 'v': case 'c': // character-wise selection + yank_type = kMTCharWise; break; - case 'V': case 'l': /* line-wise selection */ - yank_type = MLINE; + case 'V': case 'l': // line-wise selection + yank_type = kMTLineWise; break; - case 'b': case Ctrl_V: /* block-wise selection */ - yank_type = MBLOCK; + case 'b': case Ctrl_V: // block-wise selection + yank_type = kMTBlockWise; if (ascii_isdigit(stropt[1])) { ++stropt; block_len = getdigits_long(&stropt) - 1; @@ -15213,26 +14898,32 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) varname = get_tv_string_chk(&argvars[off + 1]); varp = &argvars[off + 2]; - if (win != NULL && varname != NULL && varp != NULL - && switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) { - if (*varname == '&') { - long numval; - char_u *strval; - int error = FALSE; - - ++varname; - numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) - set_option_value(varname, numval, strval, OPT_LOCAL); - } else { - winvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); - set_var(winvarname, varp, TRUE); - xfree(winvarname); + if (win != NULL && varname != NULL && varp != NULL) { + bool need_switch_win = tp != curtab || win != curwin; + if (!need_switch_win + || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { + if (*varname == '&') { + long numval; + char_u *strval; + int error = false; + + ++varname; + numval = get_tv_number_chk(varp, &error); + strval = get_tv_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } + } else { + winvarname = xmalloc(STRLEN(varname) + 3); + STRCPY(winvarname, "w:"); + STRCPY(winvarname + 2, varname); + set_var(winvarname, varp, true); + xfree(winvarname); + } + } + if (need_switch_win) { + restore_win(save_curwin, save_curtab, true); } - restore_win(save_curwin, save_curtab, TRUE); } } @@ -15302,6 +14993,8 @@ typedef struct { static int item_compare_ic; static bool item_compare_numeric; +static bool item_compare_numbers; +static bool item_compare_float; static char_u *item_compare_func; static dict_T *item_compare_selfdict; static int item_compare_func_err; @@ -15323,9 +15016,24 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) si2 = (sortItem_T *)s2; typval_T *tv1 = &si1->item->li_tv; typval_T *tv2 = &si2->item->li_tv; - // tv2string() puts quotes around a string and allocates memory. Don't do - // that for string variables. Use a single quote when comparing with a - // non-string to do what the docs promise. + + if (item_compare_numbers) { + long v1 = get_tv_number(tv1); + long v2 = get_tv_number(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + + if (item_compare_float) { + float_T v1 = get_tv_float(tv1); + float_T v2 = get_tv_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + + // encode_tv2string() puts quotes around a string and allocates memory. Don't + // do that for string variables. Use a single quote when comparing with + // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { if (tv2->v_type != VAR_STRING || item_compare_numeric) { p1 = (char_u *)"'"; @@ -15333,7 +15041,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) p1 = tv1->vval.v_string; } } else { - tofree1 = p1 = (char_u *) tv2string(tv1, NULL); + tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); } if (tv2->v_type == VAR_STRING) { if (tv1->v_type != VAR_STRING || item_compare_numeric) { @@ -15342,7 +15050,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) p2 = tv2->vval.v_string; } } else { - tofree2 = p2 = (char_u *) tv2string(tv2, NULL); + tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); } if (p1 == NULL) p1 = (char_u *)""; @@ -15451,8 +15159,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, - (char_u *)(sort ? _("sort() argument") : _("uniq() argument")))) { + if (l == NULL + || tv_check_lock(l->lv_lock, + (char_u *)(sort + ? N_("sort() argument") + : N_("uniq() argument")), + true)) { return; } rettv->vval.v_list = l; @@ -15465,6 +15177,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) item_compare_ic = FALSE; item_compare_numeric = false; + item_compare_numbers = false; + item_compare_float = false; item_compare_func = NULL; item_compare_selfdict = NULL; @@ -15486,6 +15200,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (STRCMP(item_compare_func, "n") == 0) { item_compare_func = NULL; item_compare_numeric = true; + } else if (STRCMP(item_compare_func, "N") == 0) { + item_compare_func = NULL; + item_compare_numbers = true; + } else if (STRCMP(item_compare_func, "f") == 0) { + item_compare_func = NULL; + item_compare_float = true; } else if (STRCMP(item_compare_func, "i") == 0) { item_compare_func = NULL; item_compare_ic = TRUE; @@ -15590,6 +15310,21 @@ static void f_uniq(typval_T *argvars, typval_T *rettv) do_sort_uniq(argvars, rettv, false); } +// +// "reltimefloat()" function +// +static void f_reltimefloat(typval_T *argvars , typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + proftime_T tm; + + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = 0; + if (list2proftime(&argvars[0], &tm) == OK) { + rettv->vval.v_float = ((float_T)tm) / 1000000000; + } +} + /* * "soundfold({word})" function */ @@ -15774,31 +15509,44 @@ static void f_str2float(typval_T *argvars, typval_T *rettv) if (*p == '+') p = skipwhite(p + 1); - (void)string2float(p, &rettv->vval.v_float); + (void) string2float((char *) p, &rettv->vval.v_float); rettv->v_type = VAR_FLOAT; } -/* - * "str2nr()" function - */ +// "str2nr()" function static void f_str2nr(typval_T *argvars, typval_T *rettv) { int base = 10; char_u *p; long n; + int what; if (argvars[1].v_type != VAR_UNKNOWN) { base = get_tv_number(&argvars[1]); - if (base != 8 && base != 10 && base != 16) { + if (base != 2 && base != 8 && base != 10 && base != 16) { EMSG(_(e_invarg)); return; } } p = skipwhite(get_tv_string(&argvars[0])); - if (*p == '+') + if (*p == '+') { p = skipwhite(p + 1); - vim_str2nr(p, NULL, NULL, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL); + } + switch (base) { + case 2: + what = STR2NR_BIN + STR2NR_FORCE; + break; + case 8: + what = STR2NR_OCT + STR2NR_FORCE; + break; + case 16: + what = STR2NR_HEX + STR2NR_FORCE; + break; + default: + what = 0; + } + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); rettv->vval.v_number = n; } @@ -15892,7 +15640,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv) static void f_string(typval_T *argvars, typval_T *rettv) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char_u *) tv2string(&argvars[0], NULL); + rettv->vval.v_string = (char_u *) encode_tv2string(&argvars[0], NULL); } /* @@ -15910,13 +15658,23 @@ static void f_strlen(typval_T *argvars, typval_T *rettv) static void f_strchars(typval_T *argvars, typval_T *rettv) { char_u *s = get_tv_string(&argvars[0]); + int skipcc = 0; varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(char_u **pp); - while (*s != NUL) { - mb_cptr2char_adv(&s); - ++len; + if (argvars[1].v_type != VAR_UNKNOWN) { + skipcc = get_tv_number_chk(&argvars[1], NULL); + } + if (skipcc < 0 || skipcc > 1) { + EMSG(_(e_invarg)); + } else { + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) { + func_mb_ptr2char_adv(&s); + ++len; + } + rettv->vval.v_number = len; } - rettv->vval.v_number = len; } /* @@ -16583,7 +16341,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) } TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - job_opts, true); + job_opts, true, false); data->proc.pty.width = curwin->w_width; data->proc.pty.height = curwin->w_height; data->proc.pty.term_name = xstrdup("xterm-256color"); @@ -16615,9 +16373,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) // Save the job id and pid in b:terminal_job_{id,pid} Error err; dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ(rettv->vval.v_number), &err); + INTEGER_OBJ(rettv->vval.v_number), false, &err); dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_pid"), - INTEGER_OBJ(pid), &err); + INTEGER_OBJ(pid), false, &err); Terminal *term = terminal_open(topts); data->term = term; @@ -16803,16 +16561,33 @@ static void f_trunc(typval_T *argvars, typval_T *rettv) */ static void f_type(typval_T *argvars, typval_T *rettv) { - int n; + int n = -1; switch (argvars[0].v_type) { - case VAR_NUMBER: n = 0; break; - case VAR_STRING: n = 1; break; - case VAR_FUNC: n = 2; break; - case VAR_LIST: n = 3; break; - case VAR_DICT: n = 4; break; - case VAR_FLOAT: n = 5; break; - default: EMSG2(_(e_intern2), "f_type()"); n = 0; break; + case VAR_NUMBER: n = 0; break; + case VAR_STRING: n = 1; break; + case VAR_FUNC: n = 2; break; + case VAR_LIST: n = 3; break; + case VAR_DICT: n = 4; break; + case VAR_FLOAT: n = 5; break; + case VAR_SPECIAL: { + switch (argvars[0].vval.v_special) { + case kSpecialVarTrue: + case kSpecialVarFalse: { + n = 6; + break; + } + case kSpecialVarNull: { + n = 7; + break; + } + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "f_type(UNKNOWN)"); + break; + } } rettv->vval.v_number = n; } @@ -17164,6 +16939,13 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = wp->w_width; } +/// "wordcount()" function +static void f_wordcount(typval_T *argvars, typval_T *rettv) +{ + rettv_dict_alloc(rettv); + cursor_pos_info(rettv->vval.v_dict); +} + /// "writefile()" function static void f_writefile(typval_T *argvars, typval_T *rettv) { @@ -17391,21 +17173,28 @@ static int get_env_len(char_u **arg) return len; } -/* - * Get the length of the name of a function or internal variable. - * "arg" is advanced to the first non-white character after the name. - * Return 0 if something is wrong. - */ -static int get_id_len(char_u **arg) -{ - char_u *p; +// Get the length of the name of a function or internal variable. +// "arg" is advanced to the first non-white character after the name. +// Return 0 if something is wrong. +static int get_id_len(char_u **arg) { + char_u *p; int len; - /* Find the end of the name. */ - for (p = *arg; eval_isnamec(*p); ++p) - ; - if (p == *arg) /* no name found */ + // Find the end of the name. + for (p = *arg; eval_isnamec(*p); p++) { + if (*p == ':') { + // "s:" is start of "s:var", but "n:" is not and can be used in + // slice "[n:]". Also "xx:" is not a namespace. + len = (int)(p - *arg); + if (len > 1 + || (len == 1 && vim_strchr(namespace_char, **arg) == NULL)) { + break; + } + } + } + if (p == *arg) { // no name found return 0; + } len = (int)(p - *arg); *arg = skipwhite(p); @@ -17476,28 +17265,29 @@ static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose) return len; } -/* - * Find the end of a variable or function name, taking care of magic braces. - * If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the - * start and end of the first magic braces item. - * "flags" can have FNE_INCL_BR and FNE_CHECK_START. - * Return a pointer to just after the name. Equal to "arg" if there is no - * valid name. - */ -static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags) +// Find the end of a variable or function name, taking care of magic braces. +// If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the +// start and end of the first magic braces item. +// "flags" can have FNE_INCL_BR and FNE_CHECK_START. +// Return a pointer to just after the name. Equal to "arg" if there is no +// valid name. +static char_u *find_name_end(char_u *arg, char_u **expr_start, + char_u **expr_end, int flags) { int mb_nest = 0; int br_nest = 0; - char_u *p; + char_u *p; + int len; if (expr_start != NULL) { *expr_start = NULL; *expr_end = NULL; } - /* Quick check for valid starting character. */ - if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') + // Quick check for valid starting character. + if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') { return arg; + } for (p = arg; *p != NUL && (eval_isnamec(*p) @@ -17512,30 +17302,44 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end if (*p == NUL) break; } else if (*p == '"') { - /* skip over "str\"ing" to avoid counting [ and ] inside it. */ - for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) - if (*p == '\\' && p[1] != NUL) + // skip over "str\"ing" to avoid counting [ and ] inside it. + for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) { + if (*p == '\\' && p[1] != NUL) { ++p; - if (*p == NUL) + } + } + if (*p == NUL) { break; + } + } else if (br_nest == 0 && mb_nest == 0 && *p == ':') { + // "s:" is start of "s:var", but "n:" is not and can be used in + // slice "[n:]". Also "xx:" is not a namespace. But {ns}: is. */ + len = (int)(p - arg); + if ((len > 1 && p[-1] != '}') + || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) { + break; + } } if (mb_nest == 0) { - if (*p == '[') + if (*p == '[') { ++br_nest; - else if (*p == ']') + } else if (*p == ']') { --br_nest; + } } if (br_nest == 0) { if (*p == '{') { mb_nest++; - if (expr_start != NULL && *expr_start == NULL) + if (expr_start != NULL && *expr_start == NULL) { *expr_start = p; + } } else if (*p == '}') { mb_nest--; - if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) + if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) { *expr_end = p; + } } } } @@ -17617,14 +17421,6 @@ static int eval_isnamec1(int c) } /* - * Set number v: variable to "val". - */ -void set_vim_var_nr(int idx, long val) -{ - vimvars[idx].vv_nr = val; -} - -/* * Get number v: variable value. */ long get_vim_var_nr(int idx) FUNC_ATTR_PURE @@ -17661,11 +17457,11 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE */ void set_vim_var_char(int c) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; - if (has_mbyte) - buf[(*mb_char2bytes)(c, buf)] = NUL; - else { + if (has_mbyte) { + buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL; + } else { buf[0] = c; buf[1] = NUL; } @@ -17684,57 +17480,71 @@ void set_vcount(long count, long count1, int set_prevcount) vimvars[VV_COUNT1].vv_nr = count1; } -/* - * Set string v: variable to a copy of "val". - */ -void set_vim_var_string ( - int idx, - char_u *val, - int len /* length of "val" to use or -1 (whole string) */ -) +/// Set number v: variable to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) { - /* Need to do this (at least) once, since we can't initialize a union. - * Will always be invoked when "v:progname" is set. */ - vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + vimvars[idx].vv_nr = val; +} + +/// Set special v: variable to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) +{ + vimvars[idx].vv_special = val; +} +/// Set string v: variable to the given string +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. Will be copied. +/// @param[in] len Legth of that value or -1 in which case strlen() will be +/// used. +void set_vim_var_string(const VimVarIndex idx, const char *const val, + const ptrdiff_t len) +{ xfree(vimvars[idx].vv_str); - if (val == NULL) + if (val == NULL) { vimvars[idx].vv_str = NULL; - else if (len == -1) - vimvars[idx].vv_str = vim_strsave(val); - else - vimvars[idx].vv_str = vim_strnsave(val, len); + } else if (len == -1) { + vimvars[idx].vv_str = (char_u *) xstrdup(val); + } else { + vimvars[idx].vv_str = (char_u *) xstrndup(val, (size_t) len); + } } -/* - * Set List v: variable to "val". - */ -void set_vim_var_list(int idx, list_T *val) +/// Set list v: variable to the given list +/// +/// @param[in] idx Index of variable to set. +/// @param[in,out] val Value to set to. Reference count will be incremented. +void set_vim_var_list(const VimVarIndex idx, list_T *const val) { list_unref(vimvars[idx].vv_list); vimvars[idx].vv_list = val; - if (val != NULL) - ++val->lv_refcount; + if (val != NULL) { + val->lv_refcount++; + } } -/// Set Dictionary v: variable to "val". -void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL +/// Set Dictionary v: variable to the given dictionary +/// +/// @param[in] idx Index of variable to set. +/// @param[in,out] val Value to set to. Reference count will be incremented. +/// Also keys of the dictionary will be made read-only. +void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) { dict_unref(vimvars[idx].vv_dict); + vimvars[idx].vv_dict = val; - // Set readonly - int todo = (int)val->dv_hashtab.ht_used; - for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - - --todo; - HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + if (val != NULL) { + val->dv_refcount++; + // Set readonly + dict_set_keys_readonly(val); } - - vimvars[idx].vv_dict = val; - ++val->dv_refcount; } /* @@ -17742,15 +17552,17 @@ void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL */ void set_reg_var(int c) { - char_u regname; + char regname; - if (c == 0 || c == ' ') + if (c == 0 || c == ' ') { regname = '"'; - else + } else { regname = c; - /* Avoid free/alloc when the value is already right. */ - if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) + } + // Avoid free/alloc when the value is already right. + if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) { set_vim_var_string(VV_REG, ®name, 1); + } } /* @@ -17852,10 +17664,11 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) static int get_var_tv ( char_u *name, - int len, /* length of "name" */ - typval_T *rettv, /* NULL when only checking existence */ - int verbose, /* may give error message */ - int no_autoload /* do not use script autoloading */ + int len, // length of "name" + typval_T *rettv, // NULL when only checking existence + dictitem_T **dip, // non-NULL when typval's dict item is needed + int verbose, // may give error message + int no_autoload // do not use script autoloading ) { int ret = OK; @@ -17881,8 +17694,12 @@ get_var_tv ( */ else { v = find_var(name, NULL, no_autoload); - if (v != NULL) + if (v != NULL) { tv = &v->di_tv; + if (dip != NULL) { + *dip = v; + } + } } if (tv == NULL) { @@ -17975,25 +17792,23 @@ void free_tv(typval_T *varp) { if (varp != NULL) { switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - /*FALLTHROUGH*/ - case VAR_STRING: - xfree(varp->vval.v_string); - break; - case VAR_LIST: - list_unref(varp->vval.v_list); - break; - case VAR_DICT: - dict_unref(varp->vval.v_dict); - break; - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - default: - EMSG2(_(e_intern2), "free_tv()"); - break; + case VAR_FUNC: + func_unref(varp->vval.v_string); + // FALLTHROUGH + case VAR_STRING: + xfree(varp->vval.v_string); + break; + case VAR_LIST: + list_unref(varp->vval.v_list); + break; + case VAR_DICT: + dict_unref(varp->vval.v_dict); + break; + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: + break; } xfree(varp); } @@ -18031,10 +17846,11 @@ void clear_tv(typval_T *varp) case VAR_FLOAT: varp->vval.v_float = 0.0; break; + case VAR_SPECIAL: + varp->vval.v_special = kSpecialVarFalse; + break; case VAR_UNKNOWN: break; - default: - EMSG2(_(e_intern2), "clear_tv()"); } varp->v_lock = 0; } @@ -18078,9 +17894,10 @@ long get_tv_number_chk(typval_T *varp, int *denote) EMSG(_("E703: Using a Funcref as a Number")); break; case VAR_STRING: - if (varp->vval.v_string != NULL) + if (varp->vval.v_string != NULL) { vim_str2nr(varp->vval.v_string, NULL, NULL, - TRUE, TRUE, &n, NULL); + STR2NR_ALL, &n, NULL, 0); + } return n; case VAR_LIST: EMSG(_("E745: Using a List as a Number")); @@ -18088,17 +17905,57 @@ long get_tv_number_chk(typval_T *varp, int *denote) case VAR_DICT: EMSG(_("E728: Using a Dictionary as a Number")); break; - default: - EMSG2(_(e_intern2), "get_tv_number()"); + case VAR_SPECIAL: + switch (varp->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } + } + break; + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); break; } - if (denote == NULL) /* useful for values that must be unsigned */ + if (denote == NULL) { + // useful for values that must be unsigned n = -1; - else - *denote = TRUE; + } else { + *denote = true; + } return n; } +static float_T get_tv_float(typval_T *varp) +{ + switch (varp->v_type) { + case VAR_NUMBER: + return (float_T)(varp->vval.v_number); + case VAR_FLOAT: + return varp->vval.v_float; + break; + case VAR_FUNC: + EMSG(_("E891: Using a Funcref as a Float")); + break; + case VAR_STRING: + EMSG(_("E892: Using a String as a Float")); + break; + case VAR_LIST: + EMSG(_("E893: Using a List as a Float")); + break; + case VAR_DICT: + EMSG(_("E894: Using a Dictionary as a Float")); + break; + default: + EMSG2(_(e_intern2), "get_tv_float()"); + break; + } + return 0; +} + /* * Get the lnum from the first argument. * Also accepts ".", "$", etc., but that only works for the current buffer. @@ -18192,8 +18049,11 @@ static char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) if (varp->vval.v_string != NULL) return varp->vval.v_string; return (char_u *)""; - default: - EMSG2(_(e_intern2), "get_tv_string_buf()"); + case VAR_SPECIAL: + STRCPY(buf, encode_special_var_names[varp->vval.v_special]); + return buf; + case VAR_UNKNOWN: + EMSG(_("E908: using an invalid value as a String")); break; } return NULL; @@ -18270,6 +18130,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d) hashitem_T *hi; *d = NULL; + if (name[0] == NUL) { + return NULL; + } if (name[1] != ':') { // name has implicit scope if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) { @@ -18319,6 +18182,7 @@ end: } // Find the hashtab used for a variable name. +// Return NULL if the name is not valid. // Set "varname" to the start of name without ':'. static hashtab_T *find_var_ht(uint8_t *name, uint8_t **varname) { @@ -18426,14 +18290,16 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) if (!HASHITEM_EMPTY(hi)) { --todo; - /* Free the variable. Don't remove it from the hashtab, - * ht_array might change then. hash_clear() takes care of it - * later. */ + // Free the variable. Don't remove it from the hashtab, + // ht_array might change then. hash_clear() takes care of it + // later. v = HI2DI(hi); - if (free_val) + if (free_val) { clear_tv(&v->di_tv); - if ((v->di_flags & DI_FLAGS_FIX) == 0) + } + if (v->di_flags & DI_FLAGS_ALLOC) { xfree(v); + } } } hash_clear(ht); @@ -18458,7 +18324,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi) */ static void list_one_var(dictitem_T *v, char_u *prefix, int *first) { - char_u *s = (char_u *) echo_string(&v->di_tv, NULL); + char_u *s = (char_u *) encode_tv2echo(&v->di_tv, NULL); list_one_var_a(prefix, v->di_key, v->di_tv.v_type, s == NULL ? (char_u *)"" : s, first); xfree(s); @@ -18540,10 +18406,11 @@ set_var ( return; if (v != NULL) { - /* existing variable, need to clear the value */ - if (var_check_ro(v->di_flags, name) - || tv_check_lock(v->di_tv.v_lock, name)) + // existing variable, need to clear the value + if (var_check_ro(v->di_flags, name, false) + || tv_check_lock(v->di_tv.v_lock, name, false)) { return; + } if (v->di_tv.v_type != tv->v_type && !((v->di_tv.v_type == VAR_STRING || v->di_tv.v_type == VAR_NUMBER) @@ -18558,10 +18425,8 @@ set_var ( return; } - /* - * Handle setting internal v: variables separately: we don't change - * the type. - */ + // Handle setting internal v: variables separately where needed to + // prevent changing the type. if (ht == &vimvarht) { if (v->di_tv.v_type == VAR_STRING) { xfree(v->di_tv.vval.v_string); @@ -18572,9 +18437,8 @@ set_var ( v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; } - } else if (v->di_tv.v_type != VAR_NUMBER) - EMSG2(_(e_intern2), "set_var()"); - else { + return; + } else if (v->di_tv.v_type == VAR_NUMBER) { v->di_tv.vval.v_number = get_tv_number(tv); if (STRCMP(varname, "searchforward") == 0) set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); @@ -18582,8 +18446,10 @@ set_var ( no_hlsearch = !v->di_tv.vval.v_number; redraw_all_later(SOME_VALID); } + return; + } else if (v->di_tv.v_type != tv->v_type) { + EMSG2(_(e_intern2), "set_var()"); } - return; } if (watched) { @@ -18607,7 +18473,7 @@ set_var ( xfree(v); return; } - v->di_flags = 0; + v->di_flags = DI_FLAGS_ALLOC; } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { @@ -18628,34 +18494,31 @@ set_var ( } } -/* - * Return TRUE if di_flags "flags" indicates variable "name" is read-only. - * Also give an error message. - */ -static int var_check_ro(int flags, char_u *name) +// Return true if di_flags "flags" indicates variable "name" is read-only. +// Also give an error message. +static bool var_check_ro(int flags, char_u *name, bool use_gettext) { if (flags & DI_FLAGS_RO) { - EMSG2(_(e_readonlyvar), name); - return TRUE; + EMSG2(_(e_readonlyvar), use_gettext ? (char_u *)_(name) : name); + return true; } if ((flags & DI_FLAGS_RO_SBX) && sandbox) { - EMSG2(_(e_readonlysbx), name); - return TRUE; + EMSG2(_(e_readonlysbx), use_gettext ? (char_u *)_(name) : name); + return true; } - return FALSE; + return false; } -/* - * Return TRUE if di_flags "flags" indicates variable "name" is fixed. - * Also give an error message. - */ -static int var_check_fixed(int flags, char_u *name) +// Return true if di_flags "flags" indicates variable "name" is fixed. +// Also give an error message. +static bool var_check_fixed(int flags, char_u *name, bool use_gettext) { if (flags & DI_FLAGS_FIX) { - EMSG2(_("E795: Cannot delete variable %s"), name); - return TRUE; + EMSG2(_("E795: Cannot delete variable %s"), + use_gettext ? (char_u *)_(name) : name); + return true; } - return FALSE; + return false; } /* @@ -18703,23 +18566,28 @@ static int valid_varname(char_u *varname) return TRUE; } -/* - * Return TRUE if typeval "tv" is set to be locked (immutable). - * Also give an error message, using "name". - */ -static int tv_check_lock(int lock, char_u *name) +// Return true if typeval "tv" is set to be locked (immutable). +// Also give an error message, using "name" or _("name") when use_gettext is +// true. +static bool tv_check_lock(int lock, char_u *name, bool use_gettext) { if (lock & VAR_LOCKED) { EMSG2(_("E741: Value is locked: %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; + name == NULL + ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : name); + return true; } if (lock & VAR_FIXED) { EMSG2(_("E742: Cannot change value of %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; + name == NULL + ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : name); + return true; } - return FALSE; + return false; } /* @@ -18733,42 +18601,34 @@ void copy_tv(typval_T *from, typval_T *to) { to->v_type = from->v_type; to->v_lock = 0; + memmove(&to->vval, &from->vval, sizeof(to->vval)); switch (from->v_type) { - case VAR_NUMBER: - to->vval.v_number = from->vval.v_number; - break; - case VAR_FLOAT: - to->vval.v_float = from->vval.v_float; - break; - case VAR_STRING: - case VAR_FUNC: - if (from->vval.v_string == NULL) - to->vval.v_string = NULL; - else { - to->vval.v_string = vim_strsave(from->vval.v_string); - if (from->v_type == VAR_FUNC) - func_ref(to->vval.v_string); - } - break; - case VAR_LIST: - if (from->vval.v_list == NULL) - to->vval.v_list = NULL; - else { - to->vval.v_list = from->vval.v_list; - ++to->vval.v_list->lv_refcount; - } - break; - case VAR_DICT: - if (from->vval.v_dict == NULL) - to->vval.v_dict = NULL; - else { - to->vval.v_dict = from->vval.v_dict; - ++to->vval.v_dict->dv_refcount; - } - break; - default: - EMSG2(_(e_intern2), "copy_tv()"); - break; + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_SPECIAL: + break; + case VAR_STRING: + case VAR_FUNC: + if (from->vval.v_string != NULL) { + to->vval.v_string = vim_strsave(from->vval.v_string); + if (from->v_type == VAR_FUNC) { + func_ref(to->vval.v_string); + } + } + break; + case VAR_LIST: + if (from->vval.v_list != NULL) { + to->vval.v_list->lv_refcount++; + } + break; + case VAR_DICT: + if (from->vval.v_dict != NULL) { + to->vval.v_dict->dv_refcount++; + } + break; + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "copy_tv(UNKNOWN)"); + break; } } @@ -18808,6 +18668,7 @@ int var_item_copy(const vimconv_T *const conv, case VAR_NUMBER: case VAR_FLOAT: case VAR_FUNC: + case VAR_SPECIAL: copy_tv(from, to); break; case VAR_STRING: @@ -18854,8 +18715,8 @@ int var_item_copy(const vimconv_T *const conv, if (to->vval.v_dict == NULL) ret = FAIL; break; - default: - EMSG2(_(e_intern2), "var_item_copy()"); + case VAR_UNKNOWN: + EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); ret = FAIL; } --recurse; @@ -18910,7 +18771,7 @@ void ex_echo(exarg_T *eap) } } else if (eap->cmdidx == CMD_echo) msg_puts_attr((char_u *)" ", echo_attr); - char_u *tofree = p = (char_u *) echo_string(&rettv, NULL); + char_u *tofree = p = (char_u *) encode_tv2echo(&rettv, NULL); if (p != NULL) { for (; *p != NUL && !got_int; ++p) { if (*p == '\n' || *p == '\r' || *p == TAB) { @@ -19312,7 +19173,10 @@ void ex_function(exarg_T *eap) break; } } - ++p; /* skip the ')' */ + if (*p != ')') { + goto erret; + } + ++p; // skip the ')' /* find extra arguments "range", "dict" and "abort" */ for (;; ) { @@ -19442,9 +19306,10 @@ void ex_function(exarg_T *eap) if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) || (p[0] == 'i' && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n' - && (!ASCII_ISALPHA(p[2]) || - (p[2] == 's')))))) + && (!ASCII_ISALPHA(p[2]) + || (p[2] == 's')))))) { skip_until = vim_strsave((char_u *)"."); + } // Check for ":python <<EOF", ":lua <<EOF", etc. arg = skipwhite(skiptowhite(p)); @@ -19536,13 +19401,14 @@ void ex_function(exarg_T *eap) goto erret; } if (fudi.fd_di == NULL) { - /* Can't add a function to a locked dictionary */ - if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg)) + if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, false)) { + // Can't add a function to a locked dictionary goto erret; - } - /* Can't change an existing function if it is locked */ - else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg)) + } + } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, false)) { + // Can't change an existing function if it is locked goto erret; + } /* Give the function a sequential number. Can only be used with a * Funcref! */ @@ -19600,7 +19466,10 @@ void ex_function(exarg_T *eap) /* insert the new function in the function list */ STRCPY(fp->uf_name, name); - hash_add(&func_hashtab, UF2HIKEY(fp)); + if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) { + xfree(fp); + goto erret; + } } fp->uf_refcount = 1; fp->uf_args = newargs; @@ -19707,11 +19576,12 @@ trans_function_name ( *pp = end; } else { if (!skip && !(flags & TFN_QUIET) && (fdp == NULL - || lv.ll_dict == NULL || - fdp->fd_newkey == NULL)) + || lv.ll_dict == NULL + || fdp->fd_newkey == NULL)) { EMSG(_(e_funcref)); - else + } else { *pp = end; + } name = NULL; } goto theend; @@ -20495,7 +20365,7 @@ call_user_func ( v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); - v->di_flags = DI_FLAGS_RO; + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; } STRCPY(v->di_key, name); hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); @@ -20517,14 +20387,25 @@ call_user_func ( save_sourcing_name = sourcing_name; save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 1; - sourcing_name = xmalloc((save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) - + STRLEN(fp->uf_name) + 13); + // need space for new sourcing_name: + // * save_sourcing_name + // * "["number"].." or "function " + // * "<SNR>" + fp->uf_name - 3 + // * terminating NUL + size_t len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) + + STRLEN(fp->uf_name) + 27; + sourcing_name = xmalloc(len); { if (save_sourcing_name != NULL - && STRNCMP(save_sourcing_name, "function ", 9) == 0) - sprintf((char *)sourcing_name, "%s..", save_sourcing_name); - else + && STRNCMP(save_sourcing_name, "function ", 9) == 0) { + vim_snprintf((char *)sourcing_name, + len, + "%s[%" PRId64 "]..", + save_sourcing_name, + (int64_t)save_sourcing_lnum); + } else { STRCPY(sourcing_name, "function "); + } cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); if (p_verbose >= 12) { @@ -20544,10 +20425,10 @@ call_user_func ( msg_outnum((long)argvars[i].vval.v_number); } else { // Do not want errors such as E724 here. - ++emsg_off; - char_u *s = (char_u *) tv2string(&argvars[i], NULL); + emsg_off++; + char_u *s = (char_u *) encode_tv2string(&argvars[i], NULL); char_u *tofree = s; - --emsg_off; + emsg_off--; if (s != NULL) { if (vim_strsize(s) > MSG_BUF_CLEN) { trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); @@ -20600,9 +20481,9 @@ call_user_func ( --RedrawingDisabled; - /* when the function was aborted because of an error, return -1 */ - if ((did_emsg && - (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { + // when the function was aborted because of an error, return -1 + if ((did_emsg + && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { clear_tv(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; @@ -20638,10 +20519,10 @@ call_user_func ( // The value may be very long. Skip the middle part, so that we // have some idea how it starts and ends. smsg() would always // truncate it at the end. Don't want errors such as E724 here. - ++emsg_off; - char_u *s = (char_u *) tv2string(fc->rettv, NULL); + emsg_off++; + char_u *s = (char_u *) encode_tv2string(fc->rettv, NULL); char_u *tofree = s; - --emsg_off; + emsg_off--; if (s != NULL) { if (vim_strsize(s) > MSG_BUF_CLEN) { trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); @@ -20912,7 +20793,7 @@ char_u *get_return_cmd(void *rettv) char_u *tofree = NULL; if (rettv != NULL) { - tofree = s = (char_u *) echo_string((typval_T *) rettv, NULL); + tofree = s = (char_u *) encode_tv2echo((typval_T *) rettv, NULL); } if (s == NULL) { s = (char_u *)""; @@ -21491,7 +21372,15 @@ repeat: } if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') { + // vim_strsave_shellescape() needs a NUL terminated string. + c = (*fnamep)[*fnamelen]; + if (c != NUL) { + (*fnamep)[*fnamelen] = NUL; + } p = vim_strsave_shellescape(*fnamep, false, false); + if (c != NUL) { + (*fnamep)[*fnamelen] = c; + } xfree(*bufp); *bufp = *fnamep = p; *fnamelen = STRLEN(p); @@ -21586,8 +21475,13 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags) return ret; } -static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout, - ufunc_T *on_stderr, ufunc_T *on_exit, dict_T *self, bool pty) +static inline TerminalJobData *common_job_init(char **argv, + ufunc_T *on_stdout, + ufunc_T *on_stderr, + ufunc_T *on_exit, + dict_T *self, + bool pty, + bool detach) { TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); data->stopped = false; @@ -21610,6 +21504,7 @@ static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout, } proc->cb = on_process_exit; proc->events = data->events; + proc->detach = detach; return data; } @@ -21620,7 +21515,6 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout, if (get_dict_callback(vopts, "on_stdout", on_stdout) && get_dict_callback(vopts, "on_stderr", on_stderr) && get_dict_callback(vopts, "on_exit", on_exit)) { - vopts->internal_refcount++; vopts->dv_refcount++; return true; } @@ -21638,8 +21532,13 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout, static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) { - data->refcount++; Process *proc = (Process *)&data->proc; + if (proc->type == kProcessTypePty && proc->detach) { + EMSG2(_(e_invarg2), "terminal/pty job cannot be detached"); + return false; + } + + data->refcount++; char *cmd = xstrdup(proc->argv[0]); if (!process_spawn(proc)) { EMSG2(_(e_jobspawn), cmd); @@ -21682,7 +21581,6 @@ static inline void free_term_job_data_event(void **argv) } if (data->self) { - data->self->internal_refcount--; dict_unref(data->self); } queue_free(data->events); @@ -21778,10 +21676,9 @@ static void on_process_exit(Process *proc, int status, void *d) TerminalJobData *data = d; if (data->term && !data->exited) { data->exited = true; - char msg[22]; + char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); terminal_close(data->term, msg); - apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, curbuf); } if (data->status_ptr) { @@ -21804,6 +21701,18 @@ static void term_resize(uint16_t width, uint16_t height, void *d) pty_process_resize(&data->proc.pty, width, height); } +static inline void term_delayed_free(void **argv) +{ + TerminalJobData *j = argv[0]; + if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) { + queue_put(j->events, term_delayed_free, 1, j); + return; + } + + terminal_destroy(j->term); + term_job_data_decref(j); +} + static void term_close(void *d) { TerminalJobData *data = d; @@ -21811,8 +21720,7 @@ static void term_close(void *d) data->exited = true; process_stop((Process *)&data->proc); } - terminal_destroy(data->term); - term_job_data_decref(d); + queue_put(data->events, term_delayed_free, 1, data); } static void term_job_data_decref(TerminalJobData *data) @@ -21933,7 +21841,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) true, NULL); - arguments->lv_refcount--; + list_unref(arguments); // Restore caller scope information restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; @@ -22059,4 +21967,3 @@ static bool is_watched(dict_T *d) { return d && !QUEUE_EMPTY(&d->watchers); } - diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 19a1bbb083..d6800afd52 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -1,12 +1,17 @@ #ifndef NVIM_EVAL_H #define NVIM_EVAL_H -#include <msgpack.h> - #include "nvim/profile.h" +#include "nvim/hashtab.h" // For hashtab_T +#include "nvim/garray.h" // For garray_T +#include "nvim/buffer_defs.h" // For scid_T +#include "nvim/ex_cmds_defs.h" // For exarg_T + +#define COPYID_INC 2 +#define COPYID_MASK (~0x1) // All user-defined functions are found in this hashtable. -EXTERN hashtab_T func_hashtab; +extern hashtab_T func_hashtab; // Structure to hold info for a user function. typedef struct ufunc ufunc_T; @@ -46,8 +51,8 @@ EXTERN ufunc_T dumuf; #define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) #define HI2UF(hi) HIKEY2UF((hi)->hi_key) -/* Defines for Vim variables. These must match vimvars[] in eval.c! */ -enum { +/// Defines for Vim variables +typedef enum { VV_COUNT, VV_COUNT1, VV_PREVCOUNT, @@ -111,15 +116,38 @@ enum { VV_OPTION_NEW, VV_OPTION_OLD, VV_OPTION_TYPE, + VV_ERRORS, VV_MSGPACK_TYPES, - VV_LEN, /* number of v: vars */ -}; + VV_EVENT, + VV_FALSE, + VV_TRUE, + VV_NULL, + VV__NULL_LIST, // List with NULL value. For test purposes only. + VV__NULL_DICT, // Dictionary with NULL value. For test purposes only. +} VimVarIndex; + +/// All recognized msgpack types +typedef enum { + kMPNil, + kMPBoolean, + kMPInteger, + kMPFloat, + kMPString, + kMPBinary, + kMPArray, + kMPMap, + kMPExt, +#define LAST_MSGPACK_TYPE kMPExt +} MessagePackType; + +/// Array mapping values from MessagePackType to corresponding list pointers +extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1]; + +#undef LAST_MSGPACK_TYPE /// Maximum number of function arguments #define MAX_FUNC_ARGS 20 -int vim_to_msgpack(msgpack_packer *const, typval_T *const); - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.h.generated.h" #endif diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c new file mode 100644 index 0000000000..0774ef515f --- /dev/null +++ b/src/nvim/eval/decode.c @@ -0,0 +1,1116 @@ +#include <stddef.h> + +#include <msgpack.h> + +#include "nvim/eval_defs.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/ascii.h" +#include "nvim/message.h" +#include "nvim/charset.h" // vim_str2nr +#include "nvim/lib/kvec.h" +#include "nvim/vim.h" // OK, FAIL + +/// Helper structure for container_struct +typedef struct { + size_t stack_index; ///< Index of current container in stack. + list_T *special_val; ///< _VAL key contents for special maps. + ///< When container is not a special dictionary it is + ///< NULL. + const char *s; ///< Location where container starts. + typval_T container; ///< Container. Either VAR_LIST, VAR_DICT or VAR_LIST + ///< which is _VAL from special dictionary. +} ContainerStackItem; + +/// Helper structure for values struct +typedef struct { + bool is_special_string; ///< Indicates that current value is a special + ///< dictionary with string. + bool didcomma; ///< True if previous token was comma. + bool didcolon; ///< True if previous token was colon. + typval_T val; ///< Actual value. +} ValuesStackItem; + +/// Vector containing values not yet saved in any container +typedef kvec_t(ValuesStackItem) ValuesStack; + +/// Vector containing containers, each next container is located inside previous +typedef kvec_t(ContainerStackItem) ContainerStack; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/decode.c.generated.h" +#endif + +/// Create special dictionary +/// +/// @param[out] rettv Location where created dictionary will be saved. +/// @param[in] type Type of the dictionary. +/// @param[in] val Value associated with the _VAL key. +static inline void create_special_dict(typval_T *const rettv, + const MessagePackType type, + typval_T val) + FUNC_ATTR_NONNULL_ALL +{ + dict_T *const dict = dict_alloc(); + dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); + type_di->di_tv.v_type = VAR_LIST; + type_di->di_tv.v_lock = VAR_UNLOCKED; + type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; + type_di->di_tv.vval.v_list->lv_refcount++; + dict_add(dict, type_di); + dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); + val_di->di_tv = val; + dict_add(dict, val_di); + dict->dv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_dict = dict }, + }; +} + +#define DICT_LEN(dict) (dict)->dv_hashtab.ht_used + +/// Helper function used for working with stack vectors used by JSON decoder +/// +/// @param[in,out] obj New object. Will either be put into the stack (and, +/// probably, also inside container) or freed. +/// @param[out] stack Object stack. +/// @param[out] container_stack Container objects stack. +/// @param[in,out] pp Position in string which is currently being parsed. Used +/// for error reporting and is also set when decoding is +/// restarted due to the necessity of converting regular +/// dictionary to a special map. +/// @param[out] next_map_special Is set to true when dictionary needs to be +/// converted to a special map, otherwise not +/// touched. Indicates that decoding has been +/// restarted. +/// @param[out] didcomma True if previous token was comma. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// @param[out] didcolon True if previous token was colon. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// +/// @return OK in case of success, FAIL in case of error. +static inline int json_decoder_pop(ValuesStackItem obj, + ValuesStack *const stack, + ContainerStack *const container_stack, + const char **const pp, + bool *const next_map_special, + bool *const didcomma, + bool *const didcolon) + FUNC_ATTR_NONNULL_ALL +{ + if (kv_size(*container_stack) == 0) { + kv_push(ValuesStackItem, *stack, obj); + return OK; + } + ContainerStackItem last_container = kv_last(*container_stack); + const char *val_location = *pp; + if (obj.val.v_type == last_container.container.v_type + // vval.v_list and vval.v_dict should have the same size and offset + && ((void *) obj.val.vval.v_list + == (void *) last_container.container.vval.v_list)) { + (void) kv_pop(*container_stack); + val_location = last_container.s; + last_container = kv_last(*container_stack); + } + if (last_container.container.v_type == VAR_LIST) { + if (last_container.container.vval.v_list->lv_len != 0 + && !obj.didcomma) { + EMSG2(_("E474: Expected comma before list item: %s"), val_location); + clear_tv(&obj.val); + return FAIL; + } + assert(last_container.special_val == NULL); + listitem_T *obj_li = listitem_alloc(); + obj_li->li_tv = obj.val; + list_append(last_container.container.vval.v_list, obj_li); + } else if (last_container.stack_index == kv_size(*stack) - 2) { + if (!obj.didcolon) { + EMSG2(_("E474: Expected colon before dictionary value: %s"), + val_location); + clear_tv(&obj.val); + return FAIL; + } + ValuesStackItem key = kv_pop(*stack); + if (last_container.special_val == NULL) { + // These cases should have already been handled. + assert(!(key.is_special_string + || key.val.vval.v_string == NULL + || *key.val.vval.v_string == NUL)); + dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); + clear_tv(&key.val); + if (dict_add(last_container.container.vval.v_dict, obj_di) + == FAIL) { + assert(false); + } + obj_di->di_tv = obj.val; + } else { + list_T *const kv_pair = list_alloc(); + list_append_list(last_container.special_val, kv_pair); + listitem_T *const key_li = listitem_alloc(); + key_li->li_tv = key.val; + list_append(kv_pair, key_li); + listitem_T *const val_li = listitem_alloc(); + val_li->li_tv = obj.val; + list_append(kv_pair, val_li); + } + } else { + // Object with key only + if (!obj.is_special_string && obj.val.v_type != VAR_STRING) { + EMSG2(_("E474: Expected string key: %s"), *pp); + clear_tv(&obj.val); + return FAIL; + } else if (!obj.didcomma + && (last_container.special_val == NULL + && (DICT_LEN(last_container.container.vval.v_dict) != 0))) { + EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location); + clear_tv(&obj.val); + return FAIL; + } + // Handle empty key and key represented as special dictionary + if (last_container.special_val == NULL + && (obj.is_special_string + || obj.val.vval.v_string == NULL + || *obj.val.vval.v_string == NUL + || dict_find(last_container.container.vval.v_dict, + obj.val.vval.v_string, -1))) { + clear_tv(&obj.val); + + // Restart + (void) kv_pop(*container_stack); + ValuesStackItem last_container_val = + kv_A(*stack, last_container.stack_index); + while (kv_size(*stack) > last_container.stack_index) { + clear_tv(&(kv_pop(*stack).val)); + } + *pp = last_container.s; + *didcomma = last_container_val.didcomma; + *didcolon = last_container_val.didcolon; + *next_map_special = true; + return OK; + } + kv_push(ValuesStackItem, *stack, obj); + } + return OK; +} + +#define LENP(p, e) \ + ((int) ((e) - (p))), (p) +#define OBJ(obj_tv, is_sp_string, didcomma_, didcolon_) \ + ((ValuesStackItem) { \ + .is_special_string = (is_sp_string), \ + .val = (obj_tv), \ + .didcomma = (didcomma_), \ + .didcolon = (didcolon_), \ + }) + +#define POP(obj_tv, is_sp_string) \ + do { \ + if (json_decoder_pop(OBJ(obj_tv, is_sp_string, *didcomma, *didcolon), \ + stack, container_stack, \ + &p, next_map_special, didcomma, didcolon) \ + == FAIL) { \ + goto parse_json_string_fail; \ + } \ + if (*next_map_special) { \ + goto parse_json_string_ret; \ + } \ + } while (0) + +/// Parse JSON double-quoted string +/// +/// @param[in] conv Defines conversion necessary to convert UTF-8 string to +/// &encoding. +/// @param[in] buf Buffer being converted. +/// @param[in] buf_len Length of the buffer. +/// @param[in,out] pp Pointer to the start of the string. Must point to '"'. +/// Is advanced to the closing '"'. Also see +/// json_decoder_pop(), it may set pp to another location +/// and alter next_map_special, didcomma and didcolon. +/// @param[out] stack Object stack. +/// @param[out] container_stack Container objects stack. +/// @param[out] next_map_special Is set to true when dictionary is converted +/// to a special map, otherwise not touched. +/// @param[out] didcomma True if previous token was comma. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// @param[out] didcolon True if previous token was colon. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// +/// @return OK in case of success, FAIL in case of error. +static inline int parse_json_string(vimconv_T *const conv, + const char *const buf, const size_t buf_len, + const char **const pp, + ValuesStack *const stack, + ContainerStack *const container_stack, + bool *const next_map_special, + bool *const didcomma, + bool *const didcolon) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + const char *const e = buf + buf_len; + const char *p = *pp; + size_t len = 0; + const char *const s = ++p; + int ret = OK; + while (p < e && *p != '"') { + if (*p == '\\') { + p++; + if (p == e) { + emsgf(_("E474: Unfinished escape sequence: %.*s"), + (int) buf_len, buf); + goto parse_json_string_fail; + } + switch (*p) { + case 'u': { + if (p + 4 >= e) { + emsgf(_("E474: Unfinished unicode escape sequence: %.*s"), + (int) buf_len, buf); + goto parse_json_string_fail; + } else if (!ascii_isxdigit(p[1]) + || !ascii_isxdigit(p[2]) + || !ascii_isxdigit(p[3]) + || !ascii_isxdigit(p[4])) { + emsgf(_("E474: Expected four hex digits after \\u: %.*s"), + LENP(p - 1, e)); + goto parse_json_string_fail; + } + // One UTF-8 character below U+10000 can take up to 3 bytes, + // above up to 6, but they are encoded using two \u escapes. + len += 3; + p += 5; + break; + } + case '\\': + case '/': + case '"': + case 't': + case 'b': + case 'n': + case 'r': + case 'f': { + len++; + p++; + break; + } + default: { + emsgf(_("E474: Unknown escape sequence: %.*s"), LENP(p - 1, e)); + goto parse_json_string_fail; + } + } + } else { + uint8_t p_byte = (uint8_t) *p; + // unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (p_byte < 0x20) { + emsgf(_("E474: ASCII control characters cannot be present " + "inside string: %.*s"), LENP(p, e)); + goto parse_json_string_fail; + } + const int ch = utf_ptr2char((char_u *) p); + // All characters above U+007F are encoded using two or more bytes + // and thus cannot possibly be equal to *p. But utf_ptr2char({0xFF, + // 0}) will return 0xFF, even though 0xFF cannot start any UTF-8 + // code point at all. + // + // The only exception is U+00C3 which is represented as 0xC3 0x83. + if (ch >= 0x80 && p_byte == ch + && !(ch == 0xC3 && p + 1 < e && (uint8_t) p[1] == 0x83)) { + emsgf(_("E474: Only UTF-8 strings allowed: %.*s"), LENP(p, e)); + goto parse_json_string_fail; + } else if (ch > 0x10FFFF) { + emsgf(_("E474: Only UTF-8 code points up to U+10FFFF " + "are allowed to appear unescaped: %.*s"), LENP(p, e)); + goto parse_json_string_fail; + } + const size_t ch_len = (size_t) utf_char2len(ch); + assert(ch_len == (size_t) (ch ? utf_ptr2len((char_u *) p) : 1)); + len += ch_len; + p += ch_len; + } + } + if (p == e || *p != '"') { + emsgf(_("E474: Expected string end: %.*s"), (int) buf_len, buf); + goto parse_json_string_fail; + } + if (len == 0) { + POP(((typval_T) { + .v_type = VAR_STRING, + .vval = { .v_string = NULL }, + }), false); + goto parse_json_string_ret; + } + char *str = xmalloc(len + 1); + int fst_in_pair = 0; + char *str_end = str; + bool hasnul = false; +#define PUT_FST_IN_PAIR(fst_in_pair, str_end) \ + do { \ + if (fst_in_pair != 0) { \ + str_end += utf_char2bytes(fst_in_pair, (char_u *) str_end); \ + fst_in_pair = 0; \ + } \ + } while (0) + for (const char *t = s; t < p; t++) { + if (t[0] != '\\' || t[1] != 'u') { + PUT_FST_IN_PAIR(fst_in_pair, str_end); + } + if (*t == '\\') { + t++; + switch (*t) { + case 'u': { + const char ubuf[] = { t[1], t[2], t[3], t[4] }; + t += 4; + unsigned long ch; + vim_str2nr((char_u *) ubuf, NULL, NULL, + STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4); + if (ch == 0) { + hasnul = true; + } + if (SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) { + PUT_FST_IN_PAIR(fst_in_pair, str_end); + fst_in_pair = (int) ch; + } else if (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END + && fst_in_pair != 0) { + const int full_char = ( + (int) (ch - SURROGATE_LO_START) + + ((fst_in_pair - SURROGATE_HI_START) << 10) + + SURROGATE_FIRST_CHAR); + str_end += utf_char2bytes(full_char, (char_u *) str_end); + fst_in_pair = 0; + } else { + PUT_FST_IN_PAIR(fst_in_pair, str_end); + str_end += utf_char2bytes((int) ch, (char_u *) str_end); + } + break; + } + case '\\': + case '/': + case '"': + case 't': + case 'b': + case 'n': + case 'r': + case 'f': { + static const char escapes[] = { + ['\\'] = '\\', + ['/'] = '/', + ['"'] = '"', + ['t'] = TAB, + ['b'] = BS, + ['n'] = NL, + ['r'] = CAR, + ['f'] = FF, + }; + *str_end++ = escapes[(int) *t]; + break; + } + default: { + assert(false); + } + } + } else { + *str_end++ = *t; + } + } + PUT_FST_IN_PAIR(fst_in_pair, str_end); +#undef PUT_FST_IN_PAIR + if (conv->vc_type != CONV_NONE) { + size_t str_len = (size_t) (str_end - str); + char *const new_str = (char *) string_convert(conv, (char_u *) str, + &str_len); + if (new_str == NULL) { + emsgf(_("E474: Failed to convert string \"%.*s\" from UTF-8"), + (int) str_len, str); + xfree(str); + goto parse_json_string_fail; + } + xfree(str); + str = new_str; + str_end = new_str + str_len; + } + if (hasnul) { + typval_T obj; + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(&obj, kMPString, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) list, str, (size_t) (str_end - str)) + == -1) { + clear_tv(&obj); + goto parse_json_string_fail; + } + xfree(str); + POP(obj, true); + } else { + *str_end = NUL; + POP(((typval_T) { + .v_type = VAR_STRING, + .vval = { .v_string = (char_u *) str }, + }), false); + } + goto parse_json_string_ret; +parse_json_string_fail: + ret = FAIL; +parse_json_string_ret: + *pp = p; + return ret; +} + +#undef POP + +/// Parse JSON number: both floating-point and integer +/// +/// Number format: `-?\d+(?:.\d+)?(?:[eE][+-]?\d+)?`. +/// +/// @param[in] buf Buffer being converted. +/// @param[in] buf_len Length of the buffer. +/// @param[in,out] pp Pointer to the start of the number. Must point to +/// a digit or a minus sign. Is advanced to the last +/// character of the number. Also see json_decoder_pop(), it +/// may set pp to another location and alter +/// next_map_special, didcomma and didcolon. +/// @param[out] stack Object stack. +/// @param[out] container_stack Container objects stack. +/// @param[out] next_map_special Is set to true when dictionary is converted +/// to a special map, otherwise not touched. +/// @param[out] didcomma True if previous token was comma. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// @param[out] didcolon True if previous token was colon. Is set to recorded +/// value when decoder is restarted, otherwise unused. +/// +/// @return OK in case of success, FAIL in case of error. +static inline int parse_json_number(const char *const buf, const size_t buf_len, + const char **const pp, + ValuesStack *const stack, + ContainerStack *const container_stack, + bool *const next_map_special, + bool *const didcomma, + bool *const didcolon) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + const char *const e = buf + buf_len; + const char *p = *pp; + int ret = OK; + const char *const s = p; + const char *ints = NULL; + const char *fracs = NULL; + const char *exps = NULL; + const char *exps_s = NULL; + if (*p == '-') { + p++; + } + ints = p; + if (p >= e) { + goto parse_json_number_check; + } + while (p < e && ascii_isdigit(*p)) { + p++; + } + if (p != ints + 1 && *ints == '0') { + emsgf(_("E474: Leading zeroes are not allowed: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } + if (p >= e || p == ints) { + goto parse_json_number_check; + } + if (*p == '.') { + p++; + fracs = p; + while (p < e && ascii_isdigit(*p)) { + p++; + } + if (p >= e || p == fracs) { + goto parse_json_number_check; + } + } + if (*p == 'e' || *p == 'E') { + p++; + exps_s = p; + if (p < e && (*p == '-' || *p == '+')) { + p++; + } + exps = p; + while (p < e && ascii_isdigit(*p)) { + p++; + } + } +parse_json_number_check: + if (p == ints) { + emsgf(_("E474: Missing number after minus sign: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } else if (p == fracs || exps_s == fracs + 1) { + emsgf(_("E474: Missing number after decimal dot: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } else if (p == exps) { + emsgf(_("E474: Missing exponent: %.*s"), LENP(s, e)); + goto parse_json_number_fail; + } + typval_T tv = { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + }; + const size_t exp_num_len = (size_t) (p - s); + if (fracs || exps) { + // Convert floating-point number + const size_t num_len = string2float(s, &tv.vval.v_float); + if (exp_num_len != num_len) { + emsgf(_("E685: internal error: while converting number \"%.*s\" " + "to float string2float consumed %zu bytes in place of %zu"), + (int) exp_num_len, s, num_len, exp_num_len); + } + tv.v_type = VAR_FLOAT; + } else { + // Convert integer + long nr; + int num_len; + vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s)); + if ((int) exp_num_len != num_len) { + emsgf(_("E685: internal error: while converting number \"%.*s\" " + "to integer vim_str2nr consumed %i bytes in place of %zu"), + (int) exp_num_len, s, num_len, exp_num_len); + } + tv.vval.v_number = (varnumber_T) nr; + } + if (json_decoder_pop(OBJ(tv, false, *didcomma, *didcolon), + stack, container_stack, + &p, next_map_special, didcomma, didcolon) == FAIL) { + goto parse_json_number_fail; + } + if (*next_map_special) { + goto parse_json_number_ret; + } + p--; + goto parse_json_number_ret; +parse_json_number_fail: + ret = FAIL; +parse_json_number_ret: + *pp = p; + return ret; +} + +#define POP(obj_tv, is_sp_string) \ + do { \ + if (json_decoder_pop(OBJ(obj_tv, is_sp_string, didcomma, didcolon), \ + &stack, &container_stack, \ + &p, &next_map_special, &didcomma, &didcolon) \ + == FAIL) { \ + goto json_decode_string_fail; \ + } \ + if (next_map_special) { \ + goto json_decode_string_cycle_start; \ + } \ + } while (0) + +/// Convert JSON string into VimL object +/// +/// @param[in] buf String to convert. UTF-8 encoding is assumed. +/// @param[in] buf_len Length of the string. +/// @param[out] rettv Location where to save results. +/// +/// @return OK in case of success, FAIL otherwise. +int json_decode_string(const char *const buf, const size_t buf_len, + typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *p = buf; + const char *const e = buf + buf_len; + while (p < e && (*p == ' ' || *p == TAB || *p == NL || *p == CAR)) { + p++; + } + if (p == e) { + EMSG(_("E474: Attempt to decode a blank string")); + return FAIL; + } + vimconv_T conv = { .vc_type = CONV_NONE }; + convert_setup(&conv, (char_u *) "utf-8", p_enc); + conv.vc_fail = true; + int ret = OK; + ValuesStack stack; + kv_init(stack); + ContainerStack container_stack; + kv_init(container_stack); + rettv->v_type = VAR_UNKNOWN; + bool didcomma = false; + bool didcolon = false; + bool next_map_special = false; + for (; p < e; p++) { +json_decode_string_cycle_start: + assert(*p == '{' || next_map_special == false); + switch (*p) { + case '}': + case ']': { + if (kv_size(container_stack) == 0) { + emsgf(_("E474: No container to close: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + ContainerStackItem last_container = kv_last(container_stack); + if (*p == '}' && last_container.container.v_type != VAR_DICT) { + emsgf(_("E474: Closing list with curly bracket: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (*p == ']' && last_container.container.v_type != VAR_LIST) { + emsgf(_("E474: Closing dictionary with square bracket: %.*s"), + LENP(p, e)); + goto json_decode_string_fail; + } else if (didcomma) { + emsgf(_("E474: Trailing comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcolon) { + emsgf(_("E474: Expected value after colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.stack_index != kv_size(stack) - 1) { + assert(last_container.stack_index < kv_size(stack) - 1); + emsgf(_("E474: Expected value: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + if (kv_size(stack) == 1) { + p++; + (void) kv_pop(container_stack); + goto json_decode_string_after_cycle; + } else { + if (json_decoder_pop(kv_pop(stack), &stack, &container_stack, &p, + &next_map_special, &didcomma, &didcolon) + == FAIL) { + goto json_decode_string_fail; + } + assert(!next_map_special); + break; + } + } + case ',': { + if (kv_size(container_stack) == 0) { + emsgf(_("E474: Comma not inside container: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + ContainerStackItem last_container = kv_last(container_stack); + if (didcomma) { + emsgf(_("E474: Duplicate comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcolon) { + emsgf(_("E474: Comma after colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.container.v_type == VAR_DICT + && last_container.stack_index != kv_size(stack) - 1) { + emsgf(_("E474: Using comma in place of colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.special_val == NULL + ? (last_container.container.v_type == VAR_DICT + ? (DICT_LEN(last_container.container.vval.v_dict) == 0) + : (last_container.container.vval.v_list->lv_len == 0)) + : (last_container.special_val->lv_len == 0)) { + emsgf(_("E474: Leading comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + didcomma = true; + continue; + } + case ':': { + if (kv_size(container_stack) == 0) { + emsgf(_("E474: Colon not inside container: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + ContainerStackItem last_container = kv_last(container_stack); + if (last_container.container.v_type != VAR_DICT) { + emsgf(_("E474: Using colon not in dictionary: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (last_container.stack_index != kv_size(stack) - 2) { + emsgf(_("E474: Unexpected colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcomma) { + emsgf(_("E474: Colon after comma: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } else if (didcolon) { + emsgf(_("E474: Duplicate colon: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + didcolon = true; + continue; + } + case ' ': + case TAB: + case NL: + case CAR: { + continue; + } + case 'n': { + if ((p + 3) >= e || strncmp(p + 1, "ull", 3) != 0) { + emsgf(_("E474: Expected null: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + p += 3; + POP(((typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarNull }, + }), false); + break; + } + case 't': { + if ((p + 3) >= e || strncmp(p + 1, "rue", 3) != 0) { + emsgf(_("E474: Expected true: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + p += 3; + POP(((typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarTrue }, + }), false); + break; + } + case 'f': { + if ((p + 4) >= e || strncmp(p + 1, "alse", 4) != 0) { + emsgf(_("E474: Expected false: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + p += 4; + POP(((typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarFalse }, + }), false); + break; + } + case '"': { + if (parse_json_string(&conv, buf, buf_len, &p, &stack, &container_stack, + &next_map_special, &didcomma, &didcolon) + == FAIL) { + // Error message was already given + goto json_decode_string_fail; + } + if (next_map_special) { + goto json_decode_string_cycle_start; + } + break; + } + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + if (parse_json_number(buf, buf_len, &p, &stack, &container_stack, + &next_map_special, &didcomma, &didcolon) + == FAIL) { + // Error message was already given + goto json_decode_string_fail; + } + if (next_map_special) { + goto json_decode_string_cycle_start; + } + break; + } + case '[': { + list_T *list = list_alloc(); + list->lv_refcount++; + typval_T tv = { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + }; + kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { + .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = NULL, + })); + kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); + break; + } + case '{': { + typval_T tv; + list_T *val_list = NULL; + if (next_map_special) { + next_map_special = false; + val_list = list_alloc(); + val_list->lv_refcount++; + create_special_dict(&tv, kMPMap, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = val_list }, + })); + } else { + dict_T *dict = dict_alloc(); + dict->dv_refcount++; + tv = (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_dict = dict }, + }; + } + kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { + .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = val_list, + })); + kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); + break; + } + default: { + emsgf(_("E474: Unidentified byte: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + } + didcomma = false; + didcolon = false; + if (kv_size(container_stack) == 0) { + p++; + break; + } + } +json_decode_string_after_cycle: + for (; p < e; p++) { + switch (*p) { + case NL: + case ' ': + case TAB: + case CAR: { + break; + } + default: { + emsgf(_("E474: Trailing characters: %.*s"), LENP(p, e)); + goto json_decode_string_fail; + } + } + } + if (kv_size(stack) == 1 && kv_size(container_stack) == 0) { + *rettv = kv_pop(stack).val; + goto json_decode_string_ret; + } + emsgf(_("E474: Unexpected end of input: %.*s"), (int) buf_len, buf); +json_decode_string_fail: + ret = FAIL; + while (kv_size(stack)) { + clear_tv(&(kv_pop(stack).val)); + } +json_decode_string_ret: + kv_destroy(stack); + kv_destroy(container_stack); + return ret; +} + +#undef LENP +#undef POP + +#undef OBJ + +#undef DICT_LEN + +/// Convert msgpack object to a VimL one +int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (mobj.type) { + case MSGPACK_OBJECT_NIL: { + *rettv = (typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { .v_special = kSpecialVarNull }, + }; + break; + } + case MSGPACK_OBJECT_BOOLEAN: { + *rettv = (typval_T) { + .v_type = VAR_SPECIAL, + .v_lock = VAR_UNLOCKED, + .vval = { + .v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse + }, + }; + break; + } + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + if (mobj.via.u64 <= VARNUMBER_MAX) { + *rettv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = (varnumber_T) mobj.via.u64 }, + }; + } else { + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPInteger, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + uint64_t n = mobj.via.u64; + list_append_number(list, 1); + list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); + list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); + list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + } + break; + } + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + if (mobj.via.i64 >= VARNUMBER_MIN) { + *rettv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = (varnumber_T) mobj.via.i64 }, + }; + } else { + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPInteger, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + uint64_t n = -((uint64_t) mobj.via.i64); + list_append_number(list, -1); + list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); + list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); + list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + } + break; + } + case MSGPACK_OBJECT_FLOAT: { + *rettv = (typval_T) { + .v_type = VAR_FLOAT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_float = mobj.via.f64 }, + }; + break; + } + case MSGPACK_OBJECT_STR: { + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPString, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size) + == -1) { + return FAIL; + } + break; + } + case MSGPACK_OBJECT_BIN: { + if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) { + *rettv = (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) }, + }; + break; + } + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPBinary, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size) + == -1) { + return FAIL; + } + break; + } + case MSGPACK_OBJECT_ARRAY: { + list_T *const list = list_alloc(); + list->lv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + }; + for (size_t i = 0; i < mobj.via.array.size; i++) { + listitem_T *const li = listitem_alloc(); + li->li_tv.v_type = VAR_UNKNOWN; + list_append(list, li); + if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { + return FAIL; + } + } + break; + } + case MSGPACK_OBJECT_MAP: { + for (size_t i = 0; i < mobj.via.map.size; i++) { + if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR + || mobj.via.map.ptr[i].key.via.str.size == 0 + || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL, + mobj.via.map.ptr[i].key.via.str.size) != NULL) { + goto msgpack_to_vim_generic_map; + } + } + dict_T *const dict = dict_alloc(); + dict->dv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval = { .v_dict = dict }, + }; + for (size_t i = 0; i < mobj.via.map.size; i++) { + dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key) + + mobj.via.map.ptr[i].key.via.str.size); + memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, + mobj.via.map.ptr[i].key.via.str.size); + di->di_tv.v_type = VAR_UNKNOWN; + if (dict_add(dict, di) == FAIL) { + // Duplicate key: fallback to generic map + clear_tv(rettv); + xfree(di); + goto msgpack_to_vim_generic_map; + } + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) { + return FAIL; + } + } + break; +msgpack_to_vim_generic_map: {} + list_T *const list = list_alloc(); + list->lv_refcount++; + create_special_dict(rettv, kMPMap, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + for (size_t i = 0; i < mobj.via.map.size; i++) { + list_T *const kv_pair = list_alloc(); + list_append_list(list, kv_pair); + listitem_T *const key_li = listitem_alloc(); + key_li->li_tv.v_type = VAR_UNKNOWN; + list_append(kv_pair, key_li); + listitem_T *const val_li = listitem_alloc(); + val_li->li_tv.v_type = VAR_UNKNOWN; + list_append(kv_pair, val_li); + if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { + return FAIL; + } + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { + return FAIL; + } + } + break; + } + case MSGPACK_OBJECT_EXT: { + list_T *const list = list_alloc(); + list->lv_refcount++; + list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = list_alloc(); + list_append_list(list, ext_val_list); + create_special_dict(rettv, kMPExt, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + if (encode_list_write((void *) ext_val_list, mobj.via.ext.ptr, + mobj.via.ext.size) == -1) { + return FAIL; + } + break; + } + } + return OK; +} diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h new file mode 100644 index 0000000000..5c25a64f7a --- /dev/null +++ b/src/nvim/eval/decode.h @@ -0,0 +1,13 @@ +#ifndef NVIM_EVAL_DECODE_H +#define NVIM_EVAL_DECODE_H + +#include <stddef.h> + +#include <msgpack.h> + +#include "nvim/eval_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/decode.h.generated.h" +#endif +#endif // NVIM_EVAL_DECODE_H diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c new file mode 100644 index 0000000000..c651a50be9 --- /dev/null +++ b/src/nvim/eval/encode.c @@ -0,0 +1,1296 @@ +/// @file encode.c +/// +/// File containing functions for encoding and decoding VimL values. +/// +/// Split out from eval.c. + +#include <msgpack.h> +#include <inttypes.h> +#include <assert.h> +#include <math.h> + +#include "nvim/eval/encode.h" +#include "nvim/buffer_defs.h" // vimconv_T +#include "nvim/eval.h" +#include "nvim/eval_defs.h" +#include "nvim/garray.h" +#include "nvim/mbyte.h" +#include "nvim/message.h" +#include "nvim/memory.h" +#include "nvim/charset.h" // vim_isprintc() +#include "nvim/macros.h" +#include "nvim/ascii.h" +#include "nvim/vim.h" // For _() +#include "nvim/lib/kvec.h" + +#define ga_concat(a, b) ga_concat(a, (char_u *)b) +#define utf_ptr2char(b) utf_ptr2char((char_u *)b) +#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) +#define utf_char2len(b) ((size_t)utf_char2len(b)) +#define string_convert(a, b, c) \ + ((char *)string_convert((vimconv_T *)a, (char_u *)b, c)) +#define convert_setup(vcp, from, to) \ + (convert_setup(vcp, (char_u *)from, (char_u *)to)) + +/// Structure representing current VimL to messagepack conversion state +typedef struct { + enum { + kMPConvDict, ///< Convert dict_T *dictionary. + kMPConvList, ///< Convert list_T *list. + kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. + } type; + union { + struct { + dict_T *dict; ///< Currently converted dictionary. + hashitem_T *hi; ///< Currently converted dictionary item. + size_t todo; ///< Amount of items left to process. + } d; ///< State of dictionary conversion. + struct { + list_T *list; ///< Currently converted list. + listitem_T *li; ///< Currently converted list item. + } l; ///< State of list or generic mapping conversion. + } data; ///< Data to convert. +} MPConvStackVal; + +/// Stack used to convert VimL values to messagepack. +typedef kvec_t(MPConvStackVal) MPConvStack; + +const char *const encode_special_var_names[] = { + [kSpecialVarNull] = "null", + [kSpecialVarTrue] = "true", + [kSpecialVarFalse] = "false", +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/encode.c.generated.h" +#endif + +/// Msgpack callback for writing to readfile()-style list +int encode_list_write(void *data, const char *buf, size_t len) +{ + if (len == 0) { + return 0; + } + list_T *const list = (list_T *) data; + const char *const end = buf + len; + const char *line_end = buf; + listitem_T *li = list->lv_last; + + // Continue the last list element + if (li != NULL) { + line_end = xmemscan(buf, NL, len); + if (line_end != buf) { + const size_t line_length = (size_t)(line_end - buf); + char *str = (char *)li->li_tv.vval.v_string; + const size_t li_len = (str == NULL ? 0 : strlen(str)); + li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1); + str = (char *)li->li_tv.vval.v_string + li_len; + memcpy(str, buf, line_length); + str[line_length] = 0; + memchrsub(str, NUL, NL, line_length); + } + line_end++; + } + + while (line_end < end) { + const char *line_start = line_end; + line_end = xmemscan(line_start, NL, (size_t) (end - line_start)); + char *str = NULL; + if (line_end != line_start) { + const size_t line_length = (size_t)(line_end - line_start); + str = xmemdupz(line_start, line_length); + memchrsub(str, NUL, NL, line_length); + } + list_append_allocated_string(list, str); + line_end++; + } + if (line_end == end) { + list_append_allocated_string(list, NULL); + } + return 0; +} + +/// Abort conversion to string after a recursion error. +static bool did_echo_string_emsg = false; + +/// Show a error message when converting to msgpack value +/// +/// @param[in] msg Error message to dump. Must contain exactly two %s that +/// will be replaced with what was being dumped: first with +/// something like “F†or “function argumentâ€, second with path +/// to the failed value. +/// @param[in] mpstack Path to the failed value. +/// @param[in] objname Dumped object name. +/// +/// @return FAIL. +static int conv_error(const char *const msg, const MPConvStack *const mpstack, + const char *const objname) + FUNC_ATTR_NONNULL_ALL +{ + garray_T msg_ga; + ga_init(&msg_ga, (int)sizeof(char), 80); + char *const key_msg = _("key %s"); + char *const key_pair_msg = _("key %s at index %i from special map"); + char *const idx_msg = _("index %i"); + for (size_t i = 0; i < kv_size(*mpstack); i++) { + if (i != 0) { + ga_concat(&msg_ga, ", "); + } + MPConvStackVal v = kv_A(*mpstack, i); + switch (v.type) { + case kMPConvDict: { + typval_T key_tv = { + .v_type = VAR_STRING, + .vval = { .v_string = (v.data.d.hi == NULL + ? v.data.d.dict->dv_hashtab.ht_array + : (v.data.d.hi - 1))->hi_key }, + }; + char *const key = encode_tv2string(&key_tv, NULL); + vim_snprintf((char *) IObuff, IOSIZE, key_msg, key); + xfree(key); + ga_concat(&msg_ga, IObuff); + break; + } + case kMPConvPairs: + case kMPConvList: { + int idx = 0; + const listitem_T *li; + for (li = v.data.l.list->lv_first; + li != NULL && li->li_next != v.data.l.li; + li = li->li_next) { + idx++; + } + if (v.type == kMPConvList + || li == NULL + || (li->li_tv.v_type != VAR_LIST + && li->li_tv.vval.v_list->lv_len <= 0)) { + vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx); + ga_concat(&msg_ga, IObuff); + } else { + typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv; + char *const key = encode_tv2echo(&key_tv, NULL); + vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx); + xfree(key); + ga_concat(&msg_ga, IObuff); + } + break; + } + } + } + EMSG3(msg, objname, (kv_size(*mpstack) == 0 + ? _("itself") + : (char *) msg_ga.ga_data)); + ga_clear(&msg_ga); + return FAIL; +} + +/// Convert readfile()-style list to a char * buffer with length +/// +/// @param[in] list Converted list. +/// @param[out] ret_len Resulting buffer length. +/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is +/// zero. +/// +/// @return true in case of success, false in case of failure. +bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, + char **const ret_buf) + FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT +{ + size_t len = 0; + if (list != NULL) { + for (const listitem_T *li = list->lv_first; + li != NULL; + li = li->li_next) { + if (li->li_tv.v_type != VAR_STRING) { + return false; + } + len++; + if (li->li_tv.vval.v_string != 0) { + len += STRLEN(li->li_tv.vval.v_string); + } + } + if (len) { + len--; + } + } + *ret_len = len; + if (len == 0) { + *ret_buf = NULL; + return true; + } + ListReaderState lrstate = encode_init_lrstate(list); + char *const buf = xmalloc(len); + size_t read_bytes; + if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) { + assert(false); + } + assert(len == read_bytes); + *ret_buf = buf; + return true; +} + +/// Read bytes from list +/// +/// @param[in,out] state Structure describing position in list from which +/// reading should start. Is updated to reflect position +/// at which reading ended. +/// @param[out] buf Buffer to write to. +/// @param[in] nbuf Buffer length. +/// @param[out] read_bytes Is set to amount of bytes read. +/// +/// @return OK when reading was finished, FAIL in case of error (i.e. list item +/// was not a string), NOTDONE if reading was successfull, but there are +/// more bytes to read. +int encode_read_from_list(ListReaderState *const state, char *const buf, + const size_t nbuf, size_t *const read_bytes) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *const buf_end = buf + nbuf; + char *p = buf; + while (p < buf_end) { + for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { + const char ch = (char) state->li->li_tv.vval.v_string[state->offset++]; + *p++ = (char) ((char) ch == (char) NL ? (char) NUL : (char) ch); + } + if (p < buf_end) { + state->li = state->li->li_next; + if (state->li == NULL) { + *read_bytes = (size_t) (p - buf); + return OK; + } + *p++ = NL; + if (state->li->li_tv.v_type != VAR_STRING) { + *read_bytes = (size_t) (p - buf); + return FAIL; + } + state->offset = 0; + state->li_length = (state->li->li_tv.vval.v_string == NULL + ? 0 + : STRLEN(state->li->li_tv.vval.v_string)); + } + } + *read_bytes = nbuf; + return (state->offset < state->li_length || state->li->li_next != NULL + ? NOTDONE + : OK); +} + +/// Code for checking whether container references itself +/// +/// @param[in,out] val Container to check. +/// @param copyID_attr Name of the container attribute that holds copyID. +/// After checking whether value of this attribute is +/// copyID (variable) it is set to copyID. +#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ + do { \ + if ((val)->copyID_attr == copyID) { \ + CONV_RECURSE((val), conv_type); \ + } \ + (val)->copyID_attr = copyID; \ + } while (0) + +#define TV_STRLEN(tv) \ + (tv->vval.v_string == NULL ? 0 : STRLEN(tv->vval.v_string)) + +/// Define functions which convert VimL value to something else +/// +/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const +/// tv)` which returns OK or FAIL and helper functions. +/// +/// @param firstargtype Type of the first argument. It will be used to return +/// the results. +/// @param firstargname Name of the first argument. +/// @param name Name of the target converter. +#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \ +static int name##_convert_one_value(firstargtype firstargname, \ + MPConvStack *const mpstack, \ + typval_T *const tv, \ + const int copyID, \ + const char *const objname) \ + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + switch (tv->v_type) { \ + case VAR_STRING: { \ + CONV_STRING(tv->vval.v_string, TV_STRLEN(tv)); \ + break; \ + } \ + case VAR_NUMBER: { \ + CONV_NUMBER(tv->vval.v_number); \ + break; \ + } \ + case VAR_FLOAT: { \ + CONV_FLOAT(tv->vval.v_float); \ + break; \ + } \ + case VAR_FUNC: { \ + CONV_FUNC(tv->vval.v_string); \ + break; \ + } \ + case VAR_LIST: { \ + if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ + CONV_EMPTY_LIST(); \ + break; \ + } \ + CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \ + CONV_LIST_START(tv->vval.v_list); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvList, \ + .data = { \ + .l = { \ + .list = tv->vval.v_list, \ + .li = tv->vval.v_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case VAR_SPECIAL: { \ + switch (tv->vval.v_special) { \ + case kSpecialVarNull: { \ + CONV_NIL(); \ + break; \ + } \ + case kSpecialVarTrue: \ + case kSpecialVarFalse: { \ + CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \ + break; \ + } \ + } \ + break; \ + } \ + case VAR_DICT: { \ + if (tv->vval.v_dict == NULL \ + || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ + CONV_EMPTY_DICT(); \ + break; \ + } \ + const dictitem_T *type_di; \ + const dictitem_T *val_di; \ + if (CONV_ALLOW_SPECIAL \ + && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ + && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ + (char_u *) "_TYPE", -1)) != NULL \ + && type_di->di_tv.v_type == VAR_LIST \ + && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ + (char_u *) "_VAL", -1)) != NULL) { \ + size_t i; \ + for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \ + if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \ + break; \ + } \ + } \ + if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + switch ((MessagePackType) i) { \ + case kMPNil: { \ + CONV_NIL(); \ + break; \ + } \ + case kMPBoolean: { \ + if (val_di->di_tv.v_type != VAR_NUMBER) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CONV_BOOL(val_di->di_tv.vval.v_number); \ + break; \ + } \ + case kMPInteger: { \ + const list_T *val_list; \ + varnumber_T sign; \ + varnumber_T highest_bits; \ + varnumber_T high_bits; \ + varnumber_T low_bits; \ + /* List of 4 integers; first is signed (should be 1 or -1, but */ \ + /* this is not checked), second is unsigned and have at most */ \ + /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ + /* bits is not checked), other unsigned and have at most 31 */ \ + /* non-zero bits (number of bits is not checked).*/ \ + if (val_di->di_tv.v_type != VAR_LIST \ + || (val_list = val_di->di_tv.vval.v_list) == NULL \ + || val_list->lv_len != 4 \ + || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ + || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ + || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ + || (highest_bits = \ + val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ + || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ + || (high_bits = \ + val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ + || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ + || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ + | (uint64_t) (((uint64_t) high_bits) << 31) \ + | (uint64_t) low_bits); \ + if (sign > 0) { \ + CONV_UNSIGNED_NUMBER(number); \ + } else { \ + CONV_NUMBER(-number); \ + } \ + break; \ + } \ + case kMPFloat: { \ + if (val_di->di_tv.v_type != VAR_FLOAT) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CONV_FLOAT(val_di->di_tv.vval.v_float); \ + break; \ + } \ + case kMPString: \ + case kMPBinary: { \ + const bool is_string = ((MessagePackType) i == kMPString); \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + size_t len; \ + char *buf; \ + if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \ + &buf)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + if (is_string) { \ + CONV_STR_STRING(buf, len); \ + } else { \ + CONV_STRING(buf, len); \ + } \ + xfree(buf); \ + break; \ + } \ + case kMPArray: { \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \ + kMPConvList); \ + CONV_LIST_START(val_di->di_tv.vval.v_list); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvList, \ + .data = { \ + .l = { \ + .list = val_di->di_tv.vval.v_list, \ + .li = val_di->di_tv.vval.v_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case kMPMap: { \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + list_T *const val_list = val_di->di_tv.vval.v_list; \ + if (val_list == NULL || val_list->lv_len == 0) { \ + CONV_EMPTY_DICT(); \ + break; \ + } \ + for (const listitem_T *li = val_list->lv_first; li != NULL; \ + li = li->li_next) { \ + if (li->li_tv.v_type != VAR_LIST \ + || li->li_tv.vval.v_list->lv_len != 2) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + } \ + CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \ + CONV_DICT_START(val_list->lv_len); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvPairs, \ + .data = { \ + .l = { \ + .list = val_list, \ + .li = val_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case kMPExt: { \ + const list_T *val_list; \ + varnumber_T type; \ + if (val_di->di_tv.v_type != VAR_LIST \ + || (val_list = val_di->di_tv.vval.v_list) == NULL \ + || val_list->lv_len != 2 \ + || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ + || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ + || type < INT8_MIN \ + || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + size_t len; \ + char *buf; \ + if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ + &len, &buf)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + CONV_EXT_STRING(buf, len, type); \ + xfree(buf); \ + break; \ + } \ + } \ + break; \ + } \ +name##_convert_one_value_regular_dict: \ + CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \ + CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \ + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ + .type = kMPConvDict, \ + .data = { \ + .d = { \ + .dict = tv->vval.v_dict, \ + .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ + .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ + }, \ + }, \ + })); \ + break; \ + } \ + case VAR_UNKNOWN: { \ + EMSG2(_(e_intern2), #name "_convert_one_value()"); \ + return FAIL; \ + } \ + } \ + return OK; \ +} \ +\ +scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \ + const char *const objname) \ + FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + const int copyID = get_copyID(); \ + MPConvStack mpstack; \ + kv_init(mpstack); \ + if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \ + == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + while (kv_size(mpstack)) { \ + MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \ + typval_T *cur_tv = NULL; \ + switch (cur_mpsv->type) { \ + case kMPConvDict: { \ + if (!cur_mpsv->data.d.todo) { \ + (void) kv_pop(mpstack); \ + cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ + CONV_DICT_END(); \ + continue; \ + } else if (cur_mpsv->data.d.todo \ + != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ + CONV_DICT_BETWEEN_ITEMS(); \ + } \ + while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ + cur_mpsv->data.d.hi++; \ + } \ + dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ + cur_mpsv->data.d.todo--; \ + cur_mpsv->data.d.hi++; \ + CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \ + CONV_DICT_AFTER_KEY(); \ + cur_tv = &di->di_tv; \ + break; \ + } \ + case kMPConvList: { \ + if (cur_mpsv->data.l.li == NULL) { \ + (void) kv_pop(mpstack); \ + cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ + CONV_LIST_END(cur_mpsv->data.l.list); \ + continue; \ + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ + CONV_LIST_BETWEEN_ITEMS(); \ + } \ + cur_tv = &cur_mpsv->data.l.li->li_tv; \ + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ + break; \ + } \ + case kMPConvPairs: { \ + if (cur_mpsv->data.l.li == NULL) { \ + (void) kv_pop(mpstack); \ + cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ + CONV_DICT_END(); \ + continue; \ + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ + CONV_DICT_BETWEEN_ITEMS(); \ + } \ + const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ + CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair); \ + if (name##_convert_one_value(firstargname, &mpstack, \ + &kv_pair->lv_first->li_tv, copyID, \ + objname) == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + CONV_DICT_AFTER_KEY(); \ + cur_tv = &kv_pair->lv_last->li_tv; \ + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ + break; \ + } \ + } \ + assert(cur_tv != NULL); \ + if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ + objname) == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + } \ + kv_destroy(mpstack); \ + return OK; \ +encode_vim_to_##name##_error_ret: \ + kv_destroy(mpstack); \ + return FAIL; \ +} + +#define CONV_STRING(buf, len) \ + do { \ + const char *const buf_ = (const char *) buf; \ + if (buf == NULL) { \ + ga_concat(gap, "''"); \ + } else { \ + const size_t len_ = (len); \ + ga_grow(gap, (int) (2 + len_ + memcnt(buf_, '\'', len_))); \ + ga_append(gap, '\''); \ + for (size_t i = 0; i < len_; i++) { \ + if (buf_[i] == '\'') { \ + ga_append(gap, '\''); \ + } \ + ga_append(gap, buf_[i]); \ + } \ + ga_append(gap, '\''); \ + } \ + } while (0) + +#define CONV_STR_STRING(buf, len) \ + CONV_STRING(buf, len) + +#define CONV_EXT_STRING(buf, len, type) + +#define CONV_NUMBER(num) \ + do { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \ + ga_concat(gap, numbuf); \ + } while (0) + +#define CONV_FLOAT(flt) \ + do { \ + const float_T flt_ = (flt); \ + switch (fpclassify(flt_)) { \ + case FP_NAN: { \ + ga_concat(gap, (char_u *) "str2float('nan')"); \ + break; \ + } \ + case FP_INFINITE: { \ + if (flt_ < 0) { \ + ga_append(gap, '-'); \ + } \ + ga_concat(gap, (char_u *) "str2float('inf')"); \ + break; \ + } \ + default: { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, (char_u *) numbuf); \ + } \ + } \ + } while (0) + +#define CONV_FUNC(fun) \ + do { \ + ga_concat(gap, "function("); \ + CONV_STRING(fun, STRLEN(fun)); \ + ga_append(gap, ')'); \ + } while (0) + +#define CONV_EMPTY_LIST() \ + ga_concat(gap, "[]") + +#define CONV_LIST_START(lst) \ + ga_append(gap, '[') + +#define CONV_EMPTY_DICT() \ + ga_concat(gap, "{}") + +#define CONV_NIL() \ + ga_concat(gap, "v:null") + +#define CONV_BOOL(num) \ + ga_concat(gap, ((num)? "v:true": "v:false")) + +#define CONV_UNSIGNED_NUMBER(num) + +#define CONV_DICT_START(len) \ + ga_append(gap, '{') + +#define CONV_DICT_END() \ + ga_append(gap, '}') + +#define CONV_DICT_AFTER_KEY() \ + ga_concat(gap, ": ") + +#define CONV_DICT_BETWEEN_ITEMS() \ + ga_concat(gap, ", ") + +#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) + +#define CONV_LIST_END(lst) \ + ga_append(gap, ']') + +#define CONV_LIST_BETWEEN_ITEMS() \ + CONV_DICT_BETWEEN_ITEMS() + +#define CONV_RECURSE(val, conv_type) \ + do { \ + if (!did_echo_string_emsg) { \ + /* Only give this message once for a recursive call to avoid */ \ + /* flooding the user with errors. */ \ + did_echo_string_emsg = true; \ + EMSG(_("E724: unable to correctly dump variable " \ + "with self-referencing container")); \ + } \ + char ebuf[NUMBUFLEN + 7]; \ + size_t backref = 0; \ + for (; backref < kv_size(*mpstack); backref++) { \ + const MPConvStackVal mpval = kv_A(*mpstack, backref); \ + if (mpval.type == conv_type) { \ + if (conv_type == kMPConvDict) { \ + if ((void *) mpval.data.d.dict == (void *) (val)) { \ + break; \ + } \ + } else if (conv_type == kMPConvList) { \ + if ((void *) mpval.data.l.list == (void *) (val)) { \ + break; \ + } \ + } \ + } \ + } \ + vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{E724@%zu}", backref); \ + ga_concat(gap, &ebuf[0]); \ + return OK; \ + } while (0) + +#define CONV_ALLOW_SPECIAL false + +DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap) + +#undef CONV_RECURSE +#define CONV_RECURSE(val, conv_type) \ + do { \ + char ebuf[NUMBUFLEN + 7]; \ + size_t backref = 0; \ + for (; backref < kv_size(*mpstack); backref++) { \ + const MPConvStackVal mpval = kv_A(*mpstack, backref); \ + if (mpval.type == conv_type) { \ + if (conv_type == kMPConvDict) { \ + if ((void *) mpval.data.d.dict == (void *) val) { \ + break; \ + } \ + } else if (conv_type == kMPConvList) { \ + if ((void *) mpval.data.l.list == (void *) val) { \ + break; \ + } \ + } \ + } \ + } \ + if (conv_type == kMPConvDict) { \ + vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{...@%zu}", backref); \ + } else { \ + vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "[...@%zu]", backref); \ + } \ + ga_concat(gap, &ebuf[0]); \ + return OK; \ + } while (0) + +DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap) + +#undef CONV_RECURSE +#define CONV_RECURSE(val, conv_type) \ + do { \ + if (!did_echo_string_emsg) { \ + /* Only give this message once for a recursive call to avoid */ \ + /* flooding the user with errors. */ \ + did_echo_string_emsg = true; \ + EMSG(_("E724: unable to correctly dump variable " \ + "with self-referencing container")); \ + } \ + return OK; \ + } while (0) + +#undef CONV_ALLOW_SPECIAL +#define CONV_ALLOW_SPECIAL true + +#undef CONV_NIL +#define CONV_NIL() \ + ga_concat(gap, "null") + +#undef CONV_BOOL +#define CONV_BOOL(num) \ + ga_concat(gap, ((num)? "true": "false")) + +#undef CONV_UNSIGNED_NUMBER +#define CONV_UNSIGNED_NUMBER(num) \ + do { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \ + ga_concat(gap, numbuf); \ + } while (0) + +#undef CONV_FLOAT +#define CONV_FLOAT(flt) \ + do { \ + const float_T flt_ = (flt); \ + switch (fpclassify(flt_)) { \ + case FP_NAN: { \ + EMSG(_("E474: Unable to represent NaN value in JSON")); \ + return FAIL; \ + } \ + case FP_INFINITE: { \ + EMSG(_("E474: Unable to represent infinity in JSON")); \ + return FAIL; \ + } \ + default: { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, (char_u *) numbuf); \ + break; \ + } \ + } \ + } while (0) + +/// Last used p_enc value +/// +/// Generic pointer: it is not used as a string, only pointer comparisons are +/// performed. Must not be freed. +static const void *last_p_enc = NULL; + +/// Conversion setup for converting from last_p_enc to UTF-8 +static vimconv_T p_enc_conv = { + .vc_type = CONV_NONE, +}; + +/// Escape sequences used in JSON +static const char escapes[][3] = { + [BS] = "\\b", + [TAB] = "\\t", + [NL] = "\\n", + [CAR] = "\\r", + ['"'] = "\\\"", + ['\\'] = "\\\\", + [FF] = "\\f", +}; + +static const char xdigits[] = "0123456789ABCDEF"; + +/// Convert given string to JSON string +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] buf Converted string. +/// @param[in] len Converted string length. +/// +/// @return OK in case of success, FAIL otherwise. +static inline int convert_to_json_string(garray_T *const gap, + const char *const buf, + const size_t len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_ALWAYS_INLINE +{ + const char *utf_buf = buf; + if (utf_buf == NULL) { + ga_concat(gap, "\"\""); + } else { + size_t utf_len = len; + char *tofree = NULL; + if (last_p_enc != (const void *) p_enc) { + p_enc_conv.vc_type = CONV_NONE; + convert_setup(&p_enc_conv, p_enc, "utf-8"); + p_enc_conv.vc_fail = true; + last_p_enc = p_enc; + } + if (p_enc_conv.vc_type != CONV_NONE) { + tofree = string_convert(&p_enc_conv, buf, &utf_len); + if (tofree == NULL) { + emsgf(_("E474: Failed to convert string \"%.*s\" to UTF-8"), + utf_len, utf_buf); + return FAIL; + } + utf_buf = tofree; + } + size_t str_len = 0; + // Encode character as \u0000 if + // 1. It is an ASCII control character (0x0 .. 0x1F, 0x7F). + // 2. &encoding is not UTF-8 and code point is above 0x7F. + // 3. &encoding is UTF-8 and code point is not printable according to + // utf_printable(). + // This is done to make it possible to :echo values when &encoding is not + // UTF-8. +#define ENCODE_RAW(p_enc_conv, ch) \ + (ch >= 0x20 && (p_enc_conv.vc_type == CONV_NONE \ + ? utf_printable(ch) \ + : ch < 0x7F)) + for (size_t i = 0; i < utf_len;) { + const int ch = utf_ptr2char(utf_buf + i); + const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i)); + assert(shift > 0); + i += shift; + switch (ch) { + case BS: + case TAB: + case NL: + case FF: + case CAR: + case '"': + case '\\': { + str_len += 2; + break; + } + default: { + if (ch > 0x7F && shift == 1) { + emsgf(_("E474: String \"%.*s\" contains byte that does not start " + "any UTF-8 character"), + utf_len - (i - shift), utf_buf + i - shift); + xfree(tofree); + return FAIL; + } else if ((SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) + || (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END)) { + emsgf(_("E474: UTF-8 string contains code point which belongs " + "to a surrogate pair: %.*s"), + utf_len - (i - shift), utf_buf + i - shift); + xfree(tofree); + return FAIL; + } else if (ENCODE_RAW(p_enc_conv, ch)) { + str_len += shift; + } else { + str_len += ((sizeof("\\u1234") - 1) + * (size_t) (1 + (ch >= SURROGATE_FIRST_CHAR))); + } + break; + } + } + } + ga_append(gap, '"'); + ga_grow(gap, (int) str_len); + for (size_t i = 0; i < utf_len;) { + const int ch = utf_ptr2char(utf_buf + i); + const size_t shift = (ch == 0? 1: utf_char2len(ch)); + assert(shift > 0); + // Is false on invalid unicode, but this should already be handled. + assert(ch == 0 || shift == utf_ptr2len(utf_buf + i)); + switch (ch) { + case BS: + case TAB: + case NL: + case FF: + case CAR: + case '"': + case '\\': { + ga_concat_len(gap, escapes[ch], 2); + break; + } + default: { + if (ENCODE_RAW(p_enc_conv, ch)) { + ga_concat_len(gap, utf_buf + i, shift); + } else if (ch < SURROGATE_FIRST_CHAR) { + ga_concat_len(gap, ((const char[]) { + '\\', 'u', + xdigits[(ch >> (4 * 3)) & 0xF], + xdigits[(ch >> (4 * 2)) & 0xF], + xdigits[(ch >> (4 * 1)) & 0xF], + xdigits[(ch >> (4 * 0)) & 0xF], + }), sizeof("\\u1234") - 1); + } else { + const int tmp = ch - SURROGATE_FIRST_CHAR; + const int hi = SURROGATE_HI_START + ((tmp >> 10) & ((1 << 10) - 1)); + const int lo = SURROGATE_LO_END + ((tmp >> 0) & ((1 << 10) - 1)); + ga_concat_len(gap, ((const char[]) { + '\\', 'u', + xdigits[(hi >> (4 * 3)) & 0xF], + xdigits[(hi >> (4 * 2)) & 0xF], + xdigits[(hi >> (4 * 1)) & 0xF], + xdigits[(hi >> (4 * 0)) & 0xF], + '\\', 'u', + xdigits[(lo >> (4 * 3)) & 0xF], + xdigits[(lo >> (4 * 2)) & 0xF], + xdigits[(lo >> (4 * 1)) & 0xF], + xdigits[(lo >> (4 * 0)) & 0xF], + }), (sizeof("\\u1234") - 1) * 2); + } + break; + } + } + i += shift; + } + ga_append(gap, '"'); + xfree(tofree); + } + return OK; +} + +#undef CONV_STRING +#define CONV_STRING(buf, len) \ + do { \ + if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \ + return FAIL; \ + } \ + } while (0) + +#undef CONV_EXT_STRING +#define CONV_EXT_STRING(buf, len, type) \ + do { \ + xfree(buf); \ + EMSG(_("E474: Unable to convert EXT string to JSON")); \ + return FAIL; \ + } while (0) + +#undef CONV_FUNC +#define CONV_FUNC(fun) \ + return conv_error(_("E474: Error while dumping %s, %s: " \ + "attempt to dump function reference"), \ + mpstack, objname) + +/// Check whether given key can be used in json_encode() +/// +/// @param[in] tv Key to check. +static inline bool check_json_key(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE + FUNC_ATTR_ALWAYS_INLINE +{ + if (tv->v_type == VAR_STRING) { + return true; + } + if (tv->v_type != VAR_DICT) { + return false; + } + const dict_T *const spdict = tv->vval.v_dict; + if (spdict->dv_hashtab.ht_used != 2) { + return false; + } + const dictitem_T *type_di; + const dictitem_T *val_di; + if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL + || type_di->di_tv.v_type != VAR_LIST + || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString] + && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary]) + || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL + || val_di->di_tv.v_type != VAR_LIST) { + return false; + } + if (val_di->di_tv.vval.v_list == NULL) { + return true; + } + for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first; + li != NULL; li = li->li_next) { + if (li->li_tv.v_type != VAR_STRING) { + return false; + } + } + return true; +} + +#undef CONV_SPECIAL_DICT_KEY_CHECK +#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) \ + do { \ + if (!check_json_key(&kv_pair->lv_first->li_tv)) { \ + EMSG(_("E474: Invalid key in special dictionary")); \ + goto encode_vim_to_##name##_error_ret; \ + } \ + } while (0) + +DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap) + +#undef CONV_STRING +#undef CONV_STR_STRING +#undef CONV_EXT_STRING +#undef CONV_NUMBER +#undef CONV_FLOAT +#undef CONV_FUNC +#undef CONV_EMPTY_LIST +#undef CONV_LIST_START +#undef CONV_EMPTY_DICT +#undef CONV_NIL +#undef CONV_BOOL +#undef CONV_UNSIGNED_NUMBER +#undef CONV_DICT_START +#undef CONV_DICT_END +#undef CONV_DICT_AFTER_KEY +#undef CONV_DICT_BETWEEN_ITEMS +#undef CONV_SPECIAL_DICT_KEY_CHECK +#undef CONV_LIST_END +#undef CONV_LIST_BETWEEN_ITEMS +#undef CONV_RECURSE +#undef CONV_ALLOW_SPECIAL + +/// Return a string with the string representation of a variable. +/// Puts quotes around strings, so that they can be parsed back by eval(). +/// +/// @param[in] tv typval_T to convert. +/// @param[out] len Location where length of the result will be saved. +/// +/// @return String representation of the variable or NULL. +char *encode_tv2string(typval_T *tv, size_t *len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC +{ + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + encode_vim_to_string(&ga, tv, "encode_tv2string() argument"); + did_echo_string_emsg = false; + if (len != NULL) { + *len = (size_t) ga.ga_len; + } + ga_append(&ga, '\0'); + return (char *) ga.ga_data; +} + +/// Return a string with the string representation of a variable. +/// Does not put quotes around strings, as ":echo" displays values. +/// +/// @param[in] tv typval_T to convert. +/// @param[out] len Location where length of the result will be saved. +/// +/// @return String representation of the variable or NULL. +char *encode_tv2echo(typval_T *tv, size_t *len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC +{ + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) { + if (tv->vval.v_string != NULL) { + ga_concat(&ga, tv->vval.v_string); + } + } else { + encode_vim_to_echo(&ga, tv, ":echo argument"); + } + if (len != NULL) { + *len = (size_t) ga.ga_len; + } + ga_append(&ga, '\0'); + return (char *) ga.ga_data; +} + +/// Return a string with the string representation of a variable. +/// Puts quotes around strings, so that they can be parsed back by eval(). +/// +/// @param[in] tv typval_T to convert. +/// @param[out] len Location where length of the result will be saved. +/// +/// @return String representation of the variable or NULL. +char *encode_tv2json(typval_T *tv, size_t *len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC +{ + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + encode_vim_to_json(&ga, tv, "encode_tv2json() argument"); + did_echo_string_emsg = false; + if (len != NULL) { + *len = (size_t) ga.ga_len; + } + ga_append(&ga, '\0'); + return (char *) ga.ga_data; +} + +#define CONV_STRING(buf, len) \ + do { \ + if (buf == NULL) { \ + msgpack_pack_bin(packer, 0); \ + } else { \ + const size_t len_ = (len); \ + msgpack_pack_bin(packer, len_); \ + msgpack_pack_bin_body(packer, buf, len_); \ + } \ + } while (0) + +#define CONV_STR_STRING(buf, len) \ + do { \ + if (buf == NULL) { \ + msgpack_pack_str(packer, 0); \ + } else { \ + const size_t len_ = (len); \ + msgpack_pack_str(packer, len_); \ + msgpack_pack_str_body(packer, buf, len_); \ + } \ + } while (0) + +#define CONV_EXT_STRING(buf, len, type) \ + do { \ + if (buf == NULL) { \ + msgpack_pack_ext(packer, 0, (int8_t) type); \ + } else { \ + const size_t len_ = (len); \ + msgpack_pack_ext(packer, len_, (int8_t) type); \ + msgpack_pack_ext_body(packer, buf, len_); \ + } \ + } while (0) + +#define CONV_NUMBER(num) \ + msgpack_pack_int64(packer, (int64_t) (num)) + +#define CONV_FLOAT(flt) \ + msgpack_pack_double(packer, (double) (flt)) + +#define CONV_FUNC(fun) \ + return conv_error(_("E951: Error while dumping %s, %s: " \ + "attempt to dump function reference"), \ + mpstack, objname) + +#define CONV_EMPTY_LIST() \ + msgpack_pack_array(packer, 0) + +#define CONV_LIST_START(lst) \ + msgpack_pack_array(packer, (size_t) (lst)->lv_len) + +#define CONV_EMPTY_DICT() \ + msgpack_pack_map(packer, 0) + +#define CONV_NIL() \ + msgpack_pack_nil(packer) + +#define CONV_BOOL(num) \ + do { \ + if ((num)) { \ + msgpack_pack_true(packer); \ + } else { \ + msgpack_pack_false(packer); \ + } \ + } while (0) + +#define CONV_UNSIGNED_NUMBER(num) \ + msgpack_pack_uint64(packer, (num)) + +#define CONV_DICT_START(len) \ + msgpack_pack_map(packer, (size_t) (len)) + +#define CONV_DICT_END() + +#define CONV_DICT_AFTER_KEY() + +#define CONV_DICT_BETWEEN_ITEMS() + +#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) + +#define CONV_LIST_END(lst) + +#define CONV_LIST_BETWEEN_ITEMS() + +#define CONV_RECURSE(val, conv_type) \ + return conv_error(_("E952: Unable to dump %s: " \ + "container references itself in %s"), \ + mpstack, objname) + +#define CONV_ALLOW_SPECIAL true + +DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) + +#undef CONV_STRING +#undef CONV_STR_STRING +#undef CONV_EXT_STRING +#undef CONV_NUMBER +#undef CONV_FLOAT +#undef CONV_FUNC +#undef CONV_EMPTY_LIST +#undef CONV_LIST_START +#undef CONV_EMPTY_DICT +#undef CONV_NIL +#undef CONV_BOOL +#undef CONV_UNSIGNED_NUMBER +#undef CONV_DICT_START +#undef CONV_DICT_END +#undef CONV_DICT_AFTER_KEY +#undef CONV_DICT_BETWEEN_ITEMS +#undef CONV_SPECIAL_DICT_KEY_CHECK +#undef CONV_LIST_END +#undef CONV_LIST_BETWEEN_ITEMS +#undef CONV_RECURSE +#undef CONV_ALLOW_SPECIAL diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h new file mode 100644 index 0000000000..9bc665253b --- /dev/null +++ b/src/nvim/eval/encode.h @@ -0,0 +1,75 @@ +#ifndef NVIM_EVAL_ENCODE_H +#define NVIM_EVAL_ENCODE_H + +#include <stddef.h> + +#include <msgpack.h> + +#include "nvim/eval.h" +#include "nvim/garray.h" +#include "nvim/vim.h" // For STRLEN + +/// Convert VimL value to msgpack string +/// +/// @param[out] packer Packer to save results in. +/// @param[in] tv Dumped value. +/// @param[in] objname Object name, used for error message. +/// +/// @return OK in case of success, FAIL otherwise. +int encode_vim_to_msgpack(msgpack_packer *const packer, + typval_T *const tv, + const char *const objname); + +/// Convert VimL value to :echo output +/// +/// @param[out] packer Packer to save results in. +/// @param[in] tv Dumped value. +/// @param[in] objname Object name, used for error message. +/// +/// @return OK in case of success, FAIL otherwise. +int encode_vim_to_echo(garray_T *const packer, + typval_T *const tv, + const char *const objname); + +/// Structure defining state for read_from_list() +typedef struct { + const listitem_T *li; ///< Item currently read. + size_t offset; ///< Byte offset inside the read item. + size_t li_length; ///< Length of the string inside the read item. +} ListReaderState; + +/// Initialize ListReaderState structure +static inline ListReaderState encode_init_lrstate(const list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + return (ListReaderState) { + .li = list->lv_first, + .offset = 0, + .li_length = (list->lv_first->li_tv.vval.v_string == NULL + ? 0 + : STRLEN(list->lv_first->li_tv.vval.v_string)), + }; +} + +/// Array mapping values from SpecialVarValue enum to names +extern const char *const encode_special_var_names[]; + +/// First codepoint in high surrogates block +#define SURROGATE_HI_START 0xD800 + +/// Last codepoint in high surrogates block +#define SURROGATE_HI_END 0xDBFF + +/// First codepoint in low surrogates block +#define SURROGATE_LO_START 0xDC00 + +/// Last codepoint in low surrogates block +#define SURROGATE_LO_END 0xDFFF + +/// First character that needs to be encoded as surrogate pair +#define SURROGATE_FIRST_CHAR 0x10000 + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/encode.h.generated.h" +#endif +#endif // NVIM_EVAL_ENCODE_H diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index bd50d6b829..8ffc0c98ce 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -16,39 +16,52 @@ typedef double float_T; typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; -/* - * Structure to hold an internal variable without a name. - */ +/// Special variable values +typedef enum { + kSpecialVarFalse, ///< v:false + kSpecialVarTrue, ///< v:true + kSpecialVarNull, ///< v:null +} SpecialVarValue; + +/// Variable lock status for typval_T.v_lock +typedef enum { + VAR_UNLOCKED = 0, ///< Not locked. + VAR_LOCKED, ///< User lock, can be unlocked. + VAR_FIXED, ///< Locked forever. +} VarLockStatus; + +/// VimL variable types, for use in typval_T.v_type +typedef enum { + VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. + VAR_NUMBER, ///< Number, .v_number is used. + VAR_STRING, ///< String, .v_string is used. + VAR_FUNC, ///< Function referene, .v_string is used for function name. + VAR_LIST, ///< List, .v_list is used. + VAR_DICT, ///< Dictionary, .v_dict is used. + VAR_FLOAT, ///< Floating-point value, .v_float is used. + VAR_SPECIAL, ///< Special value (true, false, null), .v_special + ///< is used. +} VarType; + +/// Structure that holds an internal variable value typedef struct { - char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */ - char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */ + VarType v_type; ///< Variable type. + VarLockStatus v_lock; ///< Variable lock status. union { - varnumber_T v_number; /* number value */ - float_T v_float; /* floating number value */ - char_u *v_string; /* string value (can be NULL!) */ - list_T *v_list; /* list value (can be NULL!) */ - dict_T *v_dict; /* dict value (can be NULL!) */ - } vval; + varnumber_T v_number; ///< Number, for VAR_NUMBER. + SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. + float_T v_float; ///< Floating-point number, for VAR_FLOAT. + char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. + list_T *v_list; ///< List for VAR_LIST, can be NULL. + dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. + } vval; ///< Actual value. } typval_T; -/* Values for "v_type". */ -#define VAR_UNKNOWN 0 -#define VAR_NUMBER 1 /* "v_number" is used */ -#define VAR_STRING 2 /* "v_string" is used */ -#define VAR_FUNC 3 /* "v_string" is function name */ -#define VAR_LIST 4 /* "v_list" is used */ -#define VAR_DICT 5 /* "v_dict" is used */ -#define VAR_FLOAT 6 /* "v_float" is used */ - /* Values for "dv_scope". */ #define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ #define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not allowed to mask existing functions */ -/* Values for "v_lock". */ -#define VAR_LOCKED 1 /* locked with lock(), can use unlock() */ -#define VAR_FIXED 2 /* locked forever */ - /* * Structure to hold an item of a list: an internal variable without a name. */ @@ -101,26 +114,24 @@ struct dictitem_S { typedef struct dictitem_S dictitem_T; -#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */ -#define DI_FLAGS_RO_SBX 2 /* "di_flags" value: read-only in the sandbox */ -#define DI_FLAGS_FIX 4 /* "di_flags" value: fixed variable, not allocated */ -#define DI_FLAGS_LOCK 8 /* "di_flags" value: locked variable */ +#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable +#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox +#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove() +#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable +#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated -/* - * Structure to hold info about a Dictionary. - */ +/// Structure representing a Dictionary struct dictvar_S { - char dv_lock; /* zero, VAR_LOCKED, VAR_FIXED */ - char dv_scope; /* zero, VAR_SCOPE, VAR_DEF_SCOPE */ - int dv_refcount; /* reference count */ - int dv_copyID; /* ID used by deepcopy() */ - hashtab_T dv_hashtab; /* hashtab that refers to the items */ - dict_T *dv_copydict; /* copied dict used by deepcopy() */ - dict_T *dv_used_next; /* next dict in used dicts list */ - dict_T *dv_used_prev; /* previous dict in used dicts list */ - int internal_refcount; // number of internal references to - // prevent garbage collection - QUEUE watchers; // dictionary key watchers set by user code + VarLockStatus dv_lock; ///< Whole dictionary lock status. + char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if + ///< dictionary represents a scope (i.e. g:, l: …). + int dv_refcount; ///< Reference count. + int dv_copyID; ///< ID used when recursivery traversing a value. + hashtab_T dv_hashtab; ///< Hashtab containing all items. + dict_T *dv_copydict; ///< Copied dict used by deepcopy(). + dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. + dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. + QUEUE watchers; ///< Dictionary key watchers set by user code. }; // structure used for explicit stack while garbage collecting hash tables diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index 44305c69bc..9ef3468284 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -21,6 +21,9 @@ bool libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvopts.args = proc->argv; uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; + if (proc->detach) { + uvproc->uvopts.flags |= UV_PROCESS_DETACHED; + } uvproc->uvopts.exit_cb = exit_cb; uvproc->uvopts.cwd = NULL; uvproc->uvopts.env = NULL; diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 0336eb880a..9bb62891c7 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -29,6 +29,7 @@ } \ } while (0) +static bool process_is_tearing_down = false; bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL { @@ -112,11 +113,22 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL { + process_is_tearing_down = true; kl_iter(WatcherPtr, loop->children, current) { Process *proc = (*current)->data; - uv_kill(proc->pid, SIGTERM); - proc->term_sent = true; - process_stop(proc); + if (proc->detach) { + // Close handles to process without killing it. + CREATE_EVENT(loop->events, process_close_handles, 1, proc); + } else { + if (proc->type == kProcessTypeUv) { + uv_kill(proc->pid, SIGTERM); + proc->term_sent = true; + process_stop(proc); + } else { // kProcessTypePty + process_close_streams(proc); + pty_process_close_master((PtyProcess *)proc); + } + } } // Wait until all children exit @@ -175,9 +187,9 @@ int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1) // being freed) before we have a chance to get the status. proc->refcount++; LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms, - // Until... - got_int || // interrupted by the user - proc->refcount == 1); // job exited + // Until... + got_int // interrupted by the user + || proc->refcount == 1); // job exited // we'll assume that a user frantically hitting interrupt doesn't like // the current job. Signal that it has to be killed. @@ -303,6 +315,10 @@ static void decref(Process *proc) static void process_close(Process *proc) FUNC_ATTR_NONNULL_ARG(1) { + if (process_is_tearing_down && proc->detach && proc->closed) { + // If a detached process dies while tearing down it might get closed twice. + return; + } assert(!proc->closed); proc->closed = true; switch (proc->type) { @@ -333,6 +349,7 @@ static void on_process_exit(Process *proc) DLOG("Stopping process kill timer"); uv_timer_stop(&loop->children_kill_timer); } + // Process handles are closed in the next event loop tick. This is done to // give libuv more time to read data from the OS after the process exits(If // process_close_streams is called with data still in the OS buffer, we lose diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 45edc46b95..e23c8ea60f 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -25,7 +25,7 @@ struct process { Stream *in, *out, *err; process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; - bool closed, term_sent; + bool closed, term_sent, detach; Queue *events; }; @@ -48,7 +48,8 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data) .closed = false, .term_sent = false, .internal_close_cb = NULL, - .internal_exit_cb = NULL + .internal_exit_cb = NULL, + .detach = false }; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 3f19421a75..e8314e02e0 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3,6 +3,7 @@ */ #include <assert.h> +#include <float.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> @@ -50,7 +51,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -274,17 +274,26 @@ static int linelen(int *has_tab) static char_u *sortbuf1; static char_u *sortbuf2; -static int sort_ic; /* ignore case */ -static int sort_nr; /* sort on number */ -static int sort_rx; /* sort on regex instead of skipping it */ +static int sort_ic; ///< ignore case +static int sort_nr; ///< sort on number +static int sort_rx; ///< sort on regex instead of skipping it +static int sort_flt; ///< sort on floating number -static int sort_abort; /* flag to indicate if sorting has been interrupted */ +static int sort_abort; ///< flag to indicate if sorting has been interrupted -/* Struct to store info to be sorted. */ +/// Struct to store info to be sorted. typedef struct { - linenr_T lnum; /* line number */ - long start_col_nr; /* starting column number or number */ - long end_col_nr; /* ending column number */ + linenr_T lnum; ///< line number + long start_col_nr; ///< starting column number or number + long end_col_nr; ///< ending column number + union { + struct { + long start_col_nr; ///< starting column number + long end_col_nr; ///< ending column number + } line; + long value; ///< value if sorting by integer + float_T value_flt; ///< value if sorting by float + } st_u; } sorti_T; @@ -303,21 +312,26 @@ static int sort_compare(const void *s1, const void *s2) if (got_int) sort_abort = TRUE; - /* When sorting numbers "start_col_nr" is the number, not the column - * number. */ - if (sort_nr) - result = l1.start_col_nr == l2.start_col_nr ? 0 - : l1.start_col_nr > l2.start_col_nr ? 1 : -1; - else { - /* We need to copy one line into "sortbuf1", because there is no - * guarantee that the first pointer becomes invalid when obtaining the - * second one. */ - STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr, - l1.end_col_nr - l1.start_col_nr + 1); - sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0; - STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr, - l2.end_col_nr - l2.start_col_nr + 1); - sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0; + // When sorting numbers "start_col_nr" is the number, not the column + // number. + if (sort_nr) { + result = l1.st_u.value == l2.st_u.value + ? 0 : l1.st_u.value > l2.st_u.value + ? 1 : -1; + } else if (sort_flt) { + result = l1.st_u.value_flt == l2.st_u.value_flt + ? 0 : l1.st_u.value_flt > l2.st_u.value_flt + ? 1 : -1; + } else { + // We need to copy one line into "sortbuf1", because there is no + // guarantee that the first pointer becomes invalid when obtaining the + // second one. + STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr, + l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1); + sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0; + STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr, + l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1); + sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = 0; result = sort_ic ? STRICMP(sortbuf1, sortbuf2) : STRCMP(sortbuf1, sortbuf2); @@ -329,9 +343,7 @@ static int sort_compare(const void *s1, const void *s2) return result; } -/* - * ":sort". - */ +// ":sort". void ex_sort(exarg_T *eap) { regmatch_T regmatch; @@ -343,66 +355,79 @@ void ex_sort(exarg_T *eap) char_u *p; char_u *s; char_u *s2; - char_u c; /* temporary character storage */ - int unique = FALSE; + char_u c; // temporary character storage + bool unique = false; long deleted; colnr_T start_col; colnr_T end_col; - int sort_oct; /* sort on octal number */ - int sort_hex; /* sort on hex number */ + int sort_what = 0; - /* Sorting one line is really quick! */ - if (count <= 1) + // Sorting one line is really quick! + if (count <= 1) { return; + } - if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) + if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) { return; + } sortbuf1 = NULL; sortbuf2 = NULL; regmatch.regprog = NULL; sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); - sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0; + sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0; + size_t format_found = 0; for (p = eap->arg; *p != NUL; ++p) { - if (ascii_iswhite(*p)) - ; - else if (*p == 'i') - sort_ic = TRUE; - else if (*p == 'r') - sort_rx = TRUE; - else if (*p == 'n') - sort_nr = 2; - else if (*p == 'o') - sort_oct = 2; - else if (*p == 'x') - sort_hex = 2; - else if (*p == 'u') - unique = TRUE; - else if (*p == '"') /* comment start */ + if (ascii_iswhite(*p)) { + } else if (*p == 'i') { + sort_ic = true; + } else if (*p == 'r') { + sort_rx = true; + } else if (*p == 'n') { + sort_nr = 1; + format_found++; + } else if (*p == 'f') { + sort_flt = 1; + format_found++; + } else if (*p == 'b') { + sort_what = STR2NR_BIN + STR2NR_FORCE; + format_found++; + } else if (*p == 'o') { + sort_what = STR2NR_OCT + STR2NR_FORCE; + format_found++; + } else if (*p == 'x') { + sort_what = STR2NR_HEX + STR2NR_FORCE; + format_found++; + } else if (*p == 'u') { + unique = true; + } else if (*p == '"') { + // comment start break; - else if (check_nextcmd(p) != NULL) { + } else if (check_nextcmd(p) != NULL) { eap->nextcmd = check_nextcmd(p); break; } else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) { - s = skip_regexp(p + 1, *p, TRUE, NULL); + s = skip_regexp(p + 1, *p, true, NULL); if (*s != *p) { EMSG(_(e_invalpat)); goto sortend; } *s = NUL; - /* Use last search pattern if sort pattern is empty. */ + // Use last search pattern if sort pattern is empty. if (s == p + 1) { if (last_search_pat() == NULL) { EMSG(_(e_noprevre)); goto sortend; } regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC); - } else + } else { regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC); - if (regmatch.regprog == NULL) + } + if (regmatch.regprog == NULL) { goto sortend; - p = s; /* continue after the regexp */ + } + p = s; // continue after the regexp regmatch.rm_ic = p_ic; } else { EMSG2(_(e_invarg2), p); @@ -410,28 +435,28 @@ void ex_sort(exarg_T *eap) } } - /* Can only have one of 'n', 'o' and 'x'. */ - if (sort_nr + sort_oct + sort_hex > 2) { + // Can only have one of 'n', 'b', 'o' and 'x'. + if (format_found > 1) { EMSG(_(e_invarg)); goto sortend; } - /* From here on "sort_nr" is used as a flag for any number sorting. */ - sort_nr += sort_oct + sort_hex; + // From here on "sort_nr" is used as a flag for any integer number + // sorting. + sort_nr += sort_what; - /* - * Make an array with all line numbers. This avoids having to copy all - * the lines into allocated memory. - * When sorting on strings "start_col_nr" is the offset in the line, for - * numbers sorting it's the number to sort on. This means the pattern - * matching and number conversion only has to be done once per line. - * Also get the longest line length for allocating "sortbuf". - */ + // Make an array with all line numbers. This avoids having to copy all + // the lines into allocated memory. + // When sorting on strings "start_col_nr" is the offset in the line, for + // numbers sorting it's the number to sort on. This means the pattern + // matching and number conversion only has to be done once per line. + // Also get the longest line length for allocating "sortbuf". for (lnum = eap->line1; lnum <= eap->line2; ++lnum) { s = ml_get(lnum); len = (int)STRLEN(s); - if (maxlen < len) + if (maxlen < len) { maxlen = len; + } start_col = 0; end_col = len; @@ -439,36 +464,57 @@ void ex_sort(exarg_T *eap) if (sort_rx) { start_col = (colnr_T)(regmatch.startp[0] - s); end_col = (colnr_T)(regmatch.endp[0] - s); - } else + } else { start_col = (colnr_T)(regmatch.endp[0] - s); - } else if (regmatch.regprog != NULL) + } + } else if (regmatch.regprog != NULL) { end_col = 0; + } - if (sort_nr) { - /* Make sure vim_str2nr doesn't read any digits past the end - * of the match, by temporarily terminating the string there */ + if (sort_nr || sort_flt) { + // Make sure vim_str2nr doesn't read any digits past the end + // of the match, by temporarily terminating the string there s2 = s + end_col; c = *s2; *s2 = NUL; - /* Sorting on number: Store the number itself. */ + // Sorting on number: Store the number itself. p = s + start_col; - if (sort_hex) - s = skiptohex(p); - else - s = skiptodigit(p); - if (s > p && s[-1] == '-') - --s; /* include preceding negative sign */ - if (*s == NUL) - /* empty line should sort before any number */ - nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; - else - vim_str2nr(s, NULL, NULL, sort_oct, sort_hex, - &nrs[lnum - eap->line1].start_col_nr, NULL); + if (sort_nr) { + if (sort_what & STR2NR_HEX) { + s = skiptohex(p); + } else if (sort_what & STR2NR_BIN) { + s = (char_u *)skiptobin((char *)p); + } else { + s = skiptodigit(p); + } + if (s > p && s[-1] == '-') { + s--; // include preceding negative sign + } + if (*s == NUL) { + // empty line should sort before any number + nrs[lnum - eap->line1].st_u.value = -MAXLNUM; + } else { + vim_str2nr(s, NULL, NULL, sort_what, + &nrs[lnum - eap->line1].st_u.value, NULL, 0); + } + } else { + s = skipwhite(p); + if (*s == '+') { + s = skipwhite(s + 1); + } + + if (*s == NUL) { + // empty line should sort before any number + nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX; + } else { + nrs[lnum - eap->line1].st_u.value_flt = strtod((char *)s, NULL); + } + } *s2 = c; } else { - /* Store the column to sort at. */ - nrs[lnum - eap->line1].start_col_nr = start_col; - nrs[lnum - eap->line1].end_col_nr = end_col; + // Store the column to sort at. + nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col; + nrs[lnum - eap->line1].st_u.line.end_col_nr = end_col; } nrs[lnum - eap->line1].lnum = lnum; @@ -479,45 +525,50 @@ void ex_sort(exarg_T *eap) goto sortend; } - /* Allocate a buffer that can hold the longest line. */ + // Allocate a buffer that can hold the longest line. sortbuf1 = xmalloc(maxlen + 1); sortbuf2 = xmalloc(maxlen + 1); - /* Sort the array of line numbers. Note: can't be interrupted! */ + // Sort the array of line numbers. Note: can't be interrupted! qsort((void *)nrs, count, sizeof(sorti_T), sort_compare); if (sort_abort) goto sortend; - /* Insert the lines in the sorted order below the last one. */ + // Insert the lines in the sorted order below the last one. lnum = eap->line2; for (i = 0; i < count; ++i) { s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum); if (!unique || i == 0 || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) { - if (ml_append(lnum++, s, (colnr_T)0, FALSE) == FAIL) + // Copy the line into a buffer, it may become invalid in + // ml_append(). And it's needed for "unique". + STRCPY(sortbuf1, s); + if (ml_append(lnum++, sortbuf1, (colnr_T)0, false) == FAIL) { break; - if (unique) - STRCPY(sortbuf1, s); + } } fast_breakcheck(); if (got_int) goto sortend; } - /* delete the original lines if appending worked */ - if (i == count) - for (i = 0; i < count; ++i) - ml_delete(eap->line1, FALSE); - else + // delete the original lines if appending worked + if (i == count) { + for (i = 0; i < count; ++i) { + ml_delete(eap->line1, false); + } + } else { count = 0; + } - /* Adjust marks for deleted (or added) lines and prepare for displaying. */ + // Adjust marks for deleted (or added) lines and prepare for displaying. deleted = (long)(count - (lnum - eap->line2)); - if (deleted > 0) + if (deleted > 0) { mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted); - else if (deleted < 0) + } else if (deleted < 0) { mark_adjust(eap->line2, MAXLNUM, -deleted, 0L); + } changed_lines(eap->line1, 0, eap->line2 + 1, -deleted); curwin->w_cursor.lnum = eap->line1; @@ -528,8 +579,9 @@ sortend: xfree(sortbuf1); xfree(sortbuf2); vim_regfree(regmatch.regprog); - if (got_int) + if (got_int) { EMSG(_(e_interr)); + } } /* @@ -598,13 +650,13 @@ void ex_retab(exarg_T *eap) num_tabs += num_spaces / new_ts; num_spaces -= (num_spaces / new_ts) * new_ts; } - if (curbuf->b_p_et || got_tab || - (num_spaces + num_tabs < len)) { - if (did_undo == FALSE) { - did_undo = TRUE; + if (curbuf->b_p_et || got_tab + || (num_spaces + num_tabs < len)) { + if (did_undo == false) { + did_undo = true; if (u_save((linenr_T)(lnum - 1), - (linenr_T)(lnum + 1)) == FAIL) { - new_line = NULL; /* flag out-of-memory */ + (linenr_T)(lnum + 1)) == FAIL) { + new_line = NULL; // flag out-of-memory break; } } @@ -669,9 +721,17 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) { char_u *str; linenr_T l; - linenr_T extra; /* Num lines added before line1 */ - linenr_T num_lines; /* Num lines moved */ - linenr_T last_line; /* Last line in file after adding new text */ + linenr_T extra; // Num lines added before line1 + linenr_T num_lines; // Num lines moved + linenr_T last_line; // Last line in file after adding new text + + // Moving lines seems to corrupt the folds, delete folding info now + // and recreate it when finished. Don't do this for manual folding, it + // would delete all folds. + bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin); + if (isFolded) { + deleteFoldRecurse(&curwin->w_folds); + } if (dest >= line1 && dest < line2) { EMSG(_("E134: Move lines into themselves")); @@ -756,8 +816,14 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) if (dest > last_line + 1) dest = last_line + 1; changed_lines(line1, 0, dest, 0L); - } else + } else { changed_lines(dest + 1, 0, line1 + num_lines, 0L); + } + + // recreate folds + if (isFolded) { + foldUpdateAll(curwin); + } return OK; } @@ -949,30 +1015,26 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) xfree(newcmd); } -/* - * do_filter: filter lines through a command given by the user - * - * We mostly use temp files and the call_shell() routine here. This would - * normally be done using pipes on a UNIX machine, but this is more portable - * to non-unix machines. The call_shell() routine needs to be able - * to deal with redirection somehow, and should handle things like looking - * at the PATH env. variable, and adding reasonable extensions to the - * command name given by the user. All reasonable versions of call_shell() - * do this. - * Alternatively, if on Unix and redirecting input or output, but not both, - * and the 'shelltemp' option isn't set, use pipes. - * We use input redirection if do_in is TRUE. - * We use output redirection if do_out is TRUE. - */ -static void -do_filter ( +// do_filter: filter lines through a command given by the user +// +// We mostly use temp files and the call_shell() routine here. This would +// normally be done using pipes on a Unix system, but this is more portable +// to non-Unix systems. The call_shell() routine needs to be able +// to deal with redirection somehow, and should handle things like looking +// at the PATH env. variable, and adding reasonable extensions to the +// command name given by the user. All reasonable versions of call_shell() +// do this. +// Alternatively, if on Unix and redirecting input or output, but not both, +// and the 'shelltemp' option isn't set, use pipes. +// We use input redirection if do_in is TRUE. +// We use output redirection if do_out is TRUE. +static void do_filter( linenr_T line1, linenr_T line2, exarg_T *eap, /* for forced 'ff' and 'fenc' */ char_u *cmd, int do_in, - int do_out -) + int do_out) { char_u *itmp = NULL; char_u *otmp = NULL; @@ -1295,15 +1357,17 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) #endif size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL. - + len += is_fish_shell ? sizeof("begin; ""; end") - 1 : sizeof("("")") - 1; - if (itmp != NULL) + if (itmp != NULL) { len += STRLEN(itmp) + sizeof(" { "" < "" } ") - 1; - if (otmp != NULL) + } + if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), - char_u *buf = xmalloc(len); + } + char *const buf = xmalloc(len); #if defined(UNIX) // Put delimiters around the command (for concatenated commands) when @@ -1311,19 +1375,19 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) if (itmp != NULL || otmp != NULL) { char *fmt = is_fish_shell ? "begin; %s; end" : "(%s)"; - vim_snprintf((char *)buf, len, fmt, (char *)cmd); + vim_snprintf(buf, len, fmt, (char *)cmd); } else { - STRCPY(buf, cmd); + strncpy(buf, (char *) cmd, len); } if (itmp != NULL) { - STRCAT(buf, " < "); - STRCAT(buf, itmp); + strncat(buf, " < ", len); + strncat(buf, (char *) itmp, len); } #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. - STRCPY(buf, cmd); + strncpy(buf, cmd, len); if (itmp != NULL) { char_u *p; @@ -1331,55 +1395,56 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) // Don't do this when 'shellquote' is not empty, otherwise the // redirection would be inside the quotes. if (*p_shq == NUL) { - p = vim_strchr(buf, '|'); - if (p != NULL) + p = strchr(buf, '|'); + if (p != NULL) { *p = NUL; + } } - STRCAT(buf, " < "); - STRCAT(buf, itmp); + strncat(buf, " < ", len); + strncat(buf, (char *) itmp, len); if (*p_shq == NUL) { - p = vim_strchr(cmd, '|'); + p = strchr(cmd, '|'); if (p != NULL) { - STRCAT(buf, " "); // Insert a space before the '|' for DOS - STRCAT(buf, p); + strncat(buf, " ", len); // Insert a space before the '|' for DOS + strncat(buf, p, len); } } } #endif - if (otmp != NULL) - append_redir(buf, (int)len, p_srr, otmp); - - return buf; + if (otmp != NULL) { + append_redir(buf, len, (char *) p_srr, (char *) otmp); + } + return (char_u *) buf; } -/* - * Append output redirection for file "fname" to the end of string buffer - * "buf[buflen]" - * Works with the 'shellredir' and 'shellpipe' options. - * The caller should make sure that there is enough room: - * STRLEN(opt) + STRLEN(fname) + 3 - */ -void append_redir(char_u *buf, int buflen, char_u *opt, char_u *fname) +/// Append output redirection for the given file to the end of the buffer +/// +/// @param[out] buf Buffer to append to. +/// @param[in] buflen Buffer length. +/// @param[in] opt Separator or format string to append: will append +/// `printf(' ' . opt, fname)` if `%s` is found in `opt` or +/// a space, opt, a space and then fname if `%s` is not found +/// there. +/// @param[in] fname File name to append. +void append_redir(char *const buf, const size_t buflen, + const char *const opt, const char *const fname) { - char_u *p; - char_u *end; - - end = buf + STRLEN(buf); - /* find "%s" */ - for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p) { - if (p[1] == 's') /* found %s */ + char *const end = buf + strlen(buf); + // find "%s" + const char *p = opt; + for (; (p = strchr(p, '%')) != NULL; p++) { + if (p[1] == 's') { // found %s break; - if (p[1] == '%') /* skip %% */ - ++p; + } else if (p[1] == '%') { // skip %% + p++; + } } if (p != NULL) { - *end = ' '; /* not really needed? Not with sh, ksh or bash */ - vim_snprintf((char *)end + 1, (size_t)(buflen - (end + 1 - buf)), - (char *)opt, (char *)fname); - } else - vim_snprintf((char *)end, (size_t)(buflen - (end - buf)), - " %s %s", - (char *)opt, (char *)fname); + *end = ' '; // not really needed? Not with sh, ksh or bash + vim_snprintf(end + 1, (size_t) (buflen - (end + 1 - buf)), opt, fname); + } else { + vim_snprintf(end, (size_t) (buflen - (end - buf)), " %s %s", opt, fname); + } } void print_line_no_prefix(linenr_T lnum, int use_number, int list) @@ -1475,8 +1540,11 @@ void ex_file(exarg_T *eap) if (rename_buffer(eap->arg) == FAIL) return; } - /* print full file name if :cd used */ - fileinfo(FALSE, FALSE, eap->forceit); + + if (!shortmess(SHM_FILEINFO)) { + // print full file name if :cd used + fileinfo(false, false, eap->forceit); + } } /* @@ -1555,15 +1623,14 @@ int do_write(exarg_T *eap) } } - /* - * Writing to the current file is not allowed in readonly mode - * and a file name is required. - * "nofile" and "nowrite" buffers cannot be written implicitly either. - */ - if (!other && ( - bt_dontwrite_msg(curbuf) || - check_fname() == FAIL || check_readonly(&eap->forceit, curbuf))) + // Writing to the current file is not allowed in readonly mode + // and a file name is required. + // "nofile" and "nowrite" buffers cannot be written implicitly either. + if (!other && (bt_dontwrite_msg(curbuf) + || check_fname() == FAIL + || check_readonly(&eap->forceit, curbuf))) { goto theend; + } if (!other) { ffname = curbuf->b_ffname; @@ -1688,7 +1755,7 @@ check_overwrite ( && os_file_exists(ffname)) { if (!eap->forceit && !eap->append) { #ifdef UNIX - /* with UNIX it is possible to open a directory */ + // It is possible to open a directory on Unix. if (os_isdir(ffname)) { EMSG2(_(e_isadir2), ffname); return FAIL; @@ -1989,7 +2056,7 @@ do_ecmd ( char_u *free_fname = NULL; int retval = FAIL; long n; - linenr_T lnum; + pos_T orig_pos; linenr_T topline = 0; int newcol = -1; int solcol = -1; @@ -2059,15 +2126,13 @@ do_ecmd ( if ((command != NULL || newlnum > (linenr_T)0) && *get_vim_var_str(VV_SWAPCOMMAND) == NUL) { - char_u *p; - - /* Set v:swapcommand for the SwapExists autocommands. */ - size_t len = (command != NULL) ? STRLEN(command) + 3 : 30; - p = xmalloc(len); + // Set v:swapcommand for the SwapExists autocommands. + const size_t len = (command != NULL) ? STRLEN(command) + 3 : 30; + char *const p = xmalloc(len); if (command != NULL) { - vim_snprintf((char *)p, len, ":%s\r", command); + vim_snprintf(p, len, ":%s\r", command); } else { - vim_snprintf((char *)p, len, "%" PRId64 "G", (int64_t)newlnum); + vim_snprintf(p, len, "%" PRId64 "G", (int64_t)newlnum); } set_vim_var_string(VV_SWAPCOMMAND, p, -1); did_set_swapcommand = TRUE; @@ -2112,7 +2177,6 @@ do_ecmd ( goto theend; if (buf->b_ml.ml_mfp == NULL) { /* no memfile yet */ oldbuf = FALSE; - buf->b_nwindows = 0; } else { /* existing memfile */ oldbuf = TRUE; (void)buf_check_timestamp(buf, FALSE); @@ -2138,7 +2202,7 @@ do_ecmd ( * Make the (new) buffer the one used by the current window. * If the old buffer becomes unused, free it if ECMD_HIDE is FALSE. * If the current buffer was empty and has no file name, curbuf - * is returned by buflist_new(). + * is returned by buflist_new(), nothing to do here. */ if (buf != curbuf) { /* @@ -2193,16 +2257,15 @@ do_ecmd ( delbuf_msg(new_name); /* frees new_name */ goto theend; } - if (buf == curbuf) /* already in new buffer */ - auto_buf = TRUE; - else { - /* - * <VN> We could instead free the synblock - * and re-attach to buffer, perhaps. - */ - if (curwin->w_buffer != NULL && - curwin->w_s == &(curwin->w_buffer->b_s)) + if (buf == curbuf) { // already in new buffer + auto_buf = true; + } else { + // <VN> We could instead free the synblock + // and re-attach to buffer, perhaps. + if (curwin->w_buffer != NULL + && curwin->w_s == &(curwin->w_buffer->b_s)) { curwin->w_s = &(buf->b_s); + } curwin->w_buffer = buf; curbuf = buf; @@ -2225,16 +2288,15 @@ do_ecmd ( } xfree(new_name); au_new_curbuf = NULL; - } else - ++curbuf->b_nwindows; + } curwin->w_pcmark.lnum = 1; curwin->w_pcmark.col = 0; - } else { /* !other_file */ - if ( - (flags & ECMD_ADDBUF) || - check_fname() == FAIL) + } else { // !other_file + if ((flags & ECMD_ADDBUF) + || check_fname() == FAIL) { goto theend; + } oldbuf = (flags & ECMD_OLDBUF); } @@ -2353,7 +2415,7 @@ do_ecmd ( * Careful: open_buffer() and apply_autocmds() may change the current * buffer and window. */ - lnum = curwin->w_cursor.lnum; + orig_pos = curwin->w_cursor; topline = curwin->w_topline; if (!oldbuf) { /* need to read the file */ swap_exists_action = SEA_DIALOG; @@ -2381,11 +2443,9 @@ do_ecmd ( } check_arg_idx(curwin); - /* - * If autocommands change the cursor position or topline, we should - * keep it. - */ - if (curwin->w_cursor.lnum != lnum) { + // If autocommands change the cursor position or topline, we should keep + // it. Also when it moves within a line. + if (!equalpos(curwin->w_cursor, orig_pos)) { newlnum = curwin->w_cursor.lnum; newcol = curwin->w_cursor.col; } @@ -2456,7 +2516,9 @@ do_ecmd ( msg_scroll = msg_scroll_save; msg_scrolled_ign = TRUE; - fileinfo(FALSE, TRUE, FALSE); + if (!shortmess(SHM_FILEINFO)) { + fileinfo(false, true, false); + } msg_scrolled_ign = FALSE; } @@ -2861,6 +2923,8 @@ void do_sub(exarg_T *eap) static int do_list = FALSE; /* list last line with subs. */ static int do_number = FALSE; /* list last line with line nr*/ static int do_ic = 0; /* ignore case flag */ + int save_do_all; // remember user specified 'g' flag + int save_do_ask; // remember user specified 'c' flag char_u *pat = NULL, *sub = NULL; /* init for GCC */ int delimiter; int sublen; @@ -2988,7 +3052,7 @@ void do_sub(exarg_T *eap) // The number of lines joined is the number of lines in the range linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 // plus one extra line if not at the end of file. - + eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0; + + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); if (joined_lines_count > 1) { do_join(joined_lines_count, FALSE, TRUE, FALSE, true); sub_nsubs = joined_lines_count - 1; @@ -3050,12 +3114,14 @@ void do_sub(exarg_T *eap) break; ++cmd; } - if (do_count) + if (do_count) { do_ask = FALSE; + } - /* - * check for a trailing count - */ + save_do_all = do_all; + save_do_ask = do_ask; + + // check for a trailing count cmd = skipwhite(cmd); if (ascii_isdigit(*cmd)) { i = getdigits_long(&cmd); @@ -3754,8 +3820,12 @@ skip: /* Cursor position may require updating */ changed_window_setting(); - vim_regfree(regmatch.regprog); -} + vim_regfree(regmatch.regprog); + + // Restore the flag values, they can be used for ":&&". + do_all = save_do_all; + do_ask = save_do_ask; + } /* * Give message for number of substitutions. @@ -4744,6 +4814,7 @@ void ex_helptags(exarg_T *eap) WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); if (dirname == NULL || !os_isdir(dirname)) { EMSG2(_("E150: Not a directory: %s"), eap->arg); + xfree(dirname); return; } @@ -4841,16 +4912,13 @@ helptags_one ( int fi; char_u *s; char_u *fname; - int dirlen; int utf8 = MAYBE; int this_utf8; int firstline; int mix = FALSE; /* detected mixed encodings */ - /* - * Find all *.txt files. - */ - dirlen = (int)STRLEN(dir); + // Find all *.txt files. + size_t dirlen = STRLEN(dir); STRCPY(NameBuff, dir); STRCAT(NameBuff, "/**/*"); STRCAT(NameBuff, ext); @@ -4872,7 +4940,7 @@ helptags_one ( */ STRCPY(NameBuff, dir); add_pathsep((char *)NameBuff); - STRCAT(NameBuff, tagfname); + STRNCAT(NameBuff, tagfname, sizeof(NameBuff) - dirlen - 2); fd_tags = mch_fopen((char *)NameBuff, "w"); if (fd_tags == NULL) { EMSG2(_("E152: Cannot open %s for writing"), NameBuff); @@ -4978,7 +5046,9 @@ helptags_one ( /* * Sort the tags. */ - sort_strings((char_u **)ga.ga_data, ga.ga_len); + if (ga.ga_data != NULL) { + sort_strings((char_u **)ga.ga_data, ga.ga_len); + } /* * Check for duplicates. @@ -5746,13 +5816,14 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) switch (cmd_idx) { case SIGNCMD_DEFINE: - if (STRNCMP(last, "texthl", p - last) == 0 || - STRNCMP(last, "linehl", p - last) == 0) + if (STRNCMP(last, "texthl", p - last) == 0 + || STRNCMP(last, "linehl", p - last) == 0) { xp->xp_context = EXPAND_HIGHLIGHT; - else if (STRNCMP(last, "icon", p - last) == 0) + } else if (STRNCMP(last, "icon", p - last) == 0) { xp->xp_context = EXPAND_FILES; - else + } else { xp->xp_context = EXPAND_NOTHING; + } break; case SIGNCMD_PLACE: if (STRNCMP(last, "name", p - last) == 0) diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b7a3505c99..04fd88cc8d 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -34,6 +34,7 @@ local ADDR_ARGUMENTS = 2 local ADDR_LOADED_BUFFERS = 3 local ADDR_BUFFERS = 4 local ADDR_TABS = 5 +local ADDR_QUICKFIX = 6 -- The following table is described in ex_cmds_defs.h file. return { @@ -374,6 +375,12 @@ return { func='ex_cd', }, { + command='cdo', + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), + addr_type=ADDR_QUICKFIX, + func='ex_listdo', + }, + { command='center', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), addr_type=ADDR_LINES, @@ -391,6 +398,14 @@ return { addr_type=ADDR_LINES, func='ex_cfile', }, + -- Even though 'cfdo' is alphabetically lower than 'cfile', it is after + -- 'cfile' in this cmd list to support the existing ":cf" abbreviation. + { + command='cfdo', + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), + addr_type=ADDR_QUICKFIX, + func='ex_listdo', + }, { command='cfirst', flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), @@ -1010,12 +1025,6 @@ return { func='ex_helpclose', }, { - command='helpfind', - flags=bit.bor(EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, - func='ex_ni', - }, - { command='helpgrep', flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG), addr_type=ADDR_LINES, @@ -1286,6 +1295,12 @@ return { func='do_cscope', }, { + command='ldo', + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), + addr_type=ADDR_QUICKFIX, + func='ex_listdo', + }, + { command='left', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), addr_type=ADDR_LINES, @@ -1315,6 +1330,14 @@ return { addr_type=ADDR_LINES, func='ex_cfile', }, + -- Even though 'lfdo' is alphabetically lower than 'lfile', it is after + -- 'lfile' in this cmd list to support the existing ":lf" abbreviation. + { + command='lfdo', + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), + addr_type=ADDR_QUICKFIX, + func='ex_listdo', + }, { command='lfirst', flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), @@ -2552,6 +2575,18 @@ return { func='ex_copymove', }, { + command='tcd', + flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_cd', + }, + { + command='tchdir', + flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_cd', + }, + { command='tNext', flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), addr_type=ADDR_LINES, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 87a6283310..df387f9a60 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -6,6 +6,7 @@ #include <inttypes.h> #include <stdbool.h> #include <string.h> +#include <fcntl.h> #include "nvim/vim.h" #include "nvim/ascii.h" @@ -56,23 +57,23 @@ typedef struct scriptitem_S { char_u *sn_name; bool file_id_valid; FileID file_id; - int sn_prof_on; /* TRUE when script is/was profiled */ - int sn_pr_force; /* forceit: profile functions in this script */ - proftime_T sn_pr_child; /* time set when going into first child */ - int sn_pr_nest; /* nesting for sn_pr_child */ - /* profiling the script as a whole */ - int sn_pr_count; /* nr of times sourced */ - proftime_T sn_pr_total; /* time spent in script + children */ - proftime_T sn_pr_self; /* time spent in script itself */ - proftime_T sn_pr_start; /* time at script start */ - proftime_T sn_pr_children; /* time in children after script start */ - /* profiling the script per line */ - garray_T sn_prl_ga; /* things stored for every line */ - proftime_T sn_prl_start; /* start time for current line */ - proftime_T sn_prl_children; /* time spent in children for this line */ - proftime_T sn_prl_wait; /* wait start time for current line */ - int sn_prl_idx; /* index of line being timed; -1 if none */ - int sn_prl_execed; /* line being timed was executed */ + bool sn_prof_on; ///< true when script is/was profiled + int sn_pr_force; ///< forceit: profile functions in this script + proftime_T sn_pr_child; ///< time set when going into first child + int sn_pr_nest; ///< nesting for sn_pr_child + // profiling the script as a whole + int sn_pr_count; ///< nr of times sourced + proftime_T sn_pr_total; ///< time spent in script + children + proftime_T sn_pr_self; ///< time spent in script itself + proftime_T sn_pr_start; ///< time at script start + proftime_T sn_pr_children; ///< time in children after script start + // profiling the script per line + garray_T sn_prl_ga; ///< things stored for every line + proftime_T sn_prl_start; ///< start time for current line + proftime_T sn_prl_children; ///< time spent in children for this line + proftime_T sn_prl_wait; ///< wait start time for current line + linenr_T sn_prl_idx; ///< index of line being timed; -1 if none + int sn_prl_execed; ///< line being timed was executed } scriptitem_T; static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; @@ -80,9 +81,9 @@ static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; /* Struct used in sn_prl_ga for every line of a script. */ typedef struct sn_prl_S { - int snp_count; /* nr of times line was executed */ - proftime_T sn_prl_total; /* time spent in a line + children */ - proftime_T sn_prl_self; /* time spent in a line itself */ + int snp_count; ///< nr of times line was executed + proftime_T sn_prl_total; ///< time spent in a line + children + proftime_T sn_prl_self; ///< time spent in a line itself } sn_prl_T; /* @@ -92,18 +93,18 @@ typedef struct sn_prl_S { * sourcing can be done recursively. */ struct source_cookie { - FILE *fp; /* opened file for sourcing */ - char_u *nextline; /* if not NULL: line that was read ahead */ - int finished; /* ":finish" used */ + FILE *fp; ///< opened file for sourcing + char_u *nextline; ///< if not NULL: line that was read ahead + int finished; ///< ":finish" used #if defined(USE_CRNL) - int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */ - int error; /* TRUE if LF found after CR-LF */ + int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS + int error; ///< TRUE if LF found after CR-LF #endif - linenr_T breakpoint; /* next line with breakpoint or zero */ - char_u *fname; /* name of sourced file */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of sourced file */ - vimconv_T conv; /* type of conversion */ + linenr_T breakpoint; ///< next line with breakpoint or zero + char_u *fname; ///< name of sourced file + int dbg_tick; ///< debug_tick when breakpoint was set + int level; ///< top nesting level of sourced file + vimconv_T conv; ///< type of conversion }; # define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) @@ -271,7 +272,7 @@ void do_debug(char_u *cmd) xfree(cmdline); } - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); } xfree(cmdline); @@ -280,7 +281,7 @@ void do_debug(char_u *cmd) redraw_all_later(NOT_VALID); need_wait_return = FALSE; msg_scroll = save_msg_scroll; - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); State = save_State; did_emsg = save_did_emsg; cmd_silent = save_cmd_silent; @@ -391,12 +392,12 @@ int dbg_check_skipped(exarg_T *eap) * This is a grow-array of structs. */ struct debuggy { - int dbg_nr; /* breakpoint number */ - int dbg_type; /* DBG_FUNC or DBG_FILE */ - char_u *dbg_name; /* function or file name */ - regprog_T *dbg_prog; /* regexp program */ - linenr_T dbg_lnum; /* line number in function or file */ - int dbg_forceit; /* ! used */ + int dbg_nr; ///< breakpoint number + int dbg_type; ///< DBG_FUNC or DBG_FILE + char_u *dbg_name; ///< function or file name + regprog_T *dbg_prog; ///< regexp program + linenr_T dbg_lnum; ///< line number in function or file + int dbg_forceit; ///< ! used }; static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; @@ -431,14 +432,12 @@ dbg_parsearg ( bp = &DEBUGGY(gap, gap->ga_len); - /* Find "func" or "file". */ - if (STRNCMP(p, "func", 4) == 0) + // Find "func" or "file". + if (STRNCMP(p, "func", 4) == 0) { bp->dbg_type = DBG_FUNC; - else if (STRNCMP(p, "file", 4) == 0) + } else if (STRNCMP(p, "file", 4) == 0) { bp->dbg_type = DBG_FILE; - else if ( - gap != &prof_ga && - STRNCMP(p, "here", 4) == 0) { + } else if (gap != &prof_ga && STRNCMP(p, "here", 4) == 0) { if (curbuf->b_ffname == NULL) { EMSG(_(e_noname)); return FAIL; @@ -451,16 +450,15 @@ dbg_parsearg ( } p = skipwhite(p + 4); - /* Find optional line number. */ - if (here) + // Find optional line number. + if (here) { bp->dbg_lnum = curwin->w_cursor.lnum; - else if ( - gap != &prof_ga && - ascii_isdigit(*p)) { + } else if (gap != &prof_ga && ascii_isdigit(*p)) { bp->dbg_lnum = getdigits_long(&p); p = skipwhite(p); - } else + } else { bp->dbg_lnum = 0; + } /* Find the function or file name. Don't accept a function name with (). */ if ((!here && *p == NUL) @@ -562,13 +560,14 @@ void ex_breakdel(exarg_T *eap) } if (ascii_isdigit(*eap->arg)) { - /* ":breakdel {nr}" */ - nr = atol((char *)eap->arg); - for (int i = 0; i < gap->ga_len; ++i) + // ":breakdel {nr}" + nr = atoi((char *)eap->arg); + for (int i = 0; i < gap->ga_len; ++i) { if (DEBUGGY(gap, i).dbg_nr == nr) { todel = i; break; } + } } else if (*eap->arg == '*') { todel = 0; del_all = TRUE; @@ -601,11 +600,13 @@ void ex_breakdel(exarg_T *eap) --gap->ga_len; if (todel < gap->ga_len) memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), - (gap->ga_len - todel) * sizeof(struct debuggy)); - if (eap->cmdidx == CMD_breakdel) + (size_t)(gap->ga_len - todel) * sizeof(struct debuggy)); + if (eap->cmdidx == CMD_breakdel) { ++debug_tick; - if (!del_all) + } + if (!del_all) { break; + } } /* If all breakpoints were removed clear the array. */ @@ -696,14 +697,13 @@ debuggy_find ( /* Skip entries that are not useful or are for a line that is beyond * an already found breakpoint. */ bp = &DEBUGGY(gap, i); - if (((bp->dbg_type == DBG_FILE) == file && ( - gap == &prof_ga || - (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) { - /* - * Save the value of got_int and reset it. We don't want a - * previous interruption cancel matching, only hitting CTRL-C - * while matching should abort it. - */ + if (((bp->dbg_type == DBG_FILE) == file + && (gap == &prof_ga + || (bp->dbg_lnum > after + && (lnum == 0 || bp->dbg_lnum < lnum))))) { + // Save the value of got_int and reset it. We don't want a + // previous interruption cancel matching, only hitting CTRL-C + // while matching should abort it. prev_got_int = got_int; got_int = FALSE; if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) { @@ -809,8 +809,8 @@ void ex_pydo3(exarg_T *eap) /* Command line expansion for :profile. */ static enum { - PEXP_SUBCMD, /* expand :profile sub-commands */ - PEXP_FUNC /* expand :profile func {funcname} */ + PEXP_SUBCMD, ///< expand :profile sub-commands + PEXP_FUNC ///< expand :profile func {funcname} } pexpand_what; static char *pexpand_cmds[] = { @@ -891,7 +891,7 @@ static void profile_reset(void) for (int id = 1; id <= script_items.ga_len; id++) { scriptitem_T *si = &SCRIPT_ITEM(id); if (si->sn_prof_on) { - si->sn_prof_on = 0; + si->sn_prof_on = false; si->sn_pr_force = 0; si->sn_pr_child = profile_zero(); si->sn_pr_nest = 0; @@ -948,7 +948,7 @@ static void profile_init(scriptitem_T *si) ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100); si->sn_prl_idx = -1; - si->sn_prof_on = TRUE; + si->sn_prof_on = true; si->sn_pr_nest = 0; } @@ -1241,28 +1241,31 @@ static void add_bufnum(int *bufnrs, int *bufnump, int nr) *bufnump = *bufnump + 1; } -/* - * Return TRUE if any buffer was changed and cannot be abandoned. - * That changed buffer becomes the current buffer. - */ -int -check_changed_any ( - int hidden /* Only check hidden buffers */ -) +/// Check if any buffer was changed and cannot be abandoned. +/// That changed buffer becomes the current buffer. +/// When "unload" is true the current buffer is unloaded instead of making it +/// hidden. This is used for ":q!". +/// +/// @param[in] hidden specifies whether to check only hidden buffers. +/// @param[in] unload specifies whether to unload, instead of hide, the buffer. +/// +/// @returns true if any buffer is changed and cannot be abandoned +int check_changed_any(bool hidden, bool unload) { - int ret = FALSE; + bool ret = false; int save; int i; int bufnum = 0; - int bufcount = 0; + size_t bufcount = 0; int *bufnrs; FOR_ALL_BUFFERS(buf) { ++bufcount; } - if (bufcount == 0) - return FALSE; + if (bufcount == 0) { + return false; + } bufnrs = xmalloc(sizeof(*bufnrs) * bufcount); @@ -1346,9 +1349,10 @@ check_changed_any ( } buf_found: - /* Open the changed buffer in the current window. */ - if (buf != curbuf) - set_curbuf(buf, DOBUF_GOTO); + // Open the changed buffer in the current window. + if (buf != curbuf) { + set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO); + } theend: xfree(bufnrs); @@ -1519,7 +1523,7 @@ do_arglist ( didone = TRUE; xfree(ARGLIST[match].ae_fname); memmove(ARGLIST + match, ARGLIST + match + 1, - (ARGCOUNT - match - 1) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T)); --ALIST(curwin)->al_ga.ga_len; if (curwin->w_arg_idx > match) --curwin->w_arg_idx; @@ -1536,9 +1540,7 @@ do_arglist ( int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); ga_clear(&new_ga); - if (i == FAIL) - return FAIL; - if (exp_count == 0) { + if (i == FAIL || exp_count == 0) { EMSG(_(e_nomatch)); return FAIL; } @@ -1701,10 +1703,11 @@ void ex_argument(exarg_T *eap) { int i; - if (eap->addr_count > 0) - i = eap->line2 - 1; - else + if (eap->addr_count > 0) { + i = (int)eap->line2 - 1; + } else { i = curwin->w_arg_idx; + } do_argfile(eap, i); } @@ -1843,22 +1846,25 @@ void ex_argadd(exarg_T *eap) void ex_argdelete(exarg_T *eap) { if (eap->addr_count > 0) { - /* ":1,4argdel": Delete all arguments in the range. */ - if (eap->line2 > ARGCOUNT) + // ":1,4argdel": Delete all arguments in the range. + if (eap->line2 > ARGCOUNT) { eap->line2 = ARGCOUNT; - int n = eap->line2 - eap->line1 + 1; - if (*eap->arg != NUL || n <= 0) + } + linenr_T n = eap->line2 - eap->line1 + 1; + if (*eap->arg != NUL || n <= 0) { EMSG(_(e_invarg)); - else { - for (int i = eap->line1; i <= eap->line2; ++i) + } else { + for (linenr_T i = eap->line1; i <= eap->line2; ++i) { xfree(ARGLIST[i - 1].ae_fname); + } memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2, - (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T))); - ALIST(curwin)->al_ga.ga_len -= n; - if (curwin->w_arg_idx >= eap->line2) - curwin->w_arg_idx -= n; - else if (curwin->w_arg_idx > eap->line1) - curwin->w_arg_idx = eap->line1; + (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T)); + ALIST(curwin)->al_ga.ga_len -= (int)n; + if (curwin->w_arg_idx >= eap->line2) { + curwin->w_arg_idx -= (int)n; + } else if (curwin->w_arg_idx > eap->line1) { + curwin->w_arg_idx = (int)eap->line1; + } } } else if (*eap->arg == NUL) EMSG(_(e_argreq)); @@ -1868,7 +1874,7 @@ void ex_argdelete(exarg_T *eap) } /* - * ":argdo", ":windo", ":bufdo", ":tabdo" + * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" */ void ex_listdo(exarg_T *eap) { @@ -1879,7 +1885,6 @@ void ex_listdo(exarg_T *eap) char_u *save_ei = NULL; char_u *p_shm_save; - if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) /* Don't do syntax HL autocommands. Skipping the syntax file is a * great speed improvement. */ @@ -1909,12 +1914,15 @@ void ex_listdo(exarg_T *eap) } break; case CMD_argdo: - i = eap->line1 - 1; + i = (int)eap->line1 - 1; break; default: break; } + buf_T *buf = curbuf; + size_t qf_size = 0; + /* set pcmark now */ if (eap->cmdidx == CMD_bufdo) { /* Advance to the first listed buffer after "eap->line1". */ @@ -1929,6 +1937,23 @@ void ex_listdo(exarg_T *eap) if (buf != NULL) { goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum); } + } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { + qf_size = qf_get_size(eap); + assert(eap->line1 >= 0); + if (qf_size == 0 || (size_t)eap->line1 > qf_size) { + buf = NULL; + } else { + ex_cc(eap); + + buf = curbuf; + i = (int)eap->line1 - 1; + if (eap->addr_count <= 0) { + // Default to all quickfix/location list entries. + assert(qf_size < MAXLNUM); + eap->line2 = (linenr_T)qf_size; + } + } } else { setpcmark(); } @@ -2009,9 +2034,27 @@ void ex_listdo(exarg_T *eap) set_option_value((char_u *)"shm", 0L, p_shm_save, 0); xfree(p_shm_save); - /* If autocommands took us elsewhere, quit here */ - if (curbuf->b_fnum != next_fnum) + // If autocommands took us elsewhere, quit here. + if (curbuf->b_fnum != next_fnum) { break; + } + } + + if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { + assert(i >= 0); + if ((size_t)i >= qf_size || i >= eap->line2) { + break; + } + + size_t qf_idx = qf_get_cur_idx(eap); + + ex_cnext(eap); + + // If jumping to the next quickfix entry fails, quit here. + if (qf_get_cur_idx(eap) == qf_idx) { + break; + } } if (eap->cmdidx == CMD_windo) { @@ -2061,7 +2104,7 @@ alist_add_list ( after = ARGCOUNT; if (after < ARGCOUNT) memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), - (ARGCOUNT - after) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - after) * sizeof(aentry_T)); for (int i = 0; i < count; ++i) { ARGLIST[after + i].ae_fname = files[i]; ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED); @@ -2330,8 +2373,9 @@ static FILE *fopen_noinh_readbin(char *filename) # ifdef HAVE_FD_CLOEXEC { int fdflags = fcntl(fd_tmp, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); + } } # endif @@ -2534,7 +2578,7 @@ do_source ( while (script_items.ga_len < current_SID) { ++script_items.ga_len; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; - SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; + SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false; } si = &SCRIPT_ITEM(current_SID); si->sn_name = fname_exp; @@ -3125,27 +3169,30 @@ static char_u *get_mess_env(void) */ void set_lang_var(void) { - char_u *loc; + const char *loc; # ifdef HAVE_GET_LOCALE_VAL - loc = (char_u *)get_locale_val(LC_CTYPE); + loc = get_locale_val(LC_CTYPE); # else - /* setlocale() not supported: use the default value */ - loc = (char_u *)"C"; + // setlocale() not supported: use the default value + loc = "C"; # endif set_vim_var_string(VV_CTYPE, loc, -1); /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall * back to LC_CTYPE if it's empty. */ # ifdef HAVE_WORKING_LIBINTL - loc = get_mess_env(); + loc = (char *) get_mess_env(); +# elif defined(LC_MESSAGES) + loc = get_locale_val(LC_MESSAGES); # else - loc = (char_u *)get_locale_val(LC_MESSAGES); + // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE + loc = get_locale_val(LC_CTYPE); # endif set_vim_var_string(VV_LANG, loc, -1); # ifdef HAVE_GET_LOCALE_VAL - loc = (char_u *)get_locale_val(LC_TIME); + loc = get_locale_val(LC_TIME); # endif set_vim_var_string(VV_LC_TIME, loc, -1); } @@ -3349,8 +3396,8 @@ static void script_host_execute(char *name, exarg_T *eap) // script list_append_string(args, script ? script : eap->arg, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3366,16 +3413,16 @@ static void script_host_execute_file(char *name, exarg_T *eap) // filename list_append_string(args, buffer, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { list_T *args = list_alloc(); - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); list_append_string(args, eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 10d2eb688e..f46d1e6d47 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -72,6 +72,7 @@ #define ADDR_LOADED_BUFFERS 3 #define ADDR_BUFFERS 4 #define ADDR_TABS 5 +#define ADDR_QUICKFIX 6 typedef struct exarg exarg_T; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 59bda9345e..870284a0f7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -381,15 +381,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, suppress_errthrow = FALSE; } - /* - * If requested, store and reset the global values controlling the - * exception handling (used when debugging). Otherwise clear it to avoid - * a bogus compiler warning when the optimizer uses inline functions... - */ - if (flags & DOCMD_EXCRESET) + // If requested, store and reset the global values controlling the + // exception handling (used when debugging). Otherwise clear it to avoid + // a bogus compiler warning when the optimizer uses inline functions... + if (flags & DOCMD_EXCRESET) { save_dbg_stuff(&debug_saved); - else - memset(&debug_saved, 0, 1); + } else { + memset(&debug_saved, 0, sizeof(debug_saved)); + } initial_trylevel = trylevel; @@ -1531,9 +1530,12 @@ static char_u * do_one_cmd(char_u **cmdlinep, lnum = CURRENT_TAB_NR; ea.line2 = lnum; break; + case ADDR_QUICKFIX: + ea.line2 = qf_get_cur_valid_idx(&ea); + break; } ea.cmd = skipwhite(ea.cmd); - lnum = get_address(&ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0); + lnum = get_address(&ea, &ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0); if (ea.cmd == NULL) /* error detected */ goto doend; if (lnum == MAXLNUM) { @@ -1582,6 +1584,13 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = ARGCOUNT; } break; + case ADDR_QUICKFIX: + ea.line1 = 1; + ea.line2 = qf_get_size(&ea); + if (ea.line2 == 0) { + ea.line2 = 1; + } + break; } ++ea.addr_count; } @@ -1645,16 +1654,15 @@ static char_u * do_one_cmd(char_u **cmdlinep, * If we got a line, but no command, then go to the line. * If we find a '|' or '\n' we set ea.nextcmd. */ - if (*ea.cmd == NUL || *ea.cmd == '"' || - (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) { - /* - * strange vi behaviour: - * ":3" jumps to line 3 - * ":3|..." prints line 3 - * ":|" prints current line - */ - if (ea.skip) /* skip this if inside :if */ + if (*ea.cmd == NUL || *ea.cmd == '"' + || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) { + // strange vi behaviour: + // ":3" jumps to line 3 + // ":3|..." prints line 3 + // ":|" prints current line + if (ea.skip) { // skip this if inside :if goto doend; + } if (*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2)) { ea.cmdidx = CMD_print; ea.argt = RANGE | COUNT | TRLBAR; @@ -1684,7 +1692,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, // autocommands defined, trigger the matching autocommands. if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip && ASCII_ISUPPER(*ea.cmd) - && has_cmdundefined()) { + && has_event(EVENT_CMDUNDEFINED)) { p = ea.cmd; while (ASCII_ISALNUM(*p)) { ++p; @@ -1692,9 +1700,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, p = vim_strnsave(ea.cmd, p - ea.cmd); int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, TRUE, NULL); xfree(p); - if (ret && !aborting()) { - p = find_command(&ea, NULL); - } + // If the autocommands did something and didn't cause an error, try + // finding the command again. + p = (ret && !aborting()) ? find_command(&ea, NULL) : NULL; } if (p == NULL) { @@ -1817,9 +1825,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, correct_range(&ea); - if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy) { - /* Put the first line at the start of a closed fold, put the last line - * at the end of a closed fold. */ + if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy + && ea.addr_type == ADDR_LINES) { + // Put the first line at the start of a closed fold, put the last line + // at the end of a closed fold. (void)hasFolding(ea.line1, &ea.line1, NULL); (void)hasFolding(ea.line2, NULL, &ea.line2); } @@ -1962,6 +1971,12 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = ARGCOUNT; } break; + case ADDR_QUICKFIX: + ea.line2 = qf_get_size(&ea); + if (ea.line2 == 0) { + ea.line2 = 1; + } + break; } } @@ -2332,8 +2347,11 @@ static char_u *find_command(exarg_T *eap, int *full) eap->cmdidx = CMD_k; ++p; } else if (p[0] == 's' - && ((p[1] == 'c' && p[2] != 's' && p[2] != 'r' - && p[3] != 'i' && p[4] != 'p') + && ((p[1] == 'c' + && (p[2] == NUL + || (p[2] != 's' && p[2] != 'r' + && (p[3] == NUL + || (p[3] != 'i' && p[4] != 'p'))))) || p[1] == 'g' || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g') || p[1] == 'I' @@ -2660,16 +2678,25 @@ set_one_cmd_context ( p = cmd + 1; } else { p = cmd; - while (ASCII_ISALPHA(*p) || *p == '*') /* Allow * wild card */ - ++p; - /* check for non-alpha command */ - if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) - ++p; - /* for python 3.x: ":py3*" commands completion */ + while (ASCII_ISALPHA(*p) || *p == '*') { // Allow * wild card + p++; + } + // a user command may contain digits + if (ASCII_ISUPPER(cmd[0])) { + while (ASCII_ISALNUM(*p) || *p == '*') { + p++; + } + } + // for python 3.x: ":py3*" commands completion if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') { - ++p; - while (ASCII_ISALPHA(*p) || *p == '*') - ++p; + p++; + while (ASCII_ISALPHA(*p) || *p == '*') { + p++; + } + } + // check for non-alpha command + if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) { + p++; } len = (int)(p - cmd); @@ -2683,9 +2710,11 @@ set_one_cmd_context ( (size_t)len) == 0) break; - if (cmd[0] >= 'A' && cmd[0] <= 'Z') - while (ASCII_ISALNUM(*p) || *p == '*') /* Allow * wild card */ - ++p; + if (cmd[0] >= 'A' && cmd[0] <= 'Z') { + while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card + p++; + } + } } /* @@ -2794,10 +2823,11 @@ set_one_cmd_context ( } } - /* no arguments allowed */ - if (!(ea.argt & EXTRA) && *arg != NUL && - vim_strchr((char_u *)"|\"", *arg) == NULL) + // no arguments allowed + if (!(ea.argt & EXTRA) && *arg != NUL + && vim_strchr((char_u *)"|\"", *arg) == NULL) { return NULL; + } /* Find start of last argument (argument just before cursor): */ p = buff; @@ -2929,8 +2959,11 @@ set_one_cmd_context ( case CMD_chdir: case CMD_lcd: case CMD_lchdir: - if (xp->xp_context == EXPAND_FILES) + case CMD_tcd: + case CMD_tchdir: + if (xp->xp_context == EXPAND_FILES) { xp->xp_context = EXPAND_DIRECTORIES; + } break; case CMD_help: xp->xp_context = EXPAND_HELP; @@ -2945,6 +2978,8 @@ set_one_cmd_context ( case CMD_botright: case CMD_browse: case CMD_bufdo: + case CMD_cdo: + case CMD_cfdo: case CMD_confirm: case CMD_debug: case CMD_folddoclosed: @@ -2954,7 +2989,9 @@ set_one_cmd_context ( case CMD_keepjumps: case CMD_keepmarks: case CMD_keeppatterns: + case CMD_ldo: case CMD_leftabove: + case CMD_lfdo: case CMD_lockmarks: case CMD_noautocmd: case CMD_noswapfile: @@ -3367,7 +3404,8 @@ skip_range ( * * Return MAXLNUM when no Ex address was found. */ -static linenr_T get_address(char_u **ptr, +static linenr_T get_address(exarg_T *eap, + char_u **ptr, int addr_type, // flag: one of ADDR_LINES, ... int skip, // only skip the address, don't use it int to_other_file // flag: may jump to other file @@ -3405,6 +3443,9 @@ static linenr_T get_address(char_u **ptr, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_QUICKFIX: + lnum = qf_get_cur_valid_idx(eap); + break; } break; @@ -3436,6 +3477,12 @@ static linenr_T get_address(char_u **ptr, case ADDR_TABS: lnum = LAST_TAB_NR; break; + case ADDR_QUICKFIX: + lnum = qf_get_size(eap); + if (lnum == 0) { + lnum = 1; + } + break; } break; @@ -3578,6 +3625,9 @@ static linenr_T get_address(char_u **ptr, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_QUICKFIX: + lnum = qf_get_cur_valid_idx(eap); + break; } } @@ -3702,6 +3752,12 @@ static char_u *invalid_range(exarg_T *eap) return (char_u *)_(e_invrange); } break; + case ADDR_QUICKFIX: + assert(eap->line2 >= 0); + if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) { + return (char_u *)_(e_invrange); + } + break; } } return NULL; @@ -4268,7 +4324,7 @@ static void ex_unmap(exarg_T *eap) */ static void ex_mapclear(exarg_T *eap) { - map_clear(eap->cmd, eap->arg, eap->forceit, FALSE); + map_clear_mode(eap->cmd, eap->arg, eap->forceit, false); } /* @@ -4276,7 +4332,7 @@ static void ex_mapclear(exarg_T *eap) */ static void ex_abclear(exarg_T *eap) { - map_clear(eap->cmd, eap->arg, TRUE, TRUE); + map_clear_mode(eap->cmd, eap->arg, true, true); } static void ex_autocmd(exarg_T *eap) @@ -4501,7 +4557,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, char_u *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); + replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, false, + CPO_TO_CPO_FLAGS); if (rep_buf == NULL) { /* Can't replace termcodes - try using the string as is */ rep_buf = vim_strsave(rep); @@ -4589,6 +4646,7 @@ static struct { {ADDR_TABS, "tabs"}, {ADDR_BUFFERS, "buffers"}, {ADDR_WINDOWS, "windows"}, + {ADDR_QUICKFIX, "quickfix"}, {-1, NULL} }; @@ -4714,14 +4772,15 @@ static void uc_list(char_u *name, size_t name_len) IObuff[len++] = ' '; } while (len < 11); - /* Address Type */ - for (j = 0; addr_type_complete[j].expand != -1; ++j) - if (addr_type_complete[j].expand != ADDR_LINES && - addr_type_complete[j].expand == cmd->uc_addr_type) { + // Address Type + for (j = 0; addr_type_complete[j].expand != -1; j++) { + if (addr_type_complete[j].expand != ADDR_LINES + && addr_type_complete[j].expand == cmd->uc_addr_type) { STRCPY(IObuff + len, addr_type_complete[j].name); len += (int)STRLEN(IObuff + len); break; } + } do { IObuff[len++] = ' '; @@ -5446,7 +5505,8 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, int *addr_type_arg) { int i, a, b; - for (i = 0; addr_type_complete[i].expand != -1; ++i) { + + for (i = 0; addr_type_complete[i].expand != -1; i++) { a = (int)STRLEN(addr_type_complete[i].name) == vallen; b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; if (a && b) { @@ -5457,8 +5517,8 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, if (addr_type_complete[i].expand == -1) { char_u *err = value; - for (i = 0; err[i] == NUL || !ascii_iswhite(err[i]); i++) - ; + + for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {} err[i] = NUL; EMSG2(_("E180: Invalid address type value: %s"), err); return FAIL; @@ -5595,12 +5655,13 @@ static void ex_quit(exarg_T *eap) wp = curwin; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || - (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() + || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) { return; + } /* @@ -5610,14 +5671,19 @@ static void ex_quit(exarg_T *eap) exiting = TRUE; if ((!P_HID(curbuf) && check_changed(curbuf, (p_awa ? CCGD_AW : 0) - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) - || check_more(TRUE, eap->forceit) == FAIL - || (only_one_window() && check_changed_any(eap->forceit))) { + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD)) + || check_more(true, eap->forceit) == FAIL + || (only_one_window() && check_changed_any(eap->forceit, true))) { not_exiting(); } else { - if (only_one_window()) { - // quit last window + // quit last window + // Note: only_one_window() returns true, even so a help window is + // still open. In that case only quit, if no address has been + // specified. Example: + // :h|wincmd w|1q - don't quit + // :h|wincmd w|q - quit + if (only_one_window() && (firstwin == lastwin || eap->addr_count == 0)) { getout(0); } /* close window; may free buffer */ @@ -5657,9 +5723,10 @@ static void ex_quit_all(exarg_T *eap) if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) return; - exiting = TRUE; - if (eap->forceit || !check_changed_any(FALSE)) + exiting = true; + if (eap->forceit || !check_changed_any(false, false)) { getout(0); + } not_exiting(); } @@ -5944,21 +6011,22 @@ static void ex_exit(exarg_T *eap) if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) return; - /* - * if more files or windows we won't exit - */ - if (check_more(FALSE, eap->forceit) == OK && only_one_window()) - exiting = TRUE; - if ( ((eap->cmdidx == CMD_wq - || curbufIsChanged()) - && do_write(eap) == FAIL) - || check_more(TRUE, eap->forceit) == FAIL - || (only_one_window() && check_changed_any(eap->forceit))) { + // if more files or windows we won't exit + if (check_more(false, eap->forceit) == OK && only_one_window()) { + exiting = true; + } + if (((eap->cmdidx == CMD_wq + || curbufIsChanged()) + && do_write(eap) == FAIL) + || check_more(true, eap->forceit) == FAIL + || (only_one_window() && check_changed_any(eap->forceit, false))) { not_exiting(); } else { - if (only_one_window()) /* quit last window, exit Vim */ + if (only_one_window()) { + // quit last window, exit Vim getout(0); - /* Quit current window, may free the buffer. */ + } + // Quit current window, may free the buffer. win_close(curwin, !P_HID(curwin->w_buffer)); } } @@ -6232,10 +6300,8 @@ void ex_splitview(exarg_T *eap) if (eap->cmdidx == CMD_tabedit || eap->cmdidx == CMD_tabfind || eap->cmdidx == CMD_tabnew) { - if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab - : eap->addr_count == 0 ? 0 - : (int)eap->line2 + 1) != FAIL) { - apply_autocmds(EVENT_TABNEW, eap->arg, eap->arg, FALSE, curbuf); + if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0 + ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) { do_exedit(eap, old_curwin); apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf); @@ -6305,7 +6371,7 @@ static void ex_tabnext(exarg_T *eap) */ static void ex_tabmove(exarg_T *eap) { - int tab_number = 9999; + int tab_number; if (eap->arg && *eap->arg != NUL) { char_u *p = eap->arg; @@ -6321,17 +6387,35 @@ static void ex_tabmove(exarg_T *eap) } else p = eap->arg; - if (p == skipdigits(p)) { - /* No numbers as argument. */ - eap->errmsg = e_invarg; - return; + if (relative == 0) { + if (STRCMP(p, "$") == 0) { + tab_number = LAST_TAB_NR; + } else if (p == skipdigits(p)) { + // No numbers as argument. + eap->errmsg = e_invarg; + return; + } else { + tab_number = getdigits(&p); + } + } else { + if (*p != NUL) { + tab_number = getdigits(&p); + } else { + tab_number = 1; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (relative == -1) { + --tab_number; + } } - - tab_number = getdigits_int(&p); - if (relative != 0) - tab_number = tab_number * relative + tabpage_index(curtab) - 1; ; - } else if (eap->addr_count != 0) + } else if (eap->addr_count != 0) { tab_number = eap->line2; + if (**eap->cmdlinep == '-') { + --tab_number; + } + } else { + tab_number = LAST_TAB_NR; + } tabpage_move(tab_number); } @@ -6740,36 +6824,55 @@ void free_cd_dir(void) #endif -/* - * Deal with the side effects of changing the current directory. - * When "local" is TRUE then this was after an ":lcd" command. - */ -void post_chdir(int local) +/// Deal with the side effects of changing the current directory. +/// +/// @param scope Scope of the function call (global, tab or window). +void post_chdir(CdScope scope) { + // The local directory of the current window is always overwritten. xfree(curwin->w_localdir); curwin->w_localdir = NULL; - if (local) { - /* If still in global directory, need to remember current - * directory as global directory. */ - if (globaldir == NULL && prev_dir != NULL) + + // Overwrite the local directory of the current tab page for `cd` and `tcd` + if (scope >= kCdScopeTab) { + xfree(curtab->localdir); + curtab->localdir = NULL; + } + + if (scope < kCdScopeGlobal) { + // If still in global directory, need to remember current directory as + // global directory. + if (globaldir == NULL && prev_dir != NULL) { globaldir = vim_strsave(prev_dir); - /* Remember this local directory for the window. */ - if (os_dirname(NameBuff, MAXPATHL) == OK) - curwin->w_localdir = vim_strsave(NameBuff); - } else { - /* We are now in the global directory, no need to remember its - * name. */ + } + } + + switch (scope) { + case kCdScopeGlobal: + // We are now in the global directory, no need to remember its name. xfree(globaldir); globaldir = NULL; + break; + case kCdScopeTab: + // Remember this local directory for the tab page. + if (os_dirname(NameBuff, MAXPATHL) == OK) { + curtab->localdir = vim_strsave(NameBuff); + } + break; + case kCdScopeWindow: + // Remember this local directory for the window. + if (os_dirname(NameBuff, MAXPATHL) == OK) { + curwin->w_localdir = vim_strsave(NameBuff); + } + break; } shorten_fnames(TRUE); } -/* - * ":cd", ":lcd", ":chdir" and ":lchdir". - */ + +/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`. void ex_cd(exarg_T *eap) { char_u *new_dir; @@ -6803,17 +6906,32 @@ void ex_cd(exarg_T *eap) prev_dir = NULL; #if defined(UNIX) - /* for UNIX ":cd" means: go to home directory */ + // On Unix ":cd" means: go to home directory. if (*new_dir == NUL) { - /* use NameBuff for home directory name */ + // Use NameBuff for home directory name. expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); new_dir = NameBuff; } #endif - if (new_dir == NULL || vim_chdir(new_dir)) + if (vim_chdir(new_dir)) { EMSG(_(e_failed)); - else { - post_chdir(eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir); + } else { + CdScope scope = kCdScopeGlobal; // Depends on command invoked + + switch (eap->cmdidx) { + case CMD_tcd: + case CMD_tchdir: + scope = kCdScopeTab; + break; + case CMD_lcd: + case CMD_lchdir: + scope = kCdScopeWindow; + break; + default: + break; + } + + post_chdir(scope); /* Echo the new current directory if the command was typed. */ if (KeyTyped || p_verbose >= 5) @@ -6957,9 +7075,9 @@ static void ex_operators(exarg_T *eap) oa.start.lnum = eap->line1; oa.end.lnum = eap->line2; oa.line_count = eap->line2 - eap->line1 + 1; - oa.motion_type = MLINE; - virtual_op = FALSE; - if (eap->cmdidx != CMD_yank) { /* position cursor for undo */ + oa.motion_type = kMTLineWise; + virtual_op = false; + if (eap->cmdidx != CMD_yank) { // position cursor for undo setpcmark(); curwin->w_cursor.lnum = eap->line1; beginline(BL_SOL | BL_FIX); @@ -7013,9 +7131,7 @@ static void ex_put(exarg_T *eap) */ static void ex_copymove(exarg_T *eap) { - long n; - - n = get_address(&eap->arg, eap->addr_type, FALSE, FALSE); + long n = get_address(eap, &eap->arg, eap->addr_type, false, false); if (eap->arg == NULL) { /* error detected */ eap->nextcmd = NULL; return; @@ -7349,10 +7465,10 @@ static int mksession_nl = FALSE; /* use NL only in put_eol() */ static void ex_mkrc(exarg_T *eap) { FILE *fd; - int failed = FALSE; - int view_session = FALSE; - int using_vdir = FALSE; /* using 'viewdir'? */ - char_u *viewFile = NULL; + int failed = false; + int view_session = false; + int using_vdir = false; // using 'viewdir'? + char *viewFile = NULL; unsigned *flagp; if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) { @@ -7363,32 +7479,34 @@ static void ex_mkrc(exarg_T *eap) * short file name when 'acd' is set, that is checked later. */ did_lcd = FALSE; - char_u *fname; - /* ":mkview" or ":mkview 9": generate file name with 'viewdir' */ + char *fname; + // ":mkview" or ":mkview 9": generate file name with 'viewdir' if (eap->cmdidx == CMD_mkview && (*eap->arg == NUL || (ascii_isdigit(*eap->arg) && eap->arg[1] == NUL))) { - eap->forceit = TRUE; - fname = (char_u *)get_view_file(*eap->arg); - if (fname == NULL) + eap->forceit = true; + fname = get_view_file(*eap->arg); + if (fname == NULL) { return; + } viewFile = fname; - using_vdir = TRUE; - } else if (*eap->arg != NUL) - fname = eap->arg; - else if (eap->cmdidx == CMD_mkvimrc) - fname = (char_u *)VIMRC_FILE; - else if (eap->cmdidx == CMD_mksession) - fname = (char_u *)SESSION_FILE; - else - fname = (char_u *)EXRC_FILE; + using_vdir = true; + } else if (*eap->arg != NUL) { + fname = (char *) eap->arg; + } else if (eap->cmdidx == CMD_mkvimrc) { + fname = VIMRC_FILE; + } else if (eap->cmdidx == CMD_mksession) { + fname = SESSION_FILE; + } else { + fname = EXRC_FILE; + } /* When using 'viewdir' may have to create the directory. */ if (using_vdir && !os_isdir(p_vdir)) { vim_mkdir_emsg(p_vdir, 0755); } - fd = open_exfile(fname, eap->forceit, WRITEBIN); + fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN); if (fd != NULL) { if (eap->cmdidx == CMD_mkview) flagp = &vop_flags; @@ -7432,8 +7550,9 @@ static void ex_mkrc(exarg_T *eap) || os_chdir((char *)dirnow) != 0) *dirnow = NUL; if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) { - if (vim_chdirfile(fname) == OK) - shorten_fnames(TRUE); + if (vim_chdirfile((char_u *) fname) == OK) { + shorten_fnames(true); + } } else if (*dirnow != NUL && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) { if (os_chdir((char *)globaldir) == 0) @@ -7478,15 +7597,14 @@ static void ex_mkrc(exarg_T *eap) failed |= fclose(fd); - if (failed) + if (failed) { EMSG(_(e_write)); - else if (eap->cmdidx == CMD_mksession) { - /* successful session write - set this_session var */ - char_u *tbuf; - - tbuf = xmalloc(MAXPATHL); - if (vim_FullName((char *)fname, (char *)tbuf, MAXPATHL, FALSE) == OK) + } else if (eap->cmdidx == CMD_mksession) { + // successful session write - set this_session var + char *const tbuf = xmalloc(MAXPATHL); + if (vim_FullName(fname, tbuf, MAXPATHL, false) == OK) { set_vim_var_string(VV_THIS_SESSION, tbuf, -1); + } xfree(tbuf); } #ifdef MKSESSION_NL @@ -8219,16 +8337,22 @@ static char_u *arg_all(void) retval[len] = ' '; ++len; } - for (; *p != NUL; ++p) { - if (*p == ' ' || *p == '\\') { - /* insert a backslash */ - if (retval != NULL) + for (; *p != NUL; p++) { + if (*p == ' ' +#ifndef BACKSLASH_IN_FILENAME + || *p == '\\' +#endif + ) { + // insert a backslash + if (retval != NULL) { retval[len] = '\\'; - ++len; + } + len++; } - if (retval != NULL) + if (retval != NULL) { retval[len] = *p; - ++len; + } + len++; } } @@ -8307,8 +8431,7 @@ makeopens ( { int only_save_windows = TRUE; int nr; - int cnr = 1; - int restore_size = TRUE; + int restore_size = true; win_T *wp; char_u *sname; win_T *edited_win = NULL; @@ -8425,7 +8548,8 @@ makeopens ( tab_firstwin = firstwin; /* first window in tab page "tabnr" */ tab_topframe = topframe; for (tabnr = 1;; ++tabnr) { - int need_tabnew = FALSE; + int need_tabnew = false; + int cnr = 1; if ((ssop_flags & SSOP_TABPAGES)) { tabpage_T *tp = find_tabpage(tabnr); @@ -8704,19 +8828,18 @@ static int ses_do_frame(frame_T *fr) return FALSE; } -/* - * Return non-zero if window "wp" is to be stored in the Session. - */ +/// Return non-zero if window "wp" is to be stored in the Session. static int ses_do_win(win_T *wp) { if (wp->w_buffer->b_fname == NULL - /* When 'buftype' is "nofile" can't restore the window contents. */ - || bt_nofile(wp->w_buffer) - ) + // When 'buftype' is "nofile" can't restore the window contents. + || (!wp->w_buffer->terminal && bt_nofile(wp->w_buffer))) { return ssop_flags & SSOP_BLANK; - if (wp->w_buffer->b_help) + } + if (wp->w_buffer->b_help) { return ssop_flags & SSOP_HELP; - return TRUE; + } + return true; } /* @@ -9066,16 +9189,15 @@ static char *get_view_file(int c) */ int put_eol(FILE *fd) { - if ( -#ifdef USE_CRNL - ( -# ifdef MKSESSION_NL - !mksession_nl && -# endif - (putc('\r', fd) < 0)) || +#if defined(USE_CRNL) && defined(MKSESSION_NL) + if ((!mksession_nl && putc('\r', fd) < 0) || putc('\n', fd) < 0) { +#elif defined(USE_CRNL) + if (putc('\r', fd) < 0 || putc('\n', fd) < 0) { +#else + if (putc('\n', fd) < 0) { #endif - (putc('\n', fd) < 0)) return FAIL; + } return OK; } @@ -9152,9 +9274,9 @@ char_u *get_behave_arg(expand_T *xp, int idx) return NULL; } -static int filetype_detect = FALSE; -static int filetype_plugin = FALSE; -static int filetype_indent = FALSE; +static TriState filetype_detect = kNone; +static TriState filetype_plugin = kNone; +static TriState filetype_indent = kNone; /* * ":filetype [plugin] [indent] {on,off,detect}" @@ -9168,27 +9290,27 @@ static int filetype_indent = FALSE; static void ex_filetype(exarg_T *eap) { char_u *arg = eap->arg; - int plugin = FALSE; - int indent = FALSE; + bool plugin = false; + bool indent = false; if (*eap->arg == NUL) { /* Print current status. */ smsg("filetype detection:%s plugin:%s indent:%s", - filetype_detect ? "ON" : "OFF", - filetype_plugin ? (filetype_detect ? "ON" : "(on)") : "OFF", - filetype_indent ? (filetype_detect ? "ON" : "(on)") : "OFF"); + filetype_detect == kTrue ? "ON" : "OFF", + filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF", // NOLINT(whitespace/line_length) + filetype_indent == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF"); // NOLINT(whitespace/line_length) return; } /* Accept "plugin" and "indent" in any order. */ for (;; ) { if (STRNCMP(arg, "plugin", 6) == 0) { - plugin = TRUE; + plugin = true; arg = skipwhite(arg + 6); continue; } if (STRNCMP(arg, "indent", 6) == 0) { - indent = TRUE; + indent = true; arg = skipwhite(arg + 6); continue; } @@ -9196,15 +9318,15 @@ static void ex_filetype(exarg_T *eap) } if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0) { if (*arg == 'o' || !filetype_detect) { - source_runtime((char_u *)FILETYPE_FILE, TRUE); - filetype_detect = TRUE; + source_runtime((char_u *)FILETYPE_FILE, true); + filetype_detect = kTrue; if (plugin) { - source_runtime((char_u *)FTPLUGIN_FILE, TRUE); - filetype_plugin = TRUE; + source_runtime((char_u *)FTPLUGIN_FILE, true); + filetype_plugin = kTrue; } if (indent) { - source_runtime((char_u *)INDENT_FILE, TRUE); - filetype_indent = TRUE; + source_runtime((char_u *)INDENT_FILE, true); + filetype_indent = kTrue; } } if (*arg == 'd') { @@ -9214,21 +9336,37 @@ static void ex_filetype(exarg_T *eap) } else if (STRCMP(arg, "off") == 0) { if (plugin || indent) { if (plugin) { - source_runtime((char_u *)FTPLUGOF_FILE, TRUE); - filetype_plugin = FALSE; + source_runtime((char_u *)FTPLUGOF_FILE, true); + filetype_plugin = kFalse; } if (indent) { - source_runtime((char_u *)INDOFF_FILE, TRUE); - filetype_indent = FALSE; + source_runtime((char_u *)INDOFF_FILE, true); + filetype_indent = kFalse; } } else { - source_runtime((char_u *)FTOFF_FILE, TRUE); - filetype_detect = FALSE; + source_runtime((char_u *)FTOFF_FILE, true); + filetype_detect = kFalse; } } else EMSG2(_(e_invarg2), arg); } +/// Do ":filetype plugin indent on" if user did not already do some +/// permutation thereof. +void filetype_maybe_enable(void) +{ + if (filetype_detect == kNone + && filetype_plugin == kNone + && filetype_indent == kNone) { + source_runtime((char_u *)FILETYPE_FILE, true); + filetype_detect = kTrue; + source_runtime((char_u *)FTPLUGIN_FILE, true); + filetype_plugin = kTrue; + source_runtime((char_u *)INDENT_FILE, true); + filetype_indent = kTrue; + } +} + /* * ":setfiletype {name}" */ @@ -9266,59 +9404,62 @@ static void ex_nohlsearch(exarg_T *eap) redraw_all_later(SOME_VALID); } -/* - * ":[N]match {group} {pattern}" - * Sets nextcmd to the start of the next command, if any. Also called when - * skipping commands to find the next command. - */ +// ":[N]match {group} {pattern}" +// Sets nextcmd to the start of the next command, if any. Also called when +// skipping commands to find the next command. static void ex_match(exarg_T *eap) { - char_u *p; - char_u *g = NULL; - char_u *end; + char_u *p; + char_u *g = NULL; + char_u *end; int c; int id; - if (eap->line2 <= 3) + if (eap->line2 <= 3) { id = eap->line2; - else { + } else { EMSG(e_invcmd); return; } - /* First clear any old pattern. */ - if (!eap->skip) - match_delete(curwin, id, FALSE); + // First clear any old pattern. + if (!eap->skip) { + match_delete(curwin, id, false); + } - if (ends_excmd(*eap->arg)) + if (ends_excmd(*eap->arg)) { end = eap->arg; - else if ((STRNICMP(eap->arg, "none", 4) == 0 - && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) + } else if ((STRNICMP(eap->arg, "none", 4) == 0 + && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) { end = eap->arg + 4; - else { + } else { p = skiptowhite(eap->arg); - if (!eap->skip) + if (!eap->skip) { g = vim_strnsave(eap->arg, (int)(p - eap->arg)); + } p = skipwhite(p); if (*p == NUL) { - /* There must be two arguments. */ + // There must be two arguments. + xfree(g); EMSG2(_(e_invarg2), eap->arg); return; } - end = skip_regexp(p + 1, *p, TRUE, NULL); + end = skip_regexp(p + 1, *p, true, NULL); if (!eap->skip) { if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { + xfree(g); eap->errmsg = e_trailing; return; } if (*end != *p) { + xfree(g); EMSG2(_(e_invarg2), p); return; } c = *end; *end = NUL; - match_add(curwin, g, p + 1, 10, id, NULL); + match_add(curwin, g, p + 1, 10, id, NULL, NULL); xfree(g); *end = c; } @@ -9358,12 +9499,14 @@ static void ex_folddo(exarg_T *eap) static void ex_terminal(exarg_T *eap) { - // We will call termopen() with ['shell'] if not given a {cmd}. - char *name = (char *)p_sh; + char *name = (char *)p_sh; // Default to 'shell' if {cmd} is not given. + bool mustfree = false; char *lquote = "['"; char *rquote = "']"; + if (*eap->arg != NUL) { name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\"); + mustfree = true; lquote = rquote = "\""; } @@ -9373,7 +9516,7 @@ static void ex_terminal(exarg_T *eap) eap->forceit==TRUE ? "!" : "", lquote, name, rquote); do_cmdline_cmd(ex_cmd); - if (name != (char *)p_sh) { + if (mustfree) { xfree(name); } } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index a5a4edbbbf..dbfc64e2f1 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -19,6 +19,20 @@ #define EXMODE_NORMAL 1 #define EXMODE_VIM 2 +/// The scope of a working-directory command like `:cd`. +/// +/// Scopes are enumerated from lowest to highest. When adding a scope make sure +/// to update all functions using scopes as well, such as the implementation of +/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes +/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead. +typedef enum { + kCdScopeWindow, ///< Affects one window. + kCdScopeTab, ///< Affects one tab page. + kCdScopeGlobal, ///< Affects the entire instance of Neovim. +} CdScope; +#define MIN_CD_SCOPE kCdScopeWindow +#define MAX_CD_SCOPE kCdScopeGlobal + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_docmd.h.generated.h" #endif diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index bf67047ae8..82d4c2b2d5 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -378,7 +378,7 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should char_u *p, *val; if (type == ET_ERROR) { - *should_free = FALSE; + *should_free = true; mesg = ((struct msglist *)value)->throw_msg; if (cmdname != NULL && *cmdname != NUL) { size_t cmdlen = STRLEN(cmdname); @@ -403,14 +403,15 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should && (p[3] == ':' || (ascii_isdigit(p[3]) && p[4] == ':')))))) { - if (*p == NUL || p == mesg) - STRCAT(val, mesg); /* 'E123' missing or at beginning */ - else { - /* '"filename" E123: message text' */ - if (mesg[0] != '"' || p-2 < &mesg[1] || - p[-2] != '"' || p[-1] != ' ') - /* "E123:" is part of the file name. */ + if (*p == NUL || p == mesg) { + STRCAT(val, mesg); // 'E123' missing or at beginning + } else { + // '"filename" E123: message text' + if (mesg[0] != '"' || p-2 < &mesg[1] + || p[-2] != '"' || p[-1] != ' ') { + // "E123:" is part of the file name. continue; + } STRCAT(val, p); p[-2] = NUL; @@ -569,17 +570,19 @@ static void catch_exception(except_T *excp) { excp->caught = caught_stack; caught_stack = excp; - set_vim_var_string(VV_EXCEPTION, excp->value, -1); + set_vim_var_string(VV_EXCEPTION, (char *) excp->value, -1); if (*excp->throw_name != NUL) { - if (excp->throw_lnum != 0) + if (excp->throw_lnum != 0) { vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %" PRId64), - excp->throw_name, (int64_t)excp->throw_lnum); - else + excp->throw_name, (int64_t)excp->throw_lnum); + } else { vim_snprintf((char *)IObuff, IOSIZE, "%s", excp->throw_name); - set_vim_var_string(VV_THROWPOINT, IObuff, -1); - } else - /* throw_name not set on an exception from a command that was typed. */ + } + set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1); + } else { + // throw_name not set on an exception from a command that was typed. set_vim_var_string(VV_THROWPOINT, NULL, -1); + } if (p_verbose >= 13 || debug_break_level > 0) { int save_msg_silent = msg_silent; @@ -614,20 +617,22 @@ static void finish_exception(except_T *excp) EMSG(_(e_internal)); caught_stack = caught_stack->caught; if (caught_stack != NULL) { - set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1); + set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1); if (*caught_stack->throw_name != NUL) { - if (caught_stack->throw_lnum != 0) + if (caught_stack->throw_lnum != 0) { vim_snprintf((char *)IObuff, IOSIZE, - _("%s, line %" PRId64), caught_stack->throw_name, - (int64_t)caught_stack->throw_lnum); - else + _("%s, line %" PRId64), caught_stack->throw_name, + (int64_t)caught_stack->throw_lnum); + } else { vim_snprintf((char *)IObuff, IOSIZE, "%s", - caught_stack->throw_name); - set_vim_var_string(VV_THROWPOINT, IObuff, -1); - } else - /* throw_name not set on an exception from a command that was - * typed. */ + caught_stack->throw_name); + } + set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1); + } else { + // throw_name not set on an exception from a command that was + // typed. set_vim_var_string(VV_THROWPOINT, NULL, -1); + } } else { set_vim_var_string(VV_EXCEPTION, NULL, -1); set_vim_var_string(VV_THROWPOINT, NULL, -1); @@ -1370,19 +1375,24 @@ void ex_catch(exarg_T *eap) } save_cpo = p_cpo; p_cpo = (char_u *)""; + // Disable error messages, it will make current exception + // invalid + emsg_off++; regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - regmatch.rm_ic = FALSE; - if (end != NULL) + emsg_off--; + regmatch.rm_ic = false; + if (end != NULL) { *end = save_char; + } p_cpo = save_cpo; - if (regmatch.regprog == NULL) + if (regmatch.regprog == NULL) { EMSG2(_(e_invarg2), pat); - else { - /* - * Save the value of got_int and reset it. We don't want - * a previous interruption cancel matching, only hitting - * CTRL-C while matching should abort it. - */ + } else { + // + // Save the value of got_int and reset it. We don't want + // a previous interruption cancel matching, only hitting + // CTRL-C while matching should abort it. + // prev_got_int = got_int; got_int = FALSE; caught = vim_regexec_nl(®match, current_exception->value, @@ -1556,22 +1566,21 @@ void ex_endtry(exarg_T *eap) void *rettv = NULL; struct condstack *cstack = eap->cstack; - if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) + if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = (char_u *)N_("E602: :endtry without :try"); - else { - /* - * Don't do something after an error, interrupt or throw in the try - * block, catch clause, or finally clause preceding this ":endtry" or - * when an error or interrupt occurred after a ":continue", ":break", - * ":return", or ":finish" in a try block or catch clause preceding this - * ":endtry" or when the try block never got active (because of an - * inactive surrounding conditional or after an error or interrupt or - * throw) or when there is a surrounding conditional and it has been - * made inactive by a ":continue", ":break", ":return", or ":finish" in - * the finally clause. The latter case need not be tested since then - * anything pending has already been discarded. */ - skip = did_emsg || got_int || did_throw || - !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + } else { + // Don't do something after an error, interrupt or throw in the try + // block, catch clause, or finally clause preceding this ":endtry" or + // when an error or interrupt occurred after a ":continue", ":break", + // ":return", or ":finish" in a try block or catch clause preceding this + // ":endtry" or when the try block never got active (because of an + // inactive surrounding conditional or after an error or interrupt or + // throw) or when there is a surrounding conditional and it has been + // made inactive by a ":continue", ":break", ":return", or ":finish" in + // the finally clause. The latter case need not be tested since then + // anything pending has already been discarded. + skip = (did_emsg || got_int || did_throw + || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6d81f3680a..a4e5a4dcd7 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -289,7 +289,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (ccline.cmdbuff != NULL) { // Put line in history buffer (":" and "=" only when it was typed). - if (ccline.cmdlen && s->firstc != NUL + if (s->histype != HIST_INVALID + && ccline.cmdlen + && s->firstc != NUL && (s->some_key_typed || s->histype == HIST_SEARCH)) { add_to_history(s->histype, ccline.cmdbuff, true, s->histype == HIST_SEARCH ? s->firstc : NUL); @@ -596,7 +598,7 @@ static int command_line_execute(VimState *state, int key) } if (s->j > 0) { - // TODO(tarruda): this is only for DOS/UNIX systems - need to put in + // TODO(tarruda): this is only for DOS/Unix systems - need to put in // machine-specific stuff here and in upseg init cmdline_del(s->j); put_on_cmdline(upseg + 1, 3, false); @@ -622,8 +624,8 @@ static int command_line_execute(VimState *state, int key) // CTRL-\ e doesn't work when obtaining an expression, unless it // is in a mapping. if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e' - || (ccline.cmdfirstc == '=' && - KeyTyped))) { + || (ccline.cmdfirstc == '=' + && KeyTyped))) { vungetc(s->c); s->c = Ctrl_BSL; } else if (s->c == 'e') { @@ -1130,7 +1132,7 @@ static int command_line_handle_key(CommandLineState *s) if (!mouse_has(MOUSE_COMMAND)) { return command_line_not_changed(s); // Ignore mouse } - cmdline_paste(0, true, true); + cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true); redrawcmd(); return command_line_changed(s); @@ -1268,7 +1270,7 @@ static int command_line_handle_key(CommandLineState *s) case K_KPAGEUP: case K_PAGEDOWN: case K_KPAGEDOWN: - if (hislen == 0 || s->firstc == NUL) { + if (s->histype == HIST_INVALID || hislen == 0 || s->firstc == NUL) { // no history return command_line_not_changed(s); } @@ -2424,20 +2426,17 @@ void restore_cmdline_alloc(char_u *p) xfree(p); } -/* - * paste a yank register into the command line. - * used by CTRL-R command in command-line mode - * insert_reg() can't be used here, because special characters from the - * register contents will be interpreted as commands. - * - * return FAIL for failure, OK otherwise - */ -static int -cmdline_paste ( - int regname, - int literally, /* Insert text literally instead of "as typed" */ - int remcr /* remove trailing CR */ -) +/// Paste a yank register into the command line. +/// Used by CTRL-R command in command-line mode. +/// insert_reg() can't be used here, because special characters from the +/// register contents will be interpreted as commands. +/// +/// @param regname Register name. +/// @param literally Insert text literally instead of "as typed". +/// @param remcr When true, remove trailing CR. +/// +/// @returns FAIL for failure, OK otherwise +static bool cmdline_paste(int regname, bool literally, bool remcr) { long i; char_u *arg; @@ -2957,20 +2956,37 @@ ExpandOne ( } } - /* Find longest common part */ + // Find longest common part if (mode == WILD_LONGEST && xp->xp_numfiles > 0) { size_t len; - for (len = 0; xp->xp_files[0][len]; ++len) { - for (i = 0; i < xp->xp_numfiles; ++i) { + size_t mb_len = 1; + int c0; + int ci; + + for (len = 0; xp->xp_files[0][len]; len += mb_len) { + if (has_mbyte) { + mb_len = (* mb_ptr2len)(&xp->xp_files[0][len]); + c0 = (* mb_ptr2char)(&xp->xp_files[0][len]); + } else { + c0 = xp->xp_files[0][len]; + } + for (i = 1; i < xp->xp_numfiles; ++i) { + if (has_mbyte) { + ci =(* mb_ptr2char)(&xp->xp_files[i][len]); + } else { + ci = xp->xp_files[i][len]; + } + if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES || xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_BUFFERS)) { - if (TOLOWER_LOC(xp->xp_files[i][len]) != - TOLOWER_LOC(xp->xp_files[0][len])) + if (vim_tolower(c0) != vim_tolower(ci)) { break; - } else if (xp->xp_files[i][len] != xp->xp_files[0][len]) + } + } else if (c0 != ci) { break; + } } if (i < xp->xp_numfiles) { if (!(options & WILD_NO_BEEP)) { @@ -2979,8 +2995,9 @@ ExpandOne ( break; } } + ss = (char_u *)xstrndup((char *)xp->xp_files[0], len); - findex = -1; /* next p_wc gets first one */ + findex = -1; // next p_wc gets first one } // Concatenate all matching names @@ -3701,6 +3718,9 @@ ExpandFromContext ( flags |= EW_KEEPALL; if (options & WILD_SILENT) flags |= EW_SILENT; + if (options & WILD_ALLLINKS) { + flags |= EW_ALLLINKS; + } if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES @@ -3963,6 +3983,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, char_u *s, *e; int flags = flagsarg; int ret; + bool did_curdir = false; /* for ":set path=" and ":set tags=" halve backslashes for escaped * space */ @@ -3971,7 +3992,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, if (pat[i] == '\\' && pat[i + 1] == ' ') STRMOVE(pat + i, pat + i + 1); - flags |= EW_FILE | EW_EXEC; + flags |= EW_FILE | EW_EXEC | EW_SHELLCMD; bool mustfree = false; // Track memory allocation for *path. /* For an absolute name we don't use $PATH. */ @@ -3991,12 +4012,24 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, /* * Go over all directories in $PATH. Expand matches in that directory and - * collect them in "ga". + * collect them in "ga". When "." is not in $PATH also expaned for the + * current directory, to find "subdir/cmd". */ ga_init(&ga, (int)sizeof(char *), 10); - for (s = path; *s != NUL; s = e) { - if (*s == ' ') - ++s; /* Skip space used for absolute path name. */ + for (s = path; ; s = e) { + if (*s == NUL) { + if (did_curdir) { + break; + } + // Find directories in the current directory, path is empty. + did_curdir = true; + } else if (*s == '.') { + did_curdir = true; + } + + if (*s == ' ') { + s++; // Skip space used for absolute path name. + } e = vim_strchr(s, ':'); if (e == NULL) @@ -4254,20 +4287,33 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) * Command line history stuff * *********************************/ -/* - * Translate a history character to the associated type number. - */ -static int hist_char2type(int c) +/// Translate a history character to the associated type number +static HistoryType hist_char2type(const int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { - if (c == ':') - return HIST_CMD; - if (c == '=') - return HIST_EXPR; - if (c == '@') - return HIST_INPUT; - if (c == '>') - return HIST_DEBUG; - return HIST_SEARCH; /* must be '?' or '/' */ + switch (c) { + case ':': { + return HIST_CMD; + } + case '=': { + return HIST_EXPR; + } + case '@': { + return HIST_INPUT; + } + case '>': { + return HIST_DEBUG; + } + case '/': + case '?': { + return HIST_SEARCH; + } + default: { + return HIST_INVALID; + } + } + // Silence -Wreturn-type + return 0; } /* @@ -4436,28 +4482,38 @@ in_history ( return false; } -/* - * Convert history name (from table above) to its HIST_ equivalent. - * When "name" is empty, return "cmd" history. - * Returns -1 for unknown history name. - */ -int get_histtype(char_u *name) +/// Convert history name to its HIST_ equivalent +/// +/// Names are taken from the table above. When `name` is empty returns currently +/// active history or HIST_DEFAULT, depending on `return_default` argument. +/// +/// @param[in] name Converted name. +/// @param[in] len Name length. +/// @param[in] return_default Determines whether HIST_DEFAULT should be +/// returned or value based on `ccline.cmdfirstc`. +/// +/// @return Any value from HistoryType enum, including HIST_INVALID. May not +/// return HIST_DEFAULT unless return_default is true. +HistoryType get_histtype(const char_u *const name, const size_t len, + const bool return_default) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int i; - int len = (int)STRLEN(name); - - /* No argument: use current history. */ - if (len == 0) - return hist_char2type(ccline.cmdfirstc); + // No argument: use current history. + if (len == 0) { + return return_default ? HIST_DEFAULT : hist_char2type(ccline.cmdfirstc); + } - for (i = 0; history_names[i] != NULL; ++i) - if (STRNICMP(name, history_names[i], len) == 0) + for (HistoryType i = 0; history_names[i] != NULL; i++) { + if (STRNICMP(name, history_names[i], len) == 0) { return i; + } + } - if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && name[1] == NUL) + if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && len == 1) { return hist_char2type(name[0]); + } - return -1; + return HIST_INVALID; } static int last_maptick = -1; /* last seen maptick */ @@ -4478,8 +4534,10 @@ add_to_history ( histentry_T *hisptr; int len; - if (hislen == 0) /* no history */ + if (hislen == 0 || histype == HIST_INVALID) { // no history return; + } + assert(histype != HIST_DEFAULT); if (cmdmod.keeppatterns && histype == HIST_SEARCH) return; @@ -4767,35 +4825,40 @@ int del_history_idx(int histype, int idx) return TRUE; } -/* - * Get indices "num1,num2" that specify a range within a list (not a range of - * text lines in a buffer!) from a string. Used for ":history" and ":clist". - * Returns OK if parsed successfully, otherwise FAIL. - */ +/// Get indices that specify a range within a list (not a range of text lines +/// in a buffer!) from a string. Used for ":history" and ":clist". +/// +/// @param str string to parse range from +/// @param num1 from +/// @param num2 to +/// +/// @return OK if parsed successfully, otherwise FAIL. int get_list_range(char_u **str, int *num1, int *num2) { int len; - int first = FALSE; + int first = false; long num; *str = skipwhite(*str); - if (**str == '-' || ascii_isdigit(**str)) { /* parse "from" part of range */ - vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); + if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); *str += len; *num1 = (int)num; - first = TRUE; + first = true; } *str = skipwhite(*str); - if (**str == ',') { /* parse "to" part of range */ + if (**str == ',') { // parse "to" part of range *str = skipwhite(*str + 1); - vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); if (len > 0) { *num2 = (int)num; *str = skipwhite(*str + len); - } else if (!first) /* no number given at all */ + } else if (!first) { // no number given at all return FAIL; - } else if (first) /* only one number given */ + } + } else if (first) { // only one number given *num2 = *num1; + } return OK; } @@ -4824,23 +4887,20 @@ void ex_history(exarg_T *eap) while (ASCII_ISALPHA(*end) || vim_strchr((char_u *)":=@>/?", *end) != NULL) end++; - i = *end; - *end = NUL; - histype1 = get_histtype(arg); - if (histype1 == -1) { - if (STRNICMP(arg, "all", STRLEN(arg)) == 0) { + histype1 = get_histtype(arg, end - arg, false); + if (histype1 == HIST_INVALID) { + if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; histype2 = HIST_COUNT-1; } else { - *end = i; EMSG(_(e_trailing)); return; } } else histype2 = histype1; - *end = i; - } else + } else { end = arg; + } if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) { EMSG(_(e_trailing)); return; @@ -4948,7 +5008,6 @@ static int ex_window(void) win_T *wp; int i; linenr_T lnum; - int histtype; garray_T winsizes; char_u typestr[2]; int save_restart_edit = restart_edit; @@ -4997,7 +5056,7 @@ static int ex_window(void) /* Showing the prompt may have set need_wait_return, reset it. */ need_wait_return = FALSE; - histtype = hist_char2type(cmdwin_type); + const int histtype = hist_char2type(cmdwin_type); if (histtype == HIST_CMD || histtype == HIST_DEBUG) { if (p_wc == TAB) { add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT); @@ -5012,7 +5071,7 @@ static int ex_window(void) /* Fill the buffer with the history. */ init_history(); - if (hislen > 0) { + if (hislen > 0 && histtype != HIST_INVALID) { i = hisidx[histtype]; if (i >= 0) { lnum = 0; @@ -5128,6 +5187,8 @@ static int ex_window(void) /* Don't execute autocommands while deleting the window. */ block_autocmds(); + // Avoid command-line window first character being concealed + curwin->w_p_cole = 0; wp = curwin; bp = curbuf; win_goto(old_curwin); diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index c537d681c6..24eebdc303 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -14,23 +14,26 @@ #define WILD_LONGEST 7 #define WILD_ALL_KEEP 8 -#define WILD_LIST_NOTFOUND 1 -#define WILD_HOME_REPLACE 2 -#define WILD_USE_NL 4 -#define WILD_NO_BEEP 8 -#define WILD_ADD_SLASH 16 -#define WILD_KEEP_ALL 32 -#define WILD_SILENT 64 -#define WILD_ESCAPE 128 -#define WILD_ICASE 256 +#define WILD_LIST_NOTFOUND 0x01 +#define WILD_HOME_REPLACE 0x02 +#define WILD_USE_NL 0x04 +#define WILD_NO_BEEP 0x08 +#define WILD_ADD_SLASH 0x10 +#define WILD_KEEP_ALL 0x20 +#define WILD_SILENT 0x40 +#define WILD_ESCAPE 0x80 +#define WILD_ICASE 0x100 +#define WILD_ALLLINKS 0x200 /// Present history tables typedef enum { - HIST_CMD, ///< Colon commands. - HIST_SEARCH, ///< Search commands. - HIST_EXPR, ///< Expressions (e.g. from entering = register). - HIST_INPUT, ///< input() lines. - HIST_DEBUG, ///< Debug commands. + HIST_DEFAULT = -2, ///< Default (current) history. + HIST_INVALID = -1, ///< Unknown history. + HIST_CMD = 0, ///< Colon commands. + HIST_SEARCH, ///< Search commands. + HIST_EXPR, ///< Expressions (e.g. from entering = register). + HIST_INPUT, ///< input() lines. + HIST_DEBUG, ///< Debug commands. } HistoryType; /// Number of history tables diff --git a/src/nvim/farsi.c b/src/nvim/farsi.c index 47a132c0d0..61e17128ea 100644 --- a/src/nvim/farsi.c +++ b/src/nvim/farsi.c @@ -100,8 +100,9 @@ static char_u toF_Xor_X_(int c) case F_HE : tempc = _HE; - if (p_ri && - (curwin->w_cursor.col + 1 < (colnr_T)STRLEN(get_cursor_line_ptr()))) { + if (p_ri + && (curwin->w_cursor.col + 1 + < (colnr_T)STRLEN(get_cursor_line_ptr()))) { inc_cursor(); if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { tempc = _HE_; @@ -526,8 +527,8 @@ static void chg_l_toXor_X(void) { char_u tempc; - if ((curwin->w_cursor.col != 0) && - (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(get_cursor_line_ptr()))) { + if ((curwin->w_cursor.col != 0) + && (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(get_cursor_line_ptr()))) { return; } @@ -680,17 +681,17 @@ int fkmap(int c) } } - if ((c < 0x100) && - (isalpha(c) || - (c == '&') || - (c == '^') || - (c == ';') || - (c == '\'') || - (c == ',') || - (c == '[') || - (c == ']') || - (c == '{') || - (c == '}'))) { + if ((c < 0x100) + && (isalpha(c) + || (c == '&') + || (c == '^') + || (c == ';') + || (c == '\'') + || (c == ',') + || (c == '[') + || (c == ']') + || (c == '{') + || (c == '}'))) { chg_r_to_Xor_X_(); } diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 4f345158cf..beefc4238e 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -484,28 +484,21 @@ vim_findfile_init ( len = (int)(p - search_ctx->ffsc_fix_path) - 1; STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len); add_pathsep((char *)ff_expand_buffer); - } else + } else { len = (int)STRLEN(search_ctx->ffsc_fix_path); + } if (search_ctx->ffsc_wc_path != NULL) { wc_path = vim_strsave(search_ctx->ffsc_wc_path); temp = xmalloc(STRLEN(search_ctx->ffsc_wc_path) + STRLEN(search_ctx->ffsc_fix_path + len) + 1); - } - - if (temp == NULL || wc_path == NULL) { - xfree(buf); - xfree(temp); + STRCPY(temp, search_ctx->ffsc_fix_path + len); + STRCAT(temp, search_ctx->ffsc_wc_path); + xfree(search_ctx->ffsc_wc_path); xfree(wc_path); - goto error_return; + search_ctx->ffsc_wc_path = temp; } - - STRCPY(temp, search_ctx->ffsc_fix_path + len); - STRCAT(temp, search_ctx->ffsc_wc_path); - xfree(search_ctx->ffsc_wc_path); - xfree(wc_path); - search_ctx->ffsc_wc_path = temp; } xfree(buf); } @@ -1046,41 +1039,44 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename, ff_visited_l return retptr; } -/* - * check if two wildcard paths are equal. Returns TRUE or FALSE. - * They are equal if: - * - both paths are NULL - * - they have the same length - * - char by char comparison is OK - * - the only differences are in the counters behind a '**', so - * '**\20' is equal to '**\24' - */ -static int ff_wc_equal(char_u *s1, char_u *s2) +// Check if two wildcard paths are equal. +// They are equal if: +// - both paths are NULL +// - they have the same length +// - char by char comparison is OK +// - the only differences are in the counters behind a '**', so +// '**\20' is equal to '**\24' +static bool ff_wc_equal(char_u *s1, char_u *s2) { - int i; + int i, j; + int c1 = NUL; + int c2 = NUL; int prev1 = NUL; int prev2 = NUL; - if (s1 == s2) - return TRUE; - - if (s1 == NULL || s2 == NULL) - return FALSE; + if (s1 == s2) { + return true; + } - if (STRLEN(s1) != STRLEN(s2)) - return FAIL; + if (s1 == NULL || s2 == NULL) { + return false; + } - for (i = 0; s1[i] != NUL && s2[i] != NUL; i += MB_PTR2LEN(s1 + i)) { - int c1 = PTR2CHAR(s1 + i); - int c2 = PTR2CHAR(s2 + i); + for (i = 0, j = 0; s1[i] != NUL && s2[j] != NUL;) { + c1 = PTR2CHAR(s1 + i); + c2 = PTR2CHAR(s2 + j); if ((p_fic ? vim_tolower(c1) != vim_tolower(c2) : c1 != c2) - && (prev1 != '*' || prev2 != '*')) - return FAIL; + && (prev1 != '*' || prev2 != '*')) { + return false; + } prev2 = prev1; prev1 = c1; + + i += MB_PTR2LEN(s1 + i); + j += MB_PTR2LEN(s2 + j); } - return TRUE; + return s1[i] == s2[j]; } /* @@ -1111,10 +1107,11 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u * if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0) || (!url && vp->file_id_valid && os_fileid_equal(&(vp->file_id), &file_id))) { - /* are the wildcard parts equal */ - if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE) - /* already visited */ + // are the wildcard parts equal + if (ff_wc_equal(vp->ffv_wc_path, wc_path)) { + // already visited return FAIL; + } } } @@ -1340,6 +1337,7 @@ void free_findfile(void) * * options: * FNAME_MESS give error message when not found + * FNAME_UNESC unescape backslashes * * Uses NameBuff[]! * @@ -1385,6 +1383,14 @@ find_file_in_path_option ( xfree(ff_file_to_find); ff_file_to_find = vim_strsave(NameBuff); + if (options & FNAME_UNESC) { + // Change all "\ " to " ". + for (ptr = ff_file_to_find; *ptr != NUL; ++ptr) { + if (ptr[0] == '\\' && ptr[1] == ' ') { + memmove(ptr, ptr + 1, STRLEN(ptr)); + } + } + } } rel_to_curdir = (ff_file_to_find[0] == '.' @@ -1394,8 +1400,14 @@ find_file_in_path_option ( && (ff_file_to_find[2] == NUL || vim_ispathsep(ff_file_to_find[2]))))); if (vim_isAbsName(ff_file_to_find) - /* "..", "../path", "." and "./path": don't use the path_option */ + // "..", "../path", "." and "./path": don't use the path_option || rel_to_curdir +#if defined(WIN32) + // handle "\tmp" as absolute path + || vim_ispathsep(ff_file_to_find[0]) + // handle "c:name" as absolute path + || (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':') +#endif ) { /* * Absolute path, no need to use "path_option". diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index bc5b08ef24..db1469db97 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -7,6 +7,7 @@ #include <stdbool.h> #include <string.h> #include <inttypes.h> +#include <fcntl.h> #include "nvim/vim.h" #include "nvim/ascii.h" @@ -44,7 +45,6 @@ #include "nvim/search.h" #include "nvim/sha256.h" #include "nvim/strings.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/types.h" #include "nvim/undo.h" @@ -366,14 +366,13 @@ readfile ( /* don't display the file info for another buffer now */ need_fileinfo = FALSE; - /* - * For Unix: Use the short file name whenever possible. - * Avoids problems with networks and when directory names are changed. - * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to - * another directory, which we don't detect. - */ - if (sfname == NULL) + // For Unix: Use the short file name whenever possible. + // Avoids problems with networks and when directory names are changed. + // Don't do this for Windows, a "cd" in a sub-shell may have moved us to + // another directory, which we don't detect. + if (sfname == NULL) { sfname = fname; + } #if defined(UNIX) fname = sfname; #endif @@ -606,13 +605,14 @@ readfile ( * Don't do this for a "nofile" or "nowrite" buffer type. */ if (!bt_dontwrite(curbuf)) { check_need_swap(newfile); - if (!read_stdin && (curbuf != old_curbuf - || (using_b_ffname && (old_b_ffname != curbuf->b_ffname)) - || (using_b_fname && - (old_b_fname != curbuf->b_fname)))) { + if (!read_stdin + && (curbuf != old_curbuf + || (using_b_ffname && (old_b_ffname != curbuf->b_ffname)) + || (using_b_fname && (old_b_fname != curbuf->b_fname)))) { EMSG(_(e_auchangedbuf)); - if (!read_buffer) + if (!read_buffer) { close(fd); + } return FAIL; } #ifdef UNIX @@ -1543,6 +1543,11 @@ rewind_retry: if (fileformat == EOL_UNKNOWN) { /* First try finding a NL, for Dos and Unix */ if (try_dos || try_unix) { + // Reset the carriage return counter. + if (try_mac) { + try_mac = 1; + } + for (p = ptr; p < ptr + size; ++p) { if (*p == NL) { if (!try_unix @@ -1551,6 +1556,8 @@ rewind_retry: else fileformat = EOL_UNIX; break; + } else if (*p == CAR && try_mac) { + try_mac++; } } @@ -1571,6 +1578,10 @@ rewind_retry: if (try_mac > try_unix) fileformat = EOL_MAC; } + } else if (fileformat == EOL_UNKNOWN && try_mac == 1) { + // Looking for CR but found no end-of-line markers at all: + // use the default format. + fileformat = default_fileformat(); } } @@ -1867,7 +1878,8 @@ failed: } if (msg_add_fileformat(fileformat)) c = TRUE; - msg_add_lines(c, (long)linecnt, filesize); + + msg_add_lines(c, (long)linecnt, filesize); xfree(keep_msg); keep_msg = NULL; @@ -1922,10 +1934,10 @@ failed: check_marks_read(); /* - * Trick: We remember if the last line of the read didn't have - * an eol even when 'binary' is off, for when writing it again with - * 'binary' on. This is required for - * ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work. + * We remember if the last line of the read didn't have + * an eol even when 'binary' is off, to support turning 'fixeol' off, + * or writing the read again with 'binary' on. The latter is required + * for ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work. */ curbuf->b_no_eol_lnum = read_no_eol_lnum; @@ -1980,13 +1992,14 @@ failed: } #ifdef OPEN_CHR_FILES -/* - * Returns TRUE if the file name argument is of the form "/dev/fd/\d\+", - * which is the name of files used for process substitution output by - * some shells on some operating systems, e.g., bash on SunOS. - * Do not accept "/dev/fd/[012]", opening these may hang Vim. - */ -static int is_dev_fd_file(char_u *fname) +/// Returns true if the file name argument is of the form "/dev/fd/\d\+", +/// which is the name of files used for process substitution output by +/// some shells on some operating systems, e.g., bash on SunOS. +/// Do not accept "/dev/fd/[012]", opening these may hang Vim. +/// +/// @param fname file name to check +static bool is_dev_fd_file(char_u *fname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { return STRNCMP(fname, "/dev/fd/", 8) == 0 && ascii_isdigit(fname[8]) @@ -2126,9 +2139,10 @@ readfile_charconvert ( else { close(*fdp); /* close the input file, ignore errors */ *fdp = -1; - if (eval_charconvert(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc, - fname, tmpname) == FAIL) + if (eval_charconvert((char *) fenc, enc_utf8 ? "utf-8" : (char *) p_enc, + (char *) fname, (char *) tmpname) == FAIL) { errmsg = (char_u *)_("Conversion with 'charconvert' failed"); + } if (errmsg == NULL && (*fdp = os_open((char *)tmpname, O_RDONLY, 0)) < 0) { errmsg = (char_u *)_("can't read output of 'charconvert'"); } @@ -2341,13 +2355,12 @@ buf_write ( if (sfname == NULL) sfname = fname; - /* - * For Unix: Use the short file name whenever possible. - * Avoids problems with networks and when directory names are changed. - * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to - * another directory, which we don't detect - */ - ffname = fname; /* remember full fname */ + + // For Unix: Use the short file name whenever possible. + // Avoids problems with networks and when directory names are changed. + // Don't do this for Windows, a "cd" in a sub-shell may have moved us to + // another directory, which we don't detect. + ffname = fname; // remember full fname #ifdef UNIX fname = sfname; #endif @@ -2564,7 +2577,7 @@ buf_write ( errmsg = (char_u *)_("is a directory"); goto fail; } - if (mch_nodetype(fname) != NODE_WRITABLE) { + if (os_nodetype((char *)fname) != NODE_WRITABLE) { errnum = (char_u *)"E503: "; errmsg = (char_u *)_("is not a file or writable device"); goto fail; @@ -2576,11 +2589,11 @@ buf_write ( perm = -1; } } -#else /* !UNIX */ +#else /* win32 */ /* * Check for a writable device name. */ - c = mch_nodetype(fname); + c = os_nodetype((char *)fname); if (c == NODE_OTHER) { errnum = (char_u *)"E503: "; errmsg = (char_u *)_("is not a file or writable device"); @@ -2676,7 +2689,6 @@ buf_write ( } else if ((bkc & BKC_AUTO)) { /* "auto" */ int i; -# ifdef UNIX /* * Don't rename the file when: * - it's a hard link @@ -2687,9 +2699,7 @@ buf_write ( || !os_fileinfo_link((char *)fname, &file_info) || !os_fileinfo_id_equal(&file_info, &file_info_old)) { backup_copy = TRUE; - } else -# endif - { + } else { /* * Check if we can create a file and set the owner/group to * the ones from the original file. @@ -3310,7 +3320,7 @@ restore_backup: /* write failed or last line has no EOL: stop here */ if (end == 0 || (lnum == end - && write_bin + && (write_bin || !buf->b_p_fixeol) && (lnum == buf->b_no_eol_lnum || (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol)))) { ++lnum; /* written the line, count it */ @@ -3357,16 +3367,16 @@ restore_backup: nchars += len; } -#if defined(UNIX) && defined(HAVE_FSYNC) - /* On many journalling file systems there is a bug that causes both the - * original and the backup file to be lost when halting the system right - * after writing the file. That's because only the meta-data is - * journalled. Syncing the file slows down the system, but assures it has - * been written to disk and we don't lose it. - * For a device do try the fsync() but don't complain if it does not work - * (could be a pipe). - * If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. */ - if (p_fs && fsync(fd) != 0 && !device) { +#if defined(UNIX) + // On many journalling file systems there is a bug that causes both the + // original and the backup file to be lost when halting the system right + // after writing the file. That's because only the meta-data is + // journalled. Syncing the file slows down the system, but assures it has + // been written to disk and we don't lose it. + // For a device do try the fsync() but don't complain if it does not work + // (could be a pipe). + // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. + if (p_fs && os_fsync(fd) != 0 && !device) { errmsg = (char_u *)_("E667: Fsync failed"); end = 0; } @@ -3423,9 +3433,9 @@ restore_backup: * with 'charconvert' to (overwrite) the output file. */ if (end != 0) { - if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc, - wfname, fname) == FAIL) { - write_info.bw_conv_error = TRUE; + if (eval_charconvert(enc_utf8 ? "utf-8" : (char *) p_enc, (char *) fenc, + (char *) wfname, (char *) fname) == FAIL) { + write_info.bw_conv_error = true; end = 0; } } @@ -3795,29 +3805,30 @@ void msg_add_fname(buf_T *buf, char_u *fname) STRCAT(IObuff, "\" "); } -/* - * Append message for text mode to IObuff. - * Return TRUE if something appended. - */ -static int msg_add_fileformat(int eol_type) +/// Append message for text mode to IObuff. +/// +/// @param eol_type line ending type +/// +/// @return true if something was appended. +static bool msg_add_fileformat(int eol_type) { #ifndef USE_CRNL if (eol_type == EOL_DOS) { STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]")); - return TRUE; + return true; } #endif if (eol_type == EOL_MAC) { STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]")); - return TRUE; + return true; } #ifdef USE_CRNL if (eol_type == EOL_UNIX) { STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]")); - return TRUE; + return true; } #endif - return FALSE; + return false; } /* @@ -3881,7 +3892,11 @@ static int check_mtime(buf_T *buf, FileInfo *file_info) return OK; } -static int time_differs(long t1, long t2) +/// Return true if the times differ +/// +/// @param t1 first time +/// @param t2 second time +static bool time_differs(long t1, long t2) FUNC_ATTR_CONST { #if defined(__linux__) || defined(MSWIN) /* On a FAT filesystem, esp. under Linux, there are only 5 bits to store @@ -4070,19 +4085,17 @@ static int buf_write_bytes(struct bw_info *ip) return (wlen < len) ? FAIL : OK; } -/* - * Convert a Unicode character to bytes. - * Return TRUE for an error, FALSE when it's OK. - */ -static int -ucs2bytes ( - unsigned c, /* in: character */ - char_u **pp, /* in/out: pointer to result */ - int flags /* FIO_ flags */ -) +/// Convert a Unicode character to bytes. +/// +/// @param c character to convert +/// @param[in,out] pp pointer to store the result at +/// @param flags FIO_ flags that specify which encoding to use +/// +/// @return true for an error, false when it's OK. +static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL { char_u *p = *pp; - int error = FALSE; + bool error = false; int cc; @@ -4104,8 +4117,9 @@ ucs2bytes ( /* Make two words, ten bits of the character in each. First * word is 0xd800 - 0xdbff, second one 0xdc00 - 0xdfff */ c -= 0x10000; - if (c >= 0x100000) - error = TRUE; + if (c >= 0x100000) { + error = true; + } cc = ((c >> 10) & 0x3ff) + 0xd800; if (flags & FIO_ENDIAN_L) { *p++ = cc; @@ -4115,8 +4129,9 @@ ucs2bytes ( *p++ = cc; } c = (c & 0x3ff) + 0xdc00; - } else - error = TRUE; + } else { + error = true; + } } if (flags & FIO_ENDIAN_L) { *p++ = c; @@ -4127,7 +4142,7 @@ ucs2bytes ( } } else { /* Latin1 */ if (c >= 0x100) { - error = TRUE; + error = true; *p++ = 0xBF; } else *p++ = c; @@ -4137,11 +4152,14 @@ ucs2bytes ( return error; } -/* - * Return TRUE if file encoding "fenc" requires conversion from or to - * 'encoding'. - */ -static int need_conversion(char_u *fenc) +/// Return true if file encoding "fenc" requires conversion from or to +/// 'encoding'. +/// +/// @param fenc file encoding to check +/// +/// @return true if conversion is required +static bool need_conversion(const char_u *fenc) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int same_encoding; int enc_flags; @@ -4168,19 +4186,19 @@ static int need_conversion(char_u *fenc) return !(enc_utf8 && fenc_flags == FIO_UTF8); } -/* - * Check "ptr" for a unicode encoding and return the FIO_ flags needed for the - * internal conversion. - * if "ptr" is an empty string, use 'encoding'. - */ -static int get_fio_flags(char_u *ptr) +/// Return the FIO_ flags needed for the internal conversion if 'name' was +/// unicode or latin1, otherwise 0. If "name" is an empty string, +/// use 'encoding'. +/// +/// @param name string to check for encoding +static int get_fio_flags(const char_u *name) { int prop; - if (*ptr == NUL) - ptr = p_enc; - - prop = enc_canon_props(ptr); + if (*name == NUL) { + name = p_enc; + } + prop = enc_canon_props(name); if (prop & ENC_UNICODE) { if (prop & ENC_2BYTE) { if (prop & ENC_ENDIAN_L) @@ -4352,8 +4370,8 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) // (we need the full path in case :cd is used). if (fname == NULL || *fname == NUL) { retval = xmalloc(MAXPATHL + extlen + 3); // +3 for PATHSEP, "_" (Win), NUL - if (os_dirname((char_u *)retval, MAXPATHL) == FAIL || - (fnamelen = strlen(retval)) == 0) { + if (os_dirname((char_u *)retval, MAXPATHL) == FAIL + || (fnamelen = strlen(retval)) == 0) { xfree(retval); return NULL; } @@ -4412,11 +4430,15 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) return retval; } -/* - * Like fgets(), but if the file line is too long, it is truncated and the - * rest of the line is thrown away. Returns TRUE for end-of-file. - */ -int vim_fgets(char_u *buf, int size, FILE *fp) +/// Like fgets(), but if the file line is too long, it is truncated and the +/// rest of the line is thrown away. +/// +/// @param[out] buf buffer to fill +/// @param size size of the buffer +/// @param fp file to read from +/// +/// @return true for end-of-file. +bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL { char *eof; #define FGETS_SIZE 200 @@ -4716,7 +4738,6 @@ buf_check_timestamp ( { int retval = 0; char_u *path; - char_u *tbuf; char *mesg = NULL; char *mesg2 = ""; int helpmesg = FALSE; @@ -4786,19 +4807,17 @@ buf_check_timestamp ( else reason = "time"; - /* - * Only give the warning if there are no FileChangedShell - * autocommands. - * Avoid being called recursively by setting "busy". - */ - busy = TRUE; - set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1); - set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1); - ++allbuf_lock; + // Only give the warning if there are no FileChangedShell + // autocommands. + // Avoid being called recursively by setting "busy". + busy = true; + set_vim_var_string(VV_FCS_REASON, reason, -1); + set_vim_var_string(VV_FCS_CHOICE, "", -1); + allbuf_lock++; n = apply_autocmds(EVENT_FILECHANGEDSHELL, - buf->b_fname, buf->b_fname, FALSE, buf); - --allbuf_lock; - busy = FALSE; + buf->b_fname, buf->b_fname, false, buf); + allbuf_lock--; + busy = false; if (n) { if (!buf_valid(buf)) EMSG(_("E246: FileChangedShell autocommand deleted buffer")); @@ -4852,35 +4871,39 @@ buf_check_timestamp ( if (mesg != NULL) { path = home_replace_save(buf, buf->b_fname); - if (!helpmesg) + if (!helpmesg) { mesg2 = ""; - tbuf = xmalloc(STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2); - sprintf((char *)tbuf, mesg, path); - /* Set warningmsg here, before the unimportant and output-specific - * mesg2 has been appended. */ + } + const size_t tbuf_len = STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2; + char *const tbuf = xmalloc(tbuf_len); + snprintf(tbuf, tbuf_len, mesg, path); + // Set warningmsg here, before the unimportant and output-specific + // mesg2 has been appended. set_vim_var_string(VV_WARNINGMSG, tbuf, -1); if (can_reload) { if (*mesg2 != NUL) { - STRCAT(tbuf, "\n"); - STRCAT(tbuf, mesg2); + strncat(tbuf, "\n", tbuf_len); + strncat(tbuf, mesg2, tbuf_len); + } + if (do_dialog(VIM_WARNING, (char_u *) _("Warning"), (char_u *) tbuf, + (char_u *) _("&OK\n&Load File"), 1, NULL, true) == 2) { + reload = true; } - if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), tbuf, - (char_u *)_("&OK\n&Load File"), 1, NULL, TRUE) == 2) - reload = TRUE; } else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) { if (*mesg2 != NUL) { - STRCAT(tbuf, "; "); - STRCAT(tbuf, mesg2); + strncat(tbuf, "; ", tbuf_len); + strncat(tbuf, mesg2, tbuf_len); } EMSG(tbuf); retval = 2; } else { if (!autocmd_busy) { msg_start(); - msg_puts_attr(tbuf, hl_attr(HLF_E) + MSG_HIST); - if (*mesg2 != NUL) + msg_puts_attr((char_u *) tbuf, hl_attr(HLF_E) + MSG_HIST); + if (*mesg2 != NUL) { msg_puts_attr((char_u *)mesg2, hl_attr(HLF_W) + MSG_HIST); + } msg_clr_eos(); (void)msg_end(); if (emsg_silent == 0) { @@ -5074,22 +5097,167 @@ void write_lnum_adjust(linenr_T offset) } #if defined(BACKSLASH_IN_FILENAME) -/* - * Convert all backslashes in fname to forward slashes in-place. - */ +/// Convert all backslashes in fname to forward slashes in-place, +/// unless when it looks like a URL. void forward_slash(char_u *fname) { char_u *p; - for (p = fname; *p != NUL; ++p) - /* The Big5 encoding can have '\' in the trail byte. */ - if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) - ++p; - else if (*p == '\\') + if (path_with_url(fname)) { + return; + } + for (p = fname; *p != NUL; p++) { + // The Big5 encoding can have '\' in the trail byte. + if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) { + p++; + } else if (*p == '\\') { *p = '/'; + } + } } #endif +/// Name of Vim's own temp dir. Ends in a slash. +static char_u *vim_tempdir = NULL; + +/// Create a directory for private use by this instance of Neovim. +/// This is done once, and the same directory is used for all temp files. +/// This method avoids security problems because of symlink attacks et al. +/// It's also a bit faster, because we only need to check for an existing +/// file when creating the directory and not for each temp file. +static void vim_maketempdir(void) +{ + static const char *temp_dirs[] = TEMP_DIR_NAMES; + // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. + char_u template[TEMP_FILE_PATH_MAXLEN]; + char_u path[TEMP_FILE_PATH_MAXLEN]; + for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) { + // Expand environment variables, leave room for "/nvimXXXXXX/999999999" + expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); + if (!os_isdir(template)) { // directory doesn't exist + continue; + } + + add_pathsep((char *)template); + // Concatenate with temporary directory name pattern + STRCAT(template, "nvimXXXXXX"); + + if (os_mkdtemp((const char *)template, (char *)path) != 0) { + continue; + } + + if (vim_settempdir((char *)path)) { + // Successfully created and set temporary directory so stop trying. + break; + } else { + // Couldn't set `vim_tempdir` to `path` so remove created directory. + os_rmdir((char *)path); + } + } +} + +/// Delete "name" and everything in it, recursively. +/// @param name The path which should be deleted. +/// @return 0 for success, -1 if some file was not deleted. +int delete_recursive(char_u *name) +{ + int result = 0; + + if (os_isrealdir(name)) { + snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT + + char_u **files; + int file_count; + char_u *exp = vim_strsave(NameBuff); + if (gen_expand_wildcards(1, &exp, &file_count, &files, + EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS + | EW_DODOT | EW_EMPTYOK) == OK) { + for (int i = 0; i < file_count; i++) { + if (delete_recursive(files[i]) != 0) { + result = -1; + } + } + FreeWild(file_count, files); + } else { + result = -1; + } + + xfree(exp); + os_rmdir((char *)name); + } else { + result = os_remove((char *)name) == 0 ? 0 : -1; + } + + return result; +} + +/// Delete the temp directory and all files it contains. +void vim_deltempdir(void) +{ + if (vim_tempdir != NULL) { + // remove the trailing path separator + path_tail(vim_tempdir)[-1] = NUL; + delete_recursive(vim_tempdir); + xfree(vim_tempdir); + vim_tempdir = NULL; + } +} + +/// Get the name of temp directory. This directory would be created on the first +/// call to this function. +char_u *vim_gettempdir(void) +{ + if (vim_tempdir == NULL) { + vim_maketempdir(); + } + + return vim_tempdir; +} + +/// Set Neovim own temporary directory name to `tempdir`. This directory should +/// be already created. Expand this name to a full path and put it in +/// `vim_tempdir`. This avoids that using `:cd` would confuse us. +/// +/// @param tempdir must be no longer than MAXPATHL. +/// +/// @return false if we run out of memory. +static bool vim_settempdir(char *tempdir) +{ + char *buf = verbose_try_malloc(MAXPATHL + 2); + if (!buf) { + return false; + } + vim_FullName(tempdir, buf, MAXPATHL, false); + add_pathsep(buf); + vim_tempdir = (char_u *)xstrdup(buf); + xfree(buf); + return true; +} + +/// Return a unique name that can be used for a temp file. +/// +/// @note The temp file is NOT created. +/// +/// @return pointer to the temp file name or NULL if Neovim can't create +/// temporary directory for its own temporary files. +char_u *vim_tempname(void) +{ + // Temp filename counter. + static uint32_t temp_count; + + char_u *tempdir = vim_gettempdir(); + if (!tempdir) { + return NULL; + } + + // There is no need to check if the file exists, because we own the directory + // and nobody else creates a file in it. + char_u template[TEMP_FILE_PATH_MAXLEN]; + snprintf((char *)template, TEMP_FILE_PATH_MAXLEN, + "%s%" PRIu32, tempdir, temp_count++); + return vim_strsave(template); +} + /* * Code for automatic commands. @@ -5315,11 +5483,13 @@ static void au_del_group(char_u *name) } } -/* - * Find the ID of an autocmd group name. - * Return it's ID. Returns AUGROUP_ERROR (< 0) for error. - */ -static int au_find_group(char_u *name) +/// Find the ID of an autocmd group name. +/// +/// @param name augroup name +/// +/// @return the ID or AUGROUP_ERROR (< 0) for error. +static int au_find_group(const char_u *name) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { for (int i = 0; i < augroups.ga_len; ++i) { if (AUGROUP_NAME(i) != NULL && STRCMP(AUGROUP_NAME(i), name) == 0) { @@ -5329,10 +5499,11 @@ static int au_find_group(char_u *name) return AUGROUP_ERROR; } -/* - * Return TRUE if augroup "name" exists. - */ -int au_has_group(char_u *name) +/// Return true if augroup "name" exists. +/// +/// @param name augroup name +bool au_has_group(const char_u *name) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return au_find_group(name) != AUGROUP_ERROR; } @@ -5449,21 +5620,24 @@ find_end_event ( return pat; } -/* - * Return TRUE if "event" is included in 'eventignore'. - */ -static int event_ignored(event_T event) +/// Return true if "event" is included in 'eventignore'. +/// +/// @param event event to check +static bool event_ignored(event_T event) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *p = p_ei; + char_u *p = p_ei; while (*p != NUL) { - if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) - return TRUE; - if (event_name2nr(p, &p) == event) - return TRUE; + if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) { + return true; + } + if (event_name2nr(p, &p) == event) { + return true; + } } - return FALSE; + return false; } /* @@ -5980,18 +6154,19 @@ void ex_doautoall(exarg_T *eap) check_cursor(); /* just in case lines got deleted */ } -/* - * Check *argp for <nomodeline>. When it is present return FALSE, otherwise - * return TRUE and advance *argp to after it. - * Thus return TRUE when do_modelines() should be called. - */ -int check_nomodeline(char_u **argp) +/// Check *argp for <nomodeline>. When it is present return false, otherwise +/// return true and advance *argp to after it. Thus do_modelines() should be +/// called when true is returned. +/// +/// @param[in,out] argp argument string +bool check_nomodeline(char_u **argp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (STRNCMP(*argp, "<nomodeline>", 12) == 0) { *argp = skipwhite(*argp + 12); - return FALSE; + return false; } - return TRUE; + return true; } /* @@ -6168,76 +6343,78 @@ win_found: static int autocmd_nested = FALSE; -/* - * Execute autocommands for "event" and file name "fname". - * Return TRUE if some commands were executed. - */ -int -apply_autocmds ( - event_T event, - char_u *fname, /* NULL or empty means use actual file name */ - char_u *fname_io, /* fname to use for <afile> on cmdline */ - int force, /* when TRUE, ignore autocmd_busy */ - buf_T *buf /* buffer for <abuf> */ -) +/// Execute autocommands for "event" and file name "fname". +/// +/// @param event event that occured +/// @param fname filename, NULL or empty means use actual file name +/// @param fname_io filename to use for <afile> on cmdline +/// @param force When true, ignore autocmd_busy +/// @param buf Buffer for <abuf> +/// +/// @return true if some commands were executed. +bool apply_autocmds(event_T event, char_u *fname, char_u *fname_io, bool force, + buf_T *buf) { return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, NULL); } -/* - * Like apply_autocmds(), but with extra "eap" argument. This takes care of - * setting v:filearg. - */ -static int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap) +/// Like apply_autocmds(), but with extra "eap" argument. This takes care of +/// setting v:filearg. +/// +/// @param event event that occured +/// @param fname NULL or empty means use actual file name +/// @param fname_io fname to use for <afile> on cmdline +/// @param force When true, ignore autocmd_busy +/// @param buf Buffer for <abuf> +/// @param exarg Ex command arguments +/// +/// @return true if some commands were executed. +static bool apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, + bool force, buf_T *buf, exarg_T *eap) { return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, eap); } -/* - * Like apply_autocmds(), but handles the caller's retval. If the script - * processing is being aborted or if retval is FAIL when inside a try - * conditional, no autocommands are executed. If otherwise the autocommands - * cause the script to be aborted, retval is set to FAIL. - */ -int -apply_autocmds_retval ( - event_T event, - char_u *fname, /* NULL or empty means use actual file name */ - char_u *fname_io, /* fname to use for <afile> on cmdline */ - int force, /* when TRUE, ignore autocmd_busy */ - buf_T *buf, /* buffer for <abuf> */ - int *retval /* pointer to caller's retval */ -) +/// Like apply_autocmds(), but handles the caller's retval. If the script +/// processing is being aborted or if retval is FAIL when inside a try +/// conditional, no autocommands are executed. If otherwise the autocommands +/// cause the script to be aborted, retval is set to FAIL. +/// +/// @param event event that occured +/// @param fname NULL or empty means use actual file name +/// @param fname_io fname to use for <afile> on cmdline +/// @param force When true, ignore autocmd_busy +/// @param buf Buffer for <abuf> +/// @param[in,out] retval caller's retval +/// +/// @return true if some autocommands were executed +bool apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, + bool force, buf_T *buf, int *retval) { - int did_cmd; - - if (should_abort(*retval)) - return FALSE; + if (should_abort(*retval)) { + return false; + } - did_cmd = apply_autocmds_group(event, fname, fname_io, force, - AUGROUP_ALL, buf, NULL); - if (did_cmd - && aborting() - ) + bool did_cmd = apply_autocmds_group(event, fname, fname_io, force, + AUGROUP_ALL, buf, NULL); + if (did_cmd && aborting()) { *retval = FAIL; + } return did_cmd; } -/* - * Return TRUE when there is a CursorHold autocommand defined. - */ -int has_cursorhold(void) +/// Return true when there is a CursorHold/CursorHoldI autocommand defined for +/// the current mode. +bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return first_autopat[(int)(get_real_state() == NORMAL_BUSY ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL; } -/* - * Return TRUE if the CursorHold event can be triggered. - */ -int trigger_cursorhold(void) +/// Return true if the CursorHold/CursorHoldI event can be triggered. +bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { int state; @@ -6248,75 +6425,42 @@ int trigger_cursorhold(void) && !ins_compl_active() ) { state = get_real_state(); - if (state == NORMAL_BUSY || (state & INSERT) != 0) - return TRUE; + if (state == NORMAL_BUSY || (state & INSERT) != 0) { + return true; + } } - return FALSE; -} - -/* - * Return TRUE when there is a CursorMoved autocommand defined. - */ -int has_cursormoved(void) -{ - return first_autopat[(int)EVENT_CURSORMOVED] != NULL; -} - -/* - * Return TRUE when there is a CursorMovedI autocommand defined. - */ -int has_cursormovedI(void) -{ - return first_autopat[(int)EVENT_CURSORMOVEDI] != NULL; -} - -/* - * Return TRUE when there is a TextChanged autocommand defined. - */ -int has_textchanged(void) -{ - return first_autopat[(int)EVENT_TEXTCHANGED] != NULL; + return false; } -/* - * Return TRUE when there is a TextChangedI autocommand defined. - */ -int has_textchangedI(void) -{ - return first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL; -} - -/* - * Return TRUE when there is an InsertCharPre autocommand defined. - */ -int has_insertcharpre(void) -{ - return first_autopat[(int)EVENT_INSERTCHARPRE] != NULL; -} - -/// @returns true when there is an CmdUndefined autocommand defined. -int has_cmdundefined(void) +/// Return true if "event" autocommand is defined. +/// +/// @param event the autocommand to check +bool has_event(int event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return first_autopat[(int)EVENT_CMDUNDEFINED] != NULL; + return first_autopat[event] != NULL; } -static int -apply_autocmds_group ( - event_T event, - char_u *fname, /* NULL or empty means use actual file name */ - char_u *fname_io, /* fname to use for <afile> on cmdline, NULL means - use fname */ - int force, /* when TRUE, ignore autocmd_busy */ - int group, /* group ID, or AUGROUP_ALL */ - buf_T *buf, /* buffer for <abuf> */ - exarg_T *eap /* command arguments */ -) +/// Execute autocommands for "event" and file name "fname". +/// +/// @param event event that occured +/// @param fname filename, NULL or empty means use actual file name +/// @param fname_io filename to use for <afile> on cmdline, +/// NULL means use `fname`. +/// @param force When true, ignore autocmd_busy +/// @param group autocmd group ID or AUGROUP_ALL +/// @param buf Buffer for <abuf> +/// @param eap Ex command arguments +/// +/// @return true if some commands were executed. +static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, + bool force, int group, buf_T *buf, + exarg_T *eap) { char_u *sfname = NULL; /* short file name */ char_u *tail; bool save_changed; buf_T *old_curbuf; - int retval = FALSE; + bool retval = false; char_u *save_sourcing_name; linenr_T save_sourcing_lnum; char_u *save_autocmd_fname; @@ -6470,17 +6614,16 @@ apply_autocmds_group ( } if (fname == NULL) { /* out of memory */ xfree(sfname); - retval = FALSE; + retval = false; goto BYPASS_AU; } #ifdef BACKSLASH_IN_FILENAME - /* - * Replace all backslashes with forward slashes. This makes the - * autocommand patterns portable between Unix and MS-DOS. - */ - if (sfname != NULL) + // Replace all backslashes with forward slashes. This makes the + // autocommand patterns portable between Unix and Windows. + if (sfname != NULL) { forward_slash(sfname); + } forward_slash(fname); #endif @@ -6554,17 +6697,19 @@ apply_autocmds_group ( if (eap != NULL) { save_cmdarg = set_cmdarg(eap, NULL); set_vim_var_nr(VV_CMDBANG, (long)eap->forceit); - } else - save_cmdarg = NULL; /* avoid gcc warning */ - retval = TRUE; - /* mark the last pattern, to avoid an endless loop when more patterns - * are added when executing autocommands */ - for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next) - ap->last = FALSE; - ap->last = TRUE; - check_lnums(TRUE); /* make sure cursor and topline are valid */ + } else { + save_cmdarg = NULL; // avoid gcc warning + } + retval = true; + // mark the last pattern, to avoid an endless loop when more patterns + // are added when executing autocommands + for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next) { + ap->last = false; + } + ap->last = true; + check_lnums(true); // make sure cursor and topline are valid do_cmdline(NULL, getnextac, (void *)&patcmd, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); if (eap != NULL) { (void)set_cmdarg(NULL, save_cmdarg); set_vim_var_nr(VV_CMDBANG, save_cmdbang); @@ -6780,42 +6925,44 @@ char_u *getnextac(int c, void *cookie, int indent) return retval; } -/* - * Return TRUE if there is a matching autocommand for "fname". - * To account for buffer-local autocommands, function needs to know - * in which buffer the file will be opened. - */ -int has_autocmd(event_T event, char_u *sfname, buf_T *buf) +/// Return true if there is a matching autocommand for "fname". +/// To account for buffer-local autocommands, function needs to know +/// in which buffer the file will be opened. +/// +/// @param event event that occured. +/// @param sfname filename the event occured in. +/// @param buf buffer the file is open in +bool has_autocmd(event_T event, char_u *sfname, buf_T *buf) + FUNC_ATTR_WARN_UNUSED_RESULT { AutoPat *ap; char_u *fname; char_u *tail = path_tail(sfname); - int retval = FALSE; + bool retval = false; - fname = (char_u *)FullName_save((char *)sfname, FALSE); - if (fname == NULL) - return FALSE; + fname = (char_u *)FullName_save((char *)sfname, false); + if (fname == NULL) { + return false; + } #ifdef BACKSLASH_IN_FILENAME - /* - * Replace all backslashes with forward slashes. This makes the - * autocommand patterns portable between Unix and MS-DOS. - */ + // Replace all backslashes with forward slashes. This makes the + // autocommand patterns portable between Unix and Windows. sfname = vim_strsave(sfname); forward_slash(sfname); forward_slash(fname); #endif - for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) + for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { if (ap->pat != NULL && ap->cmds != NULL && (ap->buflocal_nr == 0 ? match_file_pat(NULL, &ap->reg_prog, fname, sfname, tail, ap->allow_dirs) - : buf != NULL && ap->buflocal_nr == buf->b_fnum - )) { - retval = TRUE; + : buf != NULL && ap->buflocal_nr == buf->b_fnum)) { + retval = true; break; } + } xfree(fname); #ifdef BACKSLASH_IN_FILENAME @@ -6903,29 +7050,27 @@ char_u *get_event_name(expand_T *xp, int idx) } -/* - * Return TRUE if autocmd is supported. - */ -int autocmd_supported(char_u *name) +/// Return true if autocmd "event" is supported. +bool autocmd_supported(char_u *event) { char_u *p; - return event_name2nr(name, &p) != NUM_EVENTS; + return event_name2nr(event, &p) != NUM_EVENTS; } -/* - * Return TRUE if an autocommand is defined for a group, event and - * pattern: The group can be omitted to accept any group. "event" and "pattern" - * can be NULL to accept any event and pattern. "pattern" can be NULL to accept - * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted. - * Used for: - * exists("#Group") or - * exists("#Group#Event") or - * exists("#Group#Event#pat") or - * exists("#Event") or - * exists("#Event#pat") - */ -int au_exists(char_u *arg) +/// Return true if an autocommand is defined for a group, event and +/// pattern: The group can be omitted to accept any group. +/// `event` and `pattern` can be omitted to accept any event and pattern. +/// Buffer-local patterns <buffer> or <buffer=N> are accepted. +/// Used for: +/// exists("#Group") or +/// exists("#Group#Event") or +/// exists("#Group#Event#pat") or +/// exists("#Event") or +/// exists("#Event#pat") +/// +/// @param arg autocommand string +bool au_exists(const char_u *arg) FUNC_ATTR_WARN_UNUSED_RESULT { char_u *arg_save; char_u *pattern = NULL; @@ -6935,7 +7080,7 @@ int au_exists(char_u *arg) AutoPat *ap; buf_T *buflocal_buf = NULL; int group; - int retval = FALSE; + bool retval = false; /* Make a copy so that we can change the '#' chars to a NUL. */ arg_save = vim_strsave(arg); @@ -6951,8 +7096,8 @@ int au_exists(char_u *arg) event_name = arg_save; } else { if (p == NULL) { - /* "Group": group name is present and it's recognized */ - retval = TRUE; + // "Group": group name is present and it's recognized + retval = true; goto theend; } @@ -6994,7 +7139,7 @@ int au_exists(char_u *arg) || (buflocal_buf == NULL ? fnamecmp(ap->pat, pattern) == 0 : ap->buflocal_nr == buflocal_buf->b_fnum))) { - retval = TRUE; + retval = true; break; } @@ -7009,12 +7154,13 @@ theend: /// /// Used for autocommands and 'wildignore'. /// -/// @param pattern the pattern to match with -/// @param prog the pre-compiled regprog or NULL -/// @param fname the full path of the file name -/// @param sfname the short file name or NULL -/// @param tail the tail of the path -/// @param allow_dirs allow matching with dir +/// @param pattern pattern to match with +/// @param prog pre-compiled regprog or NULL +/// @param fname full path of the file name +/// @param sfname short file name or NULL +/// @param tail tail of the path +/// @param allow_dirs Allow matching with dir +/// /// @return true if there is a match, false otherwise static bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname, char_u *sfname, char_u *tail, int allow_dirs) @@ -7053,12 +7199,17 @@ static bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname, return result; } -/* - * Return TRUE if a file matches with a pattern in "list". - * "list" is a comma-separated list of patterns, like 'wildignore'. - * "sfname" is the short file name or NULL, "ffname" the long file name. - */ -int match_file_list(char_u *list, char_u *sfname, char_u *ffname) +/// Check if a file matches with a pattern in "list". +/// "list" is a comma-separated list of patterns, like 'wildignore'. +/// "sfname" is the short file name or NULL, "ffname" the long file name. +/// +/// @param list list of patterns to match +/// @param sfname short file name +/// @param ffname full file name +/// +/// @return true if there was a match +bool match_file_list(char_u *list, char_u *sfname, char_u *ffname) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3) { char_u buf[100]; char_u *tail; @@ -7069,42 +7220,41 @@ int match_file_list(char_u *list, char_u *sfname, char_u *ffname) tail = path_tail(sfname); - /* try all patterns in 'wildignore' */ + // try all patterns in 'wildignore' p = list; while (*p) { - copy_option_part(&p, buf, 100, ","); - regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, FALSE); - if (regpat == NULL) + copy_option_part(&p, buf, ARRAY_SIZE(buf), ","); + 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); + } + match = match_file_pat(regpat, NULL, ffname, sfname, tail, (int)allow_dirs); xfree(regpat); - if (match) - return TRUE; + if (match) { + return true; + } } - return FALSE; + return false; } -/* - * Convert the given pattern "pat" which has shell style wildcards in it, into - * a regular expression, and return the result in allocated memory. If there - * is a directory path separator to be matched, then TRUE is put in - * allow_dirs, otherwise FALSE is put there -- webb. - * Handle backslashes before special characters, like "\*" and "\ ". - * - * Returns NULL on failure. - */ -char_u * -file_pat_to_reg_pat ( - char_u *pat, - char_u *pat_end, /* first char after pattern or NULL */ - char *allow_dirs, /* Result passed back out in here */ - int no_bslash /* Don't use a backward slash as pathsep */ +/// Convert the given pattern "pat" which has shell style wildcards in it, into +/// a regular expression, and return the result in allocated memory. If there +/// is a directory path separator to be matched, then TRUE is put in +/// allow_dirs, otherwise FALSE is put there -- webb. +/// Handle backslashes before special characters, like "\*" and "\ ". +/// +/// Returns NULL on failure. +char_u * file_pat_to_reg_pat( + const char_u *pat, + const char_u *pat_end, // first char after pattern or NULL + char *allow_dirs, // Result passed back out in here + int no_bslash // Don't use a backward slash as pathsep ) + FUNC_ATTR_NONNULL_ARG(1) { - char_u *endp; + const char_u *endp; char_u *reg_pat; - char_u *p; + const char_u *p; int nested = 0; int add_dollar = TRUE; @@ -7113,6 +7263,10 @@ file_pat_to_reg_pat ( if (pat_end == NULL) pat_end = pat + STRLEN(pat); + if (pat_end == pat) { + return (char_u *)xstrdup("^$"); + } + size_t size = 2; // '^' at start, '$' at end. for (p = pat; p < pat_end; p++) { @@ -7150,10 +7304,11 @@ file_pat_to_reg_pat ( else reg_pat[i++] = '^'; endp = pat_end - 1; - if (*endp == '*') { - while (endp - pat > 0 && *endp == '*') + if (endp >= pat && *endp == '*') { + while (endp - pat > 0 && *endp == '*') { endp--; - add_dollar = FALSE; + } + add_dollar = false; } for (p = pat; *p && nested >= 0 && p <= endp; p++) { switch (*p) { @@ -7208,12 +7363,12 @@ file_pat_to_reg_pat ( #ifdef BACKSLASH_IN_FILENAME && no_bslash #endif - ) + ) { reg_pat[i++] = '?'; - else if (*p == ',' || *p == '%' || *p == '#' - || *p == ' ' || *p == '{' || *p == '}') + } else if (*p == ',' || *p == '%' || *p == '#' + || ascii_isspace(*p) || *p == '{' || *p == '}') { reg_pat[i++] = *p; - else if (*p == '\\' && p[1] == '\\' && p[2] == '{') { + } else if (*p == '\\' && p[1] == '\\' && p[2] == '{') { reg_pat[i++] = '\\'; reg_pat[i++] = '{'; p += 2; diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 2e32e78062..ac3cf959c8 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -762,14 +762,18 @@ void clearFolding(win_T *win) */ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) { + if (compl_busy) { + return; + } + fold_T *fp; if (wp->w_buffer->terminal) { return; } - /* Mark all folds from top to bot as maybe-small. */ - (void)foldFind(&curwin->w_folds, top, &fp); - while (fp < (fold_T *)curwin->w_folds.ga_data + curwin->w_folds.ga_len + // Mark all folds from top to bot as maybe-small. + (void)foldFind(&wp->w_folds, top, &fp); + while (fp < (fold_T *)wp->w_folds.ga_data + wp->w_folds.ga_len && fp->fd_top < bot) { fp->fd_small = MAYBE; ++fp; @@ -1695,14 +1699,14 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, did_emsg = FALSE; if (*wp->w_p_fdt != NUL) { - char_u dashes[MAX_LEVEL + 2]; + char dashes[MAX_LEVEL + 2]; win_T *save_curwin; int level; char_u *p; - /* Set "v:foldstart" and "v:foldend". */ - set_vim_var_nr(VV_FOLDSTART, lnum); - set_vim_var_nr(VV_FOLDEND, lnume); + // Set "v:foldstart" and "v:foldend". + set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum); + set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume); /* Set "v:folddashes" to a string of "level" dashes. */ /* Set "v:foldlevel" to "level". */ @@ -1712,7 +1716,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, memset(dashes, '-', (size_t)level); dashes[level] = NUL; set_vim_var_string(VV_FOLDDASHES, dashes, -1); - set_vim_var_nr(VV_FOLDLEVEL, (long)level); + set_vim_var_nr(VV_FOLDLEVEL, (varnumber_T) level); /* skip evaluating foldtext on errors */ if (!got_fdt_error) { @@ -2106,10 +2110,11 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, */ if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level && flp->lvl > 0) { - foldFind(gap, startlnum - 1, &fp); + (void)foldFind(gap, startlnum - 1, &fp); if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len - || fp->fd_top >= startlnum) + || fp->fd_top >= startlnum) { fp = NULL; + } } /* @@ -2163,13 +2168,15 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, } } if (lvl < level + i) { - foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); - if (fp2 != NULL) + (void)foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); + if (fp2 != NULL) { bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top; - } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) - finish = TRUE; - else + } + } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) { + finish = true; + } else { break; + } } /* At the start of the first nested fold and at the end of the current @@ -2672,7 +2679,7 @@ static void foldlevelExpr(fline_T *flp) win = curwin; curwin = flp->wp; curbuf = flp->wp->w_buffer; - set_vim_var_nr(VV_LNUM, lnum); + set_vim_var_nr(VV_LNUM, (varnumber_T) lnum); flp->start = 0; flp->had_end = flp->end; diff --git a/src/nvim/garray.c b/src/nvim/garray.c index 75c3fb9a73..98cec69b54 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -174,6 +174,7 @@ char_u* ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET } /// Concatenate a string to a growarray which contains characters. +/// When "s" is NULL does not do anything. /// /// WARNING: /// - Does NOT copy the NUL at the end! @@ -183,12 +184,27 @@ char_u* ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET /// @param s void ga_concat(garray_T *gap, const char_u *restrict s) { - int len = (int)strlen((char *) s); + if (s == NULL) { + return; + } + + ga_concat_len(gap, (const char *restrict) s, strlen((char *) s)); +} + +/// Concatenate a string to a growarray which contains characters +/// +/// @param[out] gap Growarray to modify. +/// @param[in] s String to concatenate. +/// @param[in] len String length. +void ga_concat_len(garray_T *const gap, const char *restrict s, + const size_t len) + FUNC_ATTR_NONNULL_ALL +{ if (len) { - ga_grow(gap, len); + ga_grow(gap, (int) len); char *data = gap->ga_data; - memcpy(data + gap->ga_len, s, (size_t)len); - gap->ga_len += len; + memcpy(data + gap->ga_len, s, len); + gap->ga_len += (int) len; } } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 44d6c086e7..dbf0322d78 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -144,7 +144,7 @@ static int KeyNoremap = 0; /* remapping flags */ static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */ static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */ -static int last_recorded_len = 0; /* number of last recorded chars */ +static size_t last_recorded_len = 0; // number of last recorded chars static const uint8_t ui_toggle[] = { K_SPECIAL, KS_EXTRA, KE_PASTE, 0 }; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -211,7 +211,7 @@ char_u *get_recorded(void) * (possibly mapped) characters that stopped the recording. */ len = STRLEN(p); - if ((int)len >= last_recorded_len) { + if (len >= last_recorded_len) { len -= last_recorded_len; p[len] = NUL; } @@ -243,13 +243,15 @@ static void add_buff ( buffheader_T *buf, char_u *s, - long slen /* length of "s" or -1 */ + ssize_t slen // length of "s" or -1 ) { - if (slen < 0) - slen = (long)STRLEN(s); - if (slen == 0) /* don't add empty strings */ + if (slen < 0) { + slen = (ssize_t)STRLEN(s); + } + if (slen == 0) { // don't add empty strings return; + } if (buf->bh_first.b_next == NULL) { /* first add to list */ buf->bh_space = 0; @@ -263,18 +265,19 @@ add_buff ( STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); buf->bh_index = 0; - ssize_t len; - if (buf->bh_space >= (int)slen) { + size_t len; + if (buf->bh_space >= (size_t)slen) { len = STRLEN(buf->bh_curr->b_str); STRLCPY(buf->bh_curr->b_str + len, s, slen + 1); - buf->bh_space -= slen; + buf->bh_space -= (size_t)slen; } else { - if (slen < MINIMAL_SIZE) + if (slen < MINIMAL_SIZE) { len = MINIMAL_SIZE; - else - len = slen; + } else { + len = (size_t)slen; + } buffblock_T *p = xmalloc(sizeof(buffblock_T) + len); - buf->bh_space = (int)(len - slen); + buf->bh_space = len - (size_t)slen; STRLCPY(p->b_str, s, slen + 1); p->b_next = buf->bh_curr->b_next; @@ -317,11 +320,11 @@ static void add_char_buff(buffheader_T *buf, int c) if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { /* translate special key code into three byte sequence */ temp[0] = K_SPECIAL; - temp[1] = K_SECOND(c); - temp[2] = K_THIRD(c); + temp[1] = (char_u)K_SECOND(c); + temp[2] = (char_u)K_THIRD(c); temp[3] = NUL; } else { - temp[0] = c; + temp[0] = (char_u)c; temp[1] = NUL; } add_buff(buf, temp, -1L); @@ -694,10 +697,11 @@ static int read_redo(int init, int old_redo) bp = bp->b_next; p = bp->b_str; } - buf[i] = c; - if (i == n - 1) { /* last byte of a character */ - if (n != 1) + buf[i] = (char_u)c; + if (i == n - 1) { // last byte of a character + if (n != 1) { c = (*mb_ptr2char)(buf); + } break; } c = *p; @@ -882,8 +886,8 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent) setcursor(); return FAIL; } - s1 = xmalloc(newlen); - s2 = xmalloc(newlen); + s1 = xmalloc((size_t)newlen); + s2 = xmalloc((size_t)newlen); typebuf.tb_buflen = newlen; /* copy the old chars, before the insertion point */ @@ -937,7 +941,7 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent) nrm = noremap; for (i = 0; i < addlen; ++i) typebuf.tb_noremap[typebuf.tb_off + i + offset] = - (--nrm >= 0) ? val : RM_YES; + (char_u)((--nrm >= 0) ? val : RM_YES); /* tb_maplen and tb_silent only remember the length of mapped and/or * silent mappings at the start of the buffer, assuming that a mapped @@ -965,8 +969,8 @@ void ins_char_typebuf(int c) char_u buf[MB_MAXBYTES + 1]; if (IS_SPECIAL(c)) { buf[0] = K_SPECIAL; - buf[1] = K_SECOND(c); - buf[2] = K_THIRD(c); + buf[1] = (char_u)K_SECOND(c); + buf[2] = (char_u)K_THIRD(c); buf[3] = NUL; } else { buf[(*mb_char2bytes)(c, buf)] = NUL; @@ -1083,25 +1087,25 @@ void del_typebuf(int len, int offset) * Write typed characters to script file. * If recording is on put the character in the recordbuffer. */ -static void gotchars(char_u *chars, int len) +static void gotchars(char_u *chars, size_t len) { char_u *s = chars; int c; char_u buf[2]; - int todo = len; - /* remember how many chars were last recorded */ - if (Recording) + // remember how many chars were last recorded + if (Recording) { last_recorded_len += len; + } buf[1] = NUL; - while (todo--) { - /* Handle one byte at a time; no translation to be done. */ + while (len--) { + // Handle one byte at a time; no translation to be done. c = *s++; updatescript(c); if (Recording) { - buf[0] = c; + buf[0] = (char_u)c; add_buff(&recordbuff, buf, 1L); } } @@ -1380,13 +1384,15 @@ int vgetc(void) } else { mod_mask = 0x0; last_recorded_len = 0; - for (;; ) { /* this is done twice if there are modifiers */ - if (mod_mask) { /* no mapping after modifier has been read */ + for (;; ) { // this is done twice if there are modifiers + bool did_inc = false; + if (mod_mask) { // no mapping after modifier has been read ++no_mapping; ++allow_keys; + did_inc = true; // mod_mask may change value } - c = vgetorpeek(TRUE); - if (mod_mask) { + c = vgetorpeek(true); + if (did_inc) { --no_mapping; --allow_keys; } @@ -1463,10 +1469,10 @@ int vgetc(void) * Note: This will loop until enough bytes are received! */ if (has_mbyte && (n = MB_BYTE2LEN_CHECK(c)) > 1) { - ++no_mapping; - buf[0] = c; - for (i = 1; i < n; ++i) { - buf[i] = vgetorpeek(TRUE); + no_mapping++; + buf[0] = (char_u)c; + for (i = 1; i < n; i++) { + buf[i] = (char_u)vgetorpeek(true); if (buf[i] == K_SPECIAL ) { /* Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, @@ -1560,7 +1566,7 @@ int char_avail(void) { int retval; - ++no_mapping; + no_mapping++; retval = vpeekc(); --no_mapping; return retval != NUL; @@ -1709,7 +1715,7 @@ static int vgetorpeek(int advance) if (advance) { /* Also record this character, it might be needed to * get out of Insert mode. */ - *typebuf.tb_buf = c; + *typebuf.tb_buf = (char_u)c; gotchars(typebuf.tb_buf, 1); } cmd_silent = FALSE; @@ -1875,19 +1881,19 @@ static int vgetorpeek(int advance) match = typebuf_match_len(p_pt, &mlen); } if (match) { - /* write chars to script file(s) */ - if (mlen > typebuf.tb_maplen) - gotchars(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_maplen, - mlen - typebuf.tb_maplen); + // write chars to script file(s) + if (mlen > typebuf.tb_maplen) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(mlen - typebuf.tb_maplen)); + } del_typebuf(mlen, 0); /* remove the chars */ set_option_value((char_u *)"paste", (long)!p_paste, NULL, 0); if (!(State & INSERT)) { msg_col = 0; - msg_row = Rows - 1; - msg_clr_eos(); /* clear ruler */ + msg_row = (int)Rows - 1; + msg_clr_eos(); // clear ruler } status_redraw_all(); redraw_statuslines(); @@ -1973,11 +1979,11 @@ static int vgetorpeek(int advance) char_u *save_m_keys; char_u *save_m_str; - /* write chars to script file(s) */ - if (keylen > typebuf.tb_maplen) - gotchars(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_maplen, - keylen - typebuf.tb_maplen); + // write chars to script file(s) + if (keylen > typebuf.tb_maplen) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(keylen - typebuf.tb_maplen)); + } cmd_silent = (typebuf.tb_silent > 0); del_typebuf(keylen, 0); /* remove the mapped keys */ @@ -2415,7 +2421,7 @@ inchar ( else return -1; } else { - buf[0] = script_char; + buf[0] = (char_u)script_char; len = 1; } } @@ -2451,7 +2457,7 @@ inchar ( * Fill up to a third of the buffer, because each character may be * tripled below. */ - len = os_inchar(buf, maxlen / 3, wait_time, tb_change_cnt); + len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt); } if (typebuf_changed(tb_change_cnt)) @@ -2494,8 +2500,8 @@ fix_input_buffer ( && !script && (i < 2 || p[1] != KS_EXTRA))) { memmove(p + 3, p + 1, (size_t)i); - p[2] = K_THIRD(p[0]); - p[1] = K_SECOND(p[0]); + p[2] = (char_u)K_THIRD(p[0]); + p[1] = (char_u)K_SECOND(p[0]); p[0] = K_SPECIAL; p += 2; len += 2; @@ -2571,11 +2577,11 @@ do_map ( int new_hash; mapblock_T **abbr_table; mapblock_T **map_table; - int unique = FALSE; - int nowait = FALSE; - int silent = FALSE; - int special = FALSE; - int expr = FALSE; + bool unique = false; + bool nowait = false; + bool silent = false; + bool special = false; + bool expr = false; int noremap; char_u *orig_rhs; @@ -2607,7 +2613,7 @@ do_map ( */ if (STRNCMP(keys, "<nowait>", 8) == 0) { keys = skipwhite(keys + 8); - nowait = TRUE; + nowait = true; continue; } @@ -2616,7 +2622,7 @@ do_map ( */ if (STRNCMP(keys, "<silent>", 8) == 0) { keys = skipwhite(keys + 8); - silent = TRUE; + silent = true; continue; } @@ -2625,7 +2631,7 @@ do_map ( */ if (STRNCMP(keys, "<special>", 9) == 0) { keys = skipwhite(keys + 9); - special = TRUE; + special = true; continue; } @@ -2643,7 +2649,7 @@ do_map ( */ if (STRNCMP(keys, "<expr>", 6) == 0) { keys = skipwhite(keys + 6); - expr = TRUE; + expr = true; continue; } /* @@ -2651,7 +2657,7 @@ do_map ( */ if (STRNCMP(keys, "<unique>", 8) == 0) { keys = skipwhite(keys + 8); - unique = TRUE; + unique = true; continue; } break; @@ -2667,13 +2673,14 @@ do_map ( p = keys; do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); while (*p && (maptype == 1 || !ascii_iswhite(*p))) { - if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && - p[1] != NUL) - ++p; /* skip CTRL-V or backslash */ - ++p; + if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && p[1] != NUL) { + p++; // skip CTRL-V or backslash + } + p++; } - if (*p != NUL) + if (*p != NUL) { *p++ = NUL; + } p = skipwhite(p); rhs = p; @@ -2686,22 +2693,24 @@ do_map ( goto theend; } - /* - * If mapping has been given as ^V<C_UP> say, then replace the term codes - * with the appropriate two bytes. If it is a shifted special key, unshift - * it too, giving another two bytes. - * replace_termcodes() may move the result to allocated memory, which - * needs to be freed later (*keys_buf and *arg_buf). - * replace_termcodes() also removes CTRL-Vs and sometimes backslashes. - */ - if (haskey) - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special); + // If mapping has been given as ^V<C_UP> say, then replace the term codes + // with the appropriate two bytes. If it is a shifted special key, unshift + // it too, giving another two bytes. + // replace_termcodes() may move the result to allocated memory, which + // needs to be freed later (*keys_buf and *arg_buf). + // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. + if (haskey) { + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, special, + CPO_TO_CPO_FLAGS); + } orig_rhs = rhs; if (hasarg) { - if (STRICMP(rhs, "<nop>") == 0) /* "<Nop>" means nothing */ + if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing rhs = (char_u *)""; - else - rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special); + } else { + rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special, + CPO_TO_CPO_FLAGS); + } } /* @@ -2913,9 +2922,9 @@ do_map ( did_it = TRUE; } } - if (mp->m_mode == 0) { /* entry can be deleted */ - map_free(mpp); - continue; /* continue with *mpp */ + if (mp->m_mode == 0) { // entry can be deleted + mapblock_free(mpp); + continue; // continue with *mpp } /* @@ -2940,8 +2949,12 @@ do_map ( if (!did_it) { retval = 2; /* no match */ } else if (*keys == Ctrl_C) { - /* If CTRL-C has been unmapped, reuse it for Interrupting. */ - mapped_ctrl_c = FALSE; + // If CTRL-C has been unmapped, reuse it for Interrupting. + if (map_table == curbuf->b_maphash) { + curbuf->b_mapped_ctrl_c &= ~mode; + } else { + mapped_ctrl_c &= ~mode; + } } goto theend; } @@ -2966,9 +2979,14 @@ do_map ( */ mp = xmalloc(sizeof(mapblock_T)); - /* If CTRL-C has been mapped, don't always use it for Interrupting. */ - if (*keys == Ctrl_C) - mapped_ctrl_c = TRUE; + // If CTRL-C has been mapped, don't always use it for Interrupting. + if (*keys == Ctrl_C) { + if (map_table == curbuf->b_maphash) { + curbuf->b_mapped_ctrl_c |= mode; + } else { + mapped_ctrl_c |= mode; + } + } mp->m_keys = vim_strsave(keys); mp->m_str = vim_strsave(rhs); @@ -3001,7 +3019,7 @@ theend: * Delete one entry from the abbrlist or maphash[]. * "mpp" is a pointer to the m_next field of the PREVIOUS entry! */ -static void map_free(mapblock_T **mpp) +static void mapblock_free(mapblock_T **mpp) { mapblock_T *mp; @@ -3069,7 +3087,7 @@ int get_map_mode(char_u **cmdp, int forceit) * Clear all mappings or abbreviations. * 'abbr' should be FALSE for mappings, TRUE for abbreviations. */ -void map_clear(char_u *cmdp, char_u *arg, int forceit, int abbr) +void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr) { int mode; int local; @@ -3121,8 +3139,8 @@ map_clear_int ( mp = *mpp; if (mp->m_mode & mode) { mp->m_mode &= ~mode; - if (mp->m_mode == 0) { /* entry can be deleted */ - map_free(mpp); + if (mp->m_mode == 0) { // entry can be deleted + mapblock_free(mpp); continue; } /* @@ -3259,7 +3277,8 @@ int map_to_exists(char_u *str, char_u *modechars, int abbr) char_u *buf; int retval; - rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE); + rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false, + CPO_TO_CPO_FLAGS); if (vim_strchr(modechars, 'n') != NULL) mode |= NORMAL; @@ -3454,7 +3473,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) mp = maphash[hash]; for (; mp; mp = mp->m_next) { if (mp->m_mode & expand_mapmodes) { - p = translate_mapping(mp->m_keys, TRUE); + p = translate_mapping(mp->m_keys, true, CPO_TO_CPO_FLAGS); if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { if (round == 1) ++count; @@ -3472,7 +3491,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) break; /* for (round) */ if (round == 1) { - *file = (char_u **)xmalloc(count * sizeof(char_u *)); + *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *)); } } /* for (round) */ @@ -3636,8 +3655,8 @@ int check_abbr(int c, char_u *ptr, int col, int mincol) /* special key code, split up */ if (IS_SPECIAL(c) || c == K_SPECIAL) { tb[j++] = K_SPECIAL; - tb[j++] = K_SECOND(c); - tb[j++] = K_THIRD(c); + tb[j++] = (char_u)K_SECOND(c); + tb[j++] = (char_u)K_THIRD(c); } else { if (c < ABBR_OFF && (c < ' ' || c > '~')) tb[j++] = Ctrl_V; /* special char needs CTRL-V */ @@ -3646,8 +3665,9 @@ int check_abbr(int c, char_u *ptr, int col, int mincol) if (c >= ABBR_OFF) c -= ABBR_OFF; j += (*mb_char2bytes)(c, tb + j); - } else - tb[j++] = c; + } else { + tb[j++] = (char_u)c; + } } tb[j] = NUL; /* insert the last typed char */ @@ -4179,14 +4199,15 @@ void add_map(char_u *map, int mode) // Returns NULL when there is a problem. static char_u * translate_mapping ( char_u *str, - int expmap // TRUE when expanding mappings on command-line + int expmap, // True when expanding mappings on command-line + int cpo_flags // Value of various flags present in &cpo ) { garray_T ga; ga_init(&ga, 1, 40); - int cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL); - int cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL); + bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH); + bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI); for (; *str; ++str) { int c = *str; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 52eebebf41..49d1de21d9 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -2,19 +2,9 @@ #define NVIM_GLOBALS_H #include <stdbool.h> +#include <inttypes.h> -// EXTERN is only defined in main.c. That's where global variables are -// actually defined and initialized. -#ifndef EXTERN -# define EXTERN extern -# define INIT(x) -#else -# ifndef INIT -# define INIT(x) x -# define DO_INIT -# endif -#endif - +#include "nvim/macros.h" #include "nvim/ex_eval.h" #include "nvim/iconv.h" #include "nvim/mbyte.h" @@ -100,6 +90,12 @@ # define VIMRC_FILE ".nvimrc" #endif +typedef enum { + kNone = -1, + kFalse = 0, + kTrue = 1, +} TriState; + /* Values for "starting" */ #define NO_SCREEN 2 /* no screen updating yet */ #define NO_BUFFERS 1 /* not all buffers loaded yet */ @@ -116,12 +112,9 @@ #define DFLT_COLS 80 /* default value for 'columns' */ #define DFLT_ROWS 24 /* default value for 'lines' */ -EXTERN long Rows /* nr of rows in the screen */ -#ifdef DO_INIT - = DFLT_ROWS -#endif -; -EXTERN long Columns INIT(= DFLT_COLS); /* nr of columns in the screen */ +EXTERN long Rows INIT(= DFLT_ROWS); // nr of rows in the screen + +EXTERN long Columns INIT(= DFLT_COLS); // nr of columns in the screen /* * The characters and attributes cached for the screen. @@ -162,15 +155,6 @@ EXTERN int Screen_mco INIT(= 0); /* value of p_mco used when * These are single-width. */ EXTERN schar_T *ScreenLines2 INIT(= NULL); -/* - * Indexes for tab page line: - * N > 0 for label of tab page N - * N == 0 for no label - * N < 0 for closing tab page -N - * N == -999 for closing current tab page - */ -EXTERN short *TabPageIdxs INIT(= NULL); - EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */ EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */ @@ -220,6 +204,10 @@ EXTERN int compl_length INIT(= 0); * stop looking for matches. */ EXTERN int compl_interrupted INIT(= FALSE); +// Set when doing something for completion that may call edit() recursively, +// which is not allowed. Also used to disable folding during completion +EXTERN int compl_busy INIT(= false); + /* List of flags for method of completion. */ EXTERN int compl_cont_status INIT(= 0); # define CONT_ADDING 1 /* "normal" or "adding" expansion */ @@ -750,11 +738,8 @@ EXTERN int can_si INIT(= FALSE); */ EXTERN int can_si_back INIT(= FALSE); -EXTERN pos_T saved_cursor /* w_cursor before formatting text. */ -#ifdef DO_INIT - = INIT_POS_T(0, 0, 0) -#endif -; +// w_cursor before formatting text. +EXTERN pos_T saved_cursor INIT(= INIT_POS_T(0, 0, 0)); /* * Stuff for insert mode. @@ -797,6 +782,8 @@ EXTERN bool enc_utf8 INIT(= false); /* UTF-8 encoded Unicode */ EXTERN int enc_latin1like INIT(= TRUE); /* 'encoding' is latin1 comp. */ EXTERN int has_mbyte INIT(= 0); /* any multi-byte encoding */ +/// Encoding used when 'fencs' is set to "default" +EXTERN char_u *fenc_default INIT(= NULL); /* * To speed up BYTELEN() we fill a table with the byte lengths whenever @@ -884,7 +871,7 @@ EXTERN int ctrl_x_mode INIT(= 0); /* Which Ctrl-X mode are we in? */ EXTERN int no_abbr INIT(= TRUE); /* TRUE when no abbreviations loaded */ -EXTERN int mapped_ctrl_c INIT(= FALSE); /* CTRL-C is mapped */ +EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped. EXTERN cmdmod_T cmdmod; /* Ex command modifiers */ @@ -914,22 +901,19 @@ EXTERN int RedrawingDisabled INIT(= 0); EXTERN int readonlymode INIT(= FALSE); /* Set to TRUE for "view" */ EXTERN int recoverymode INIT(= FALSE); /* Set to TRUE for "-r" option */ -EXTERN typebuf_T typebuf /* typeahead buffer */ -#ifdef DO_INIT - = {NULL, NULL, 0, 0, 0, 0, 0, 0, 0} -#endif -; -EXTERN int ex_normal_busy INIT(= 0); /* recursiveness of ex_normal() */ -EXTERN int ex_normal_lock INIT(= 0); /* forbid use of ex_normal() */ -EXTERN int ignore_script INIT(= FALSE); /* ignore script input */ -EXTERN int stop_insert_mode; /* for ":stopinsert" and 'insertmode' */ +// typeahead buffer +EXTERN typebuf_T typebuf INIT(= { NULL, NULL, 0, 0, 0, 0, 0, 0, 0 }); -EXTERN int KeyTyped; /* TRUE if user typed current char */ -EXTERN int KeyStuffed; /* TRUE if current char from stuffbuf */ -EXTERN int maptick INIT(= 0); /* tick for each non-mapped char */ +EXTERN int ex_normal_busy INIT(= 0); // recursiveness of ex_normal() +EXTERN int ex_normal_lock INIT(= 0); // forbid use of ex_normal() +EXTERN int ignore_script INIT(= false); // ignore script input +EXTERN int stop_insert_mode; // for ":stopinsert" and 'insertmode' +EXTERN int KeyTyped; // TRUE if user typed current char +EXTERN int KeyStuffed; // TRUE if current char from stuffbuf +EXTERN int maptick INIT(= 0); // tick for each non-mapped char -EXTERN char_u chartab[256]; /* table used in charset.c; See - init_chartab() for explanation */ +EXTERN uint8_t chartab[256]; // table used in charset.c; See + // init_chartab() for explanation EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */ EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */ @@ -956,30 +940,28 @@ EXTERN reg_extmatch_T *re_extmatch_in INIT(= NULL); /* Used by vim_regexec(): EXTERN reg_extmatch_T *re_extmatch_out INIT(= NULL); /* Set by vim_regexec() * to store \z\(...\) matches */ -EXTERN int did_outofmem_msg INIT(= FALSE); -/* set after out of memory msg */ -EXTERN int did_swapwrite_msg INIT(= FALSE); -/* set after swap write error msg */ -EXTERN int undo_off INIT(= FALSE); /* undo switched off for now */ -EXTERN int global_busy INIT(= 0); /* set when :global is executing */ -EXTERN int listcmd_busy INIT(= FALSE); /* set when :argdo, :windo or - :bufdo is executing */ -EXTERN int need_start_insertmode INIT(= FALSE); -/* start insert mode soon */ -EXTERN char_u *last_cmdline INIT(= NULL); /* last command line (for ":) */ -EXTERN char_u *repeat_cmdline INIT(= NULL); /* command line for "." */ -EXTERN char_u *new_last_cmdline INIT(= NULL); /* new value for last_cmdline */ -EXTERN char_u *autocmd_fname INIT(= NULL); /* fname for <afile> on cmdline */ -EXTERN int autocmd_fname_full; /* autocmd_fname is full path */ -EXTERN int autocmd_bufnr INIT(= 0); /* fnum for <abuf> on cmdline */ -EXTERN char_u *autocmd_match INIT(= NULL); /* name for <amatch> on cmdline */ -EXTERN int did_cursorhold INIT(= FALSE); /* set when CursorHold t'gerd */ -EXTERN pos_T last_cursormoved /* for CursorMoved event */ -# ifdef DO_INIT - = INIT_POS_T(0, 0, 0) -# endif -; -EXTERN int last_changedtick INIT(= 0); /* for TextChanged event */ +EXTERN int did_outofmem_msg INIT(= false); +// set after out of memory msg +EXTERN int did_swapwrite_msg INIT(= false); +// set after swap write error msg +EXTERN int undo_off INIT(= false); // undo switched off for now +EXTERN int global_busy INIT(= 0); // set when :global is executing +EXTERN int listcmd_busy INIT(= false); // set when :argdo, :windo or + // :bufdo is executing +EXTERN int need_start_insertmode INIT(= false); +// start insert mode soon +EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":) +EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "." +EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline +EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline +EXTERN int autocmd_fname_full; // autocmd_fname is full path +EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline +EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline +EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd +// for CursorMoved event +EXTERN pos_T last_cursormoved INIT(= INIT_POS_T(0, 0, 0)); + +EXTERN int last_changedtick INIT(= 0); // for TextChanged event EXTERN buf_T *last_changedtick_buf INIT(= NULL); EXTERN int postponed_split INIT(= 0); /* for CTRL-W CTRL-] command */ @@ -1089,13 +1071,9 @@ EXTERN int typebuf_was_filled INIT(= FALSE); /* received text from client #ifdef BACKSLASH_IN_FILENAME -EXTERN char psepc INIT(= '\\'); /* normal path separator character */ -EXTERN char psepcN INIT(= '/'); /* abnormal path separator character */ -EXTERN char pseps[2] /* normal path separator string */ -# ifdef DO_INIT - = {'\\', 0} -# endif -; +EXTERN char psepc INIT(= '\\'); // normal path separator character +EXTERN char psepcN INIT(= '/'); // abnormal path separator character +EXTERN char pseps[2] INIT(= { '\\', 0 }); // normal path separator string #endif /* Set to TRUE when an operator is being executed with virtual editing, MAYBE @@ -1114,12 +1092,8 @@ EXTERN int need_cursor_line_redraw INIT(= FALSE); #ifdef USE_MCH_ERRMSG -/* Grow array to collect error messages in until they can be displayed. */ -EXTERN garray_T error_ga -# ifdef DO_INIT - = GA_EMPTY_INIT_VALUE -# endif -; +// Grow array to collect error messages in until they can be displayed. +EXTERN garray_T error_ga INIT(= GA_EMPTY_INIT_VALUE); #endif @@ -1239,6 +1213,7 @@ EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s")); EXTERN char_u e_maxmempat[] INIT(= N_( "E363: pattern uses more memory than 'maxmempattern'")); EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer")); +EXTERN char_u e_nobufnr[] INIT(= N_("E86: Buffer %" PRId64 " does not exist")); EXTERN char_u e_invalpat[] INIT(= N_( "E682: Invalid search pattern or delimiter")); diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index ab8959239b..916d27a964 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -32,7 +32,6 @@ #include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/version.h" -#include "nvim/tempfile.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -2191,18 +2190,19 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen); } - /* Check if need to use Courier for ASCII code range, and if so pick up - * the encoding to use */ - prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present && - (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) - == 'y'); + // Check if need to use Courier for ASCII code range, and if so pick up + // the encoding to use + prt_use_courier = ( + mbfont_opts[OPT_MBFONT_USECOURIER].present + && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y')); if (prt_use_courier) { - /* Use national ASCII variant unless ASCII wanted */ - if (mbfont_opts[OPT_MBFONT_ASCII].present && - (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y')) + // Use national ASCII variant unless ASCII wanted + if (mbfont_opts[OPT_MBFONT_ASCII].present + && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y')) { prt_ascii_encoding = "ascii"; - else + } else { prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc; + } } prt_ps_font = &prt_ps_mb_font; @@ -2780,11 +2780,13 @@ void mch_print_end(prt_settings_T *psettings) } prt_message((char_u *)_("Sending to printer...")); - /* Not printing to a file: use 'printexpr' to print the file. */ - if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL) + // Not printing to a file: use 'printexpr' to print the file. + if (eval_printexpr((char *) prt_ps_file_name, (char *) psettings->arguments) + == FAIL) { EMSG(_("E365: Failed to print PostScript file")); - else + } else { prt_message((char_u *)_("Print job sent.")); + } } mch_print_cleanup(); @@ -3028,10 +3030,10 @@ int mch_print_text_out(char_u *p, size_t len) prt_text_run += char_width; prt_pos_x += char_width; - /* The downside of fp - use relative error on right margin check */ + // The downside of fp - use relative error on right margin check next_pos = prt_pos_x + prt_char_width; - need_break = (next_pos > prt_right_margin) && - ((next_pos - prt_right_margin) > (prt_right_margin*1e-5)); + need_break = ((next_pos > prt_right_margin) + && ((next_pos - prt_right_margin) > (prt_right_margin * 1e-5))); if (need_break) prt_flush_buffer(); diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 3585c26ca2..2f9ec0b3ff 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -11,6 +11,7 @@ #include <assert.h> #include <errno.h> #include <inttypes.h> +#include <fcntl.h> #include "nvim/vim.h" #include "nvim/ascii.h" @@ -26,7 +27,6 @@ #include "nvim/quickfix.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -743,8 +743,16 @@ err_closing: (void)close(to_cs[1]); (void)close(from_cs[0]); #else - /* WIN32 */ - /* Create pipes to communicate with cscope */ + // Create pipes to communicate with cscope + int fd; + SECURITY_ATTRIBUTES sa; + PROCESS_INFORMATION pi; + BOOL pipe_stdin = FALSE, pipe_stdout = FALSE; // NOLINT(readability/bool) + STARTUPINFO si; + HANDLE stdin_rd, stdout_rd; + HANDLE stdout_wr, stdin_wr; + BOOL created; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; @@ -861,17 +869,16 @@ err_closing: csinfo[i].hProc = pi.hProcess; CloseHandle(pi.hThread); - /* TODO - tidy up after failure to create files on pipe handles. */ - if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdin_wr, - _O_TEXT|_O_APPEND)) < 0) - || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL)) + // TODO(neovim): tidy up after failure to create files on pipe handles. + if (((fd = _open_osfhandle((intptr_t)stdin_wr, _O_TEXT|_O_APPEND)) < 0) + || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL)) { PERROR(_("cs_create_connection: fdopen for to_fp failed")); - if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdout_rd, - _O_TEXT|_O_RDONLY)) < 0) - || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL)) + } + if (((fd = _open_osfhandle((intptr_t)stdout_rd, _O_TEXT|_O_RDONLY)) < 0) + || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL)) { PERROR(_("cs_create_connection: fdopen for fr_fp failed")); - - /* Close handles for file descriptors inherited by the cscope process */ + } + // Close handles for file descriptors inherited by the cscope process. CloseHandle(stdin_rd); CloseHandle(stdout_wr); @@ -1055,8 +1062,8 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m", *qfpos == '-', cmdline) > 0) { if (postponed_split != 0) { - win_split(postponed_split > 0 ? postponed_split : 0, - postponed_split_flags); + (void)win_split(postponed_split > 0 ? postponed_split : 0, + postponed_split_flags); RESET_BINDING(curwin); postponed_split = 0; } @@ -1146,22 +1153,6 @@ static void clear_csinfo(size_t i) csinfo[i].to_fp = NULL; } -#ifndef UNIX -static char *GetWin32Error(void) -{ - char *msg = NULL; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, - NULL, GetLastError(), 0, (LPSTR)&msg, 0, NULL); - if (msg != NULL) { - /* remove trailing \r\n */ - char *pcrlf = strstr(msg, "\r\n"); - if (pcrlf != NULL) - *pcrlf = '\0'; - } - return msg; -} -#endif - /* * PRIVATE: cs_insert_filelist * @@ -1640,77 +1631,79 @@ static char *cs_pathcomponents(char *path) return s; } -/* - * PRIVATE: cs_print_tags_priv - * - * called from cs_manage_matches() - */ +/// Print cscope output that was converted into ctags style entries. +/// +/// Only called from cs_manage_matches(). +/// +/// @param matches Array of cscope lines in ctags style. Every entry was +// produced with a format string of the form +// "%s\t%s\t%s;\"\t%s" or +// "%s\t%s\t%s;\"" +// by cs_make_vim_style_matches(). +/// @param cntxts Context for matches. +/// @param num_matches Number of entries in matches/cntxts, always greater 0. static void cs_print_tags_priv(char **matches, char **cntxts, - size_t num_matches) + size_t num_matches) FUNC_ATTR_NONNULL_ALL { - char *ptag; - char *fname, *lno, *extra, *tbuf; - size_t num; - char *globalcntx = "GLOBAL"; - char *context; - char *cstag_msg = _("Cscope tag: %s"); + char *globalcntx = "GLOBAL"; + char *cstag_msg = _("Cscope tag: %s"); - assert (num_matches > 0); + assert(num_matches > 0); + assert(strcnt(matches[0], '\t') >= 2); - tbuf = xmalloc(strlen(matches[0]) + 1); + char *ptag = matches[0]; + char *ptag_end = strchr(ptag, '\t'); + assert(ptag_end >= ptag); + // NUL terminate tag string in matches[0]. + *ptag_end = NUL; - strcpy(tbuf, matches[0]); - ptag = strtok(tbuf, "\t"); - - size_t newsize = strlen(cstag_msg) + strlen(ptag); + // The "%s" in cstag_msg won't appear in the result string, so we don't need + // extra memory for terminating NUL. + size_t newsize = strlen(cstag_msg) + (size_t)(ptag_end - ptag); char *buf = xmalloc(newsize); size_t bufsize = newsize; // Track available bufsize - (void)sprintf(buf, cstag_msg, ptag); + (void)snprintf(buf, bufsize, cstag_msg, ptag); MSG_PUTS_ATTR(buf, hl_attr(HLF_T)); + msg_clr_eos(); - xfree(tbuf); + // restore matches[0] + *ptag_end = '\t'; - MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); /* strlen is 7 */ + // Column headers for match number, line number and filename. + MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); msg_advance(msg_col + 2); MSG_PUTS_ATTR(_("filename / context / line\n"), hl_attr(HLF_T)); - num = 1; for (size_t i = 0; i < num_matches; i++) { - size_t idx = i; - - /* if we really wanted to, we could avoid this malloc and strcpy - * by parsing matches[i] on the fly and placing stuff into buf - * directly, but that's too much of a hassle - */ - tbuf = xmalloc(strlen(matches[idx]) + 1); - (void)strcpy(tbuf, matches[idx]); - - if (strtok(tbuf, (const char *)"\t") == NULL) - continue; - if ((fname = strtok(NULL, (const char *)"\t")) == NULL) - continue; - if ((lno = strtok(NULL, (const char *)"\t")) == NULL) - continue; - extra = strtok(NULL, (const char *)"\t"); - - lno[strlen(lno)-2] = '\0'; /* ignore ;" at the end */ + assert(strcnt(matches[i], '\t') >= 2); + + // Parse filename, line number and optional part. + char *fname = strchr(matches[i], '\t') + 1; + char *fname_end = strchr(fname, '\t'); + // Replace second '\t' in matches[i] with NUL to terminate fname. + *fname_end = NUL; + + char *lno = fname_end + 1; + char *extra = xstrchrnul(lno, '\t'); + // Ignore ;" at the end of lno. + char *lno_end = extra - 2; + *lno_end = NUL; + // Do we have an optional part? + extra = *extra ? extra + 1 : NULL; const char *csfmt_str = "%4zu %6s "; - /* hopefully 'num' (num of matches) will be less than 10^16 */ - newsize = strlen(csfmt_str) + 16 + strlen(lno); + // hopefully num_matches will be less than 10^16 + newsize = strlen(csfmt_str) + 16 + (size_t)(lno_end - lno); if (bufsize < newsize) { buf = xrealloc(buf, newsize); bufsize = newsize; } - (void)sprintf(buf, csfmt_str, num, lno); + (void)snprintf(buf, bufsize, csfmt_str, i + 1, lno); MSG_PUTS_ATTR(buf, hl_attr(HLF_CM)); MSG_PUTS_LONG_ATTR(cs_pathcomponents(fname), hl_attr(HLF_CM)); - /* compute the required space for the context */ - if (cntxts[idx] != NULL) - context = cntxts[idx]; - else - context = globalcntx; + // compute the required space for the context + char *context = cntxts[i] ? cntxts[i] : globalcntx; const char *cntxformat = " <<%s>>"; // '%s' won't appear in result string, so: @@ -1721,11 +1714,13 @@ static void cs_print_tags_priv(char **matches, char **cntxts, buf = xrealloc(buf, newsize); bufsize = newsize; } - (void)sprintf(buf, cntxformat, context); + int buf_len = snprintf(buf, bufsize, cntxformat, context); + assert(buf_len >= 0); - /* print the context only if it fits on the same line */ - if (msg_col + (int)strlen(buf) >= (int)Columns) + // Print the context only if it fits on the same line. + if (msg_col + buf_len >= (int)Columns) { msg_putchar('\n'); + } msg_advance(12); MSG_PUTS_LONG(buf); msg_putchar('\n'); @@ -1734,23 +1729,23 @@ static void cs_print_tags_priv(char **matches, char **cntxts, MSG_PUTS_LONG(extra); } - xfree(tbuf); /* only after printing extra due to strtok use */ + // restore matches[i] + *fname_end = '\t'; + *lno_end = ';'; - if (msg_col) + if (msg_col) { msg_putchar('\n'); + } os_breakcheck(); if (got_int) { - got_int = FALSE; /* don't print any more matches */ + got_int = false; // don't print any more matches break; } - - num++; - } /* for all matches */ + } xfree(buf); -} /* cs_print_tags_priv */ - +} /* * PRIVATE: cs_read_prompt @@ -1850,9 +1845,7 @@ static void sig_handler(int s) { */ static void cs_release_csp(size_t i, int freefnpp) { - /* - * Trying to exit normally (not sure whether it is fit to UNIX cscope - */ + // Trying to exit normally (not sure whether it is fit to Unix cscope) if (csinfo[i].to_fp != NULL) { (void)fputs("q\n", csinfo[i].to_fp); (void)fflush(csinfo[i].to_fp); @@ -2087,12 +2080,13 @@ static int cs_show(exarg_T *eap) if (csinfo[i].fname == NULL) continue; - if (csinfo[i].ppath != NULL) - (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", - i, (long)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath); - else - (void)smsg("%2zu %-5" PRId64 " %-34s <none>", - i, (long)csinfo[i].pid, csinfo[i].fname); + if (csinfo[i].ppath != NULL) { + (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", i, + (int64_t)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath); + } else { + (void)smsg("%2zu %-5" PRId64 " %-34s <none>", i, + (int64_t)csinfo[i].pid, csinfo[i].fname); + } } } diff --git a/src/nvim/if_cscope_defs.h b/src/nvim/if_cscope_defs.h index a0c84e8ba1..8cd74c74e6 100644 --- a/src/nvim/if_cscope_defs.h +++ b/src/nvim/if_cscope_defs.h @@ -16,6 +16,7 @@ # include <sys/types.h> /* pid_t */ #endif +#include "nvim/os/os_defs.h" #include "nvim/os/fs_defs.h" #define CSCOPE_SUCCESS 0 @@ -50,7 +51,13 @@ typedef struct csi { char * ppath; /* path to prepend (the -P option) */ char * flags; /* additional cscope flags/options (e.g, -p2) */ #if defined(UNIX) - pid_t pid; /* PID of the connected cscope process. */ + pid_t pid; // PID of the connected cscope process +#else + DWORD pid; // PID of the connected cscope process + HANDLE hProc; // cscope process handle + DWORD nVolume; // Volume serial number, instead of st_dev + DWORD nIndexHigh; // st_ino has no meaning on Windows + DWORD nIndexLow; #endif FileID file_id; diff --git a/src/nvim/indent.c b/src/nvim/indent.c index d3008185dc..f197669a97 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -529,7 +529,7 @@ int get_expr_indent(void) save_pos = curwin->w_cursor; save_curswant = curwin->w_curswant; save_set_curswant = curwin->w_set_curswant; - set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum); + set_vim_var_nr(VV_LNUM, (varnumber_T) curwin->w_cursor.lnum); if (use_sandbox) { sandbox++; diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index d62e7aad03..efe8e73a3c 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -17,7 +17,11 @@ #include "nvim/search.h" #include "nvim/strings.h" - +// Find result cache for cpp_baseclass +typedef struct { + int found; + lpos_T lpos; +} cpp_baseclass_cache_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "indent_c.c.generated.h" @@ -25,6 +29,7 @@ /* * Find the start of a comment, not knowing if we are in a comment right now. * Search starts at w_cursor.lnum and goes backwards. + * Return NULL when not inside a comment. */ static pos_T *ind_find_start_comment(void) { /* XXX */ @@ -64,6 +69,72 @@ find_start_comment ( /* XXX */ return pos; } +/// Find the start of a comment or raw string, not knowing if we are in a +/// comment or raw string right now. +/// Search starts at w_cursor.lnum and goes backwards. +/// +/// @returns NULL when not inside a comment or raw string. +/// +/// @note "CORS" -> Comment Or Raw String +static pos_T *ind_find_start_CORS(void) +{ + // XXX + static pos_T comment_pos_copy; + + pos_T *comment_pos = find_start_comment(curbuf->b_ind_maxcomment); + if (comment_pos != NULL) { + // Need to make a copy of the static pos in findmatchlimit(), + // calling find_start_rawstring() may change it. + comment_pos_copy = *comment_pos; + comment_pos = &comment_pos_copy; + } + pos_T *rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment); + + // If comment_pos is before rs_pos the raw string is inside the comment. + // If rs_pos is before comment_pos the comment is inside the raw string. + if (comment_pos == NULL || (rs_pos != NULL && lt(*rs_pos, *comment_pos))) { + return rs_pos; + } + return comment_pos; +} + +/* + * Find the start of a raw string, not knowing if we are in one right now. + * Search starts at w_cursor.lnum and goes backwards. + * Return NULL when not inside a raw string. + */ +static pos_T *find_start_rawstring(int ind_maxcomment) +{ /* XXX */ + pos_T *pos; + char_u *line; + char_u *p; + long cur_maxcomment = ind_maxcomment; + + for (;;) + { + pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment); + if (pos == NULL) + break; + + /* + * Check if the raw string start we found is inside a string. + * If it is then restrict the search to below this line and try again. + */ + line = ml_get(pos->lnum); + for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) + p = skip_string(p); + if ((colnr_T)(p - line) <= pos->col) + break; + cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; + if (cur_maxcomment <= 0) + { + pos = NULL; + break; + } + } + return pos; +} + /* * Skip to the end of a "string" and a 'c' character. * If there is no string or character, return argument unmodified. @@ -97,7 +168,26 @@ static char_u *skip_string(char_u *p) break; } if (p[0] == '"') - continue; + continue; /* continue for another string */ + } else if (p[0] == 'R' && p[1] == '"') { + /* Raw string: R"[delim](...)[delim]" */ + char_u *delim = p + 2; + char_u *paren = vim_strchr(delim, '('); + + if (paren != NULL) + { + long delim_len = paren - delim; + + for (p += 3; *p; ++p) + if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 + && p[delim_len + 1] == '"') + { + p += delim_len + 1; + break; + } + if (p[0] == '"') + continue; /* continue for another string */ + } } break; /* no string found */ } @@ -288,10 +378,11 @@ int cin_islabel(void) --curwin->w_cursor.lnum; /* - * If we're in a comment now, skip to the start of the comment. + * If we're in a comment or raw string now, skip to the start of + * it. */ curwin->w_cursor.col = 0; - if ((trypos = ind_find_start_comment()) != NULL) /* XXX */ + if ((trypos = ind_find_start_CORS()) != NULL) /* XXX */ curwin->w_cursor = *trypos; line = get_cursor_line_ptr(); @@ -766,13 +857,27 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum) return FALSE; while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"') { - if (cin_iscomment(s)) /* ignore comments */ + // ignore comments + if (cin_iscomment(s)) { s = cin_skipcomment(s); - else - ++s; + } else if (*s == ':') { + if (*(s + 1) == ':') { + s += 2; + } else { + // To avoid a mistake in the following situation: + // A::A(int a, int b) + // : a(0) // <--not a function decl + // , b(0) + // {... + return false; + } + } else { + s++; + } + } + if (*s != '(') { + return false; // ';', ' or " before any () or no '(' } - if (*s != '(') - return FALSE; /* ';', ' or " before any () or no '(' */ while (*s && *s != ';' && *s != '\'' && *s != '"') { if (*s == ')' && cin_nocode(s + 1)) { @@ -986,17 +1091,18 @@ static int cin_isbreak(char_u *p) * * This is a lot of guessing. Watch out for "cond ? func() : foo". */ -static int -cin_is_cpp_baseclass ( - colnr_T *col /* return: column to align with */ -) -{ +static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) { + lpos_T *pos = &cached->lpos; // find position char_u *s; int class_or_struct, lookfor_ctor_init, cpp_base_class; linenr_T lnum = curwin->w_cursor.lnum; char_u *line = get_cursor_line_ptr(); - *col = 0; + if (pos->lnum <= lnum) { + return cached->found; // Use the cached result + } + + pos->col = 0; s = skipwhite(line); if (*s == '#') /* skip #define FOO x ? (x) : x */ @@ -1038,20 +1144,29 @@ cin_is_cpp_baseclass ( --lnum; } + pos->lnum = lnum; line = ml_get(lnum); - s = cin_skipcomment(line); + s = line; for (;; ) { if (*s == NUL) { - if (lnum == curwin->w_cursor.lnum) + if (lnum == curwin->w_cursor.lnum) { break; - /* Continue in the cursor line. */ + } + // Continue in the cursor line. line = ml_get(++lnum); + s = line; + } + if (s == line) { + // don't recognize "case (foo):" as a baseclass */ + if (cin_iscase(s, false)) { + break; + } s = cin_skipcomment(line); if (*s == NUL) continue; } - if (s[0] == '"') + if (s[0] == '"' || (s[0] == 'R' && s[1] == '"')) s = skip_string(s) + 1; else if (s[0] == ':') { if (s[1] == ':') { @@ -1062,9 +1177,9 @@ cin_is_cpp_baseclass ( } else if (lookfor_ctor_init || class_or_struct) { /* we have something found, that looks like the start of * cpp-base-class-declaration or constructor-initialization */ - cpp_base_class = TRUE; - lookfor_ctor_init = class_or_struct = FALSE; - *col = 0; + cpp_base_class = true; + lookfor_ctor_init = class_or_struct = false; + pos->col = 0; s = cin_skipcomment(s + 1); } else s = cin_skipcomment(s + 1); @@ -1090,25 +1205,31 @@ cin_is_cpp_baseclass ( return FALSE; } else if (!vim_isIDc(s[0])) { /* if it is not an identifier, we are wrong */ - class_or_struct = FALSE; - lookfor_ctor_init = FALSE; - } else if (*col == 0) { + class_or_struct = false; + lookfor_ctor_init = false; + } else if (pos->col == 0) { /* it can't be a constructor-initialization any more */ lookfor_ctor_init = FALSE; /* the first statement starts here: lineup with this one... */ - if (cpp_base_class) - *col = (colnr_T)(s - line); + if (cpp_base_class) { + pos->col = (colnr_T)(s - line); + } } /* When the line ends in a comma don't align with it. */ - if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) - *col = 0; + if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) { + pos->col = 0; + } s = cin_skipcomment(s + 1); } } + cached->found = cpp_base_class; + if (cpp_base_class) { + pos->lnum = lnum; + } return cpp_base_class; } @@ -1216,7 +1337,7 @@ static pos_T *find_start_brace(void) pos = NULL; /* ignore the { if it's in a // or / * * / comment */ if ((colnr_T)cin_skip2pos(trypos) == trypos->col - && (pos = ind_find_start_comment()) == NULL) /* XXX */ + && (pos = ind_find_start_CORS()) == NULL) /* XXX */ break; if (pos != NULL) curwin->w_cursor.lnum = pos->lnum; @@ -1237,18 +1358,36 @@ static pos_T * find_match_char(char_u c, int ind_maxparen) pos_T cursor_save; pos_T *trypos; static pos_T pos_copy; + int ind_maxp_wk; cursor_save = curwin->w_cursor; - if ((trypos = findmatchlimit(NULL, c, 0, ind_maxparen)) != NULL) { - /* check if the ( is in a // comment */ - if ((colnr_T)cin_skip2pos(trypos) > trypos->col) + ind_maxp_wk = ind_maxparen; +retry: + if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) { + // check if the ( is in a // comment + if ((colnr_T)cin_skip2pos(trypos) > trypos->col) { + ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum); + if (ind_maxp_wk > 0) { + curwin->w_cursor = *trypos; + curwin->w_cursor.col = 0; // XXX + goto retry; + } trypos = NULL; - else { + } else { + pos_T *trypos_wk; + pos_copy = *trypos; /* copy trypos, findmatch will change it */ trypos = &pos_copy; curwin->w_cursor = *trypos; - if (ind_find_start_comment() != NULL) + if ((trypos_wk = ind_find_start_CORS()) != NULL) { /* XXX */ + ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum + - trypos_wk->lnum); + if (ind_maxp_wk > 0) { + curwin->w_cursor = *trypos_wk; + goto retry; + } trypos = NULL; + } } } curwin->w_cursor = cursor_save; @@ -1532,6 +1671,10 @@ void parse_cino(buf_T *buf) } } +/* + * Return the desired indent for C code. + * Return -1 if the indent should be left alone (inside a raw string). + */ int get_c_indent(void) { pos_T cur_curpos; @@ -1542,8 +1685,9 @@ int get_c_indent(void) char_u *theline; char_u *linecopy; pos_T *trypos; + pos_T *comment_pos; pos_T *tryposBrace = NULL; - pos_T tryposBraceCopy; + pos_T tryposCopy; pos_T our_paren_pos; char_u *start; int start_brace; @@ -1567,7 +1711,7 @@ int get_c_indent(void) #define LOOKFOR_CPP_BASECLASS 9 #define LOOKFOR_ENUM_OR_INIT 10 #define LOOKFOR_JS_KEY 11 -#define LOOKFOR_NO_COMMA 12 +#define LOOKFOR_COMMA 12 int whilelevel; linenr_T lnum; @@ -1578,6 +1722,7 @@ int get_c_indent(void) int cont_amount = 0; /* amount for continuation line */ int original_line_islabel; int added_to_amount = 0; + cpp_baseclass_cache_T cache_cpp_baseclass = { false, { MAXLNUM, 0 } }; /* make a copy, value is changed below */ int ind_continuation = curbuf->b_ind_continuation; @@ -1585,7 +1730,7 @@ int get_c_indent(void) /* remember where the cursor was when we started */ cur_curpos = curwin->w_cursor; - /* if we are at line 1 0 is fine, right? */ + /* if we are at line 1 zero indent is fine, right? */ if (cur_curpos.lnum == 1) return 0; @@ -1615,37 +1760,55 @@ int get_c_indent(void) original_line_islabel = cin_islabel(); /* XXX */ /* + * If we are inside a raw string don't change the indent. + * Ignore a raw string inside a comment. + */ + comment_pos = ind_find_start_comment(); + if (comment_pos != NULL) { + /* findmatchlimit() static pos is overwritten, make a copy */ + tryposCopy = *comment_pos; + comment_pos = &tryposCopy; + } + trypos = find_start_rawstring(curbuf->b_ind_maxcomment); + if (trypos != NULL && (comment_pos == NULL || lt(*trypos, *comment_pos))) { + amount = -1; + goto laterend; + } + + /* * #defines and so on always go at the left when included in 'cinkeys'. */ - if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE))) + if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) { amount = curbuf->b_ind_hash_comment; + goto theend; + } /* * Is it a non-case label? Then that goes at the left margin too unless: * - JS flag is set. * - 'L' item has a positive value. */ - else if (original_line_islabel && !curbuf->b_ind_js + if (original_line_islabel && !curbuf->b_ind_js && curbuf->b_ind_jump_label < 0) { amount = 0; + goto theend; } /* * If we're inside a "//" comment and there is a "//" comment in a * previous line, lineup with that one. */ - else if (cin_islinecomment(theline) + if (cin_islinecomment(theline) && (trypos = find_line_comment()) != NULL) { /* XXX */ /* find how indented the line beginning the comment is */ getvcol(curwin, trypos, &col, NULL, NULL); amount = col; + goto theend; } /* * If we're inside a comment and not looking at the start of the * comment, try using the 'comments' option. */ - else if (!cin_iscomment(theline) - && (trypos = ind_find_start_comment()) != NULL) { - /* XXX */ + if (!cin_iscomment(theline) && comment_pos != NULL) { /* XXX */ int lead_start_len = 2; int lead_middle_len = 1; char_u lead_start[COM_MAX_LEN]; /* start-comment string */ @@ -1657,7 +1820,7 @@ int get_c_indent(void) int done = FALSE; /* find how indented the line beginning the comment is */ - getvcol(curwin, trypos, &col, NULL, NULL); + getvcol(curwin, comment_pos, &col, NULL, NULL); amount = col; *lead_start = NUL; *lead_middle = NUL; @@ -1709,12 +1872,12 @@ int get_c_indent(void) lead_middle_len) == 0) { amount = get_indent_lnum(curwin->w_cursor.lnum - 1); break; - } - /* If the start comment string doesn't match with the - * start of the comment, skip this entry. XXX */ - else if (STRNCMP(ml_get(trypos->lnum) + trypos->col, - lead_start, lead_start_len) != 0) + } else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col, + lead_start, lead_start_len) != 0) { + /* If the start comment string doesn't match with the + * start of the comment, skip this entry. XXX */ continue; + } } if (start_off != 0) amount += start_off; @@ -1758,7 +1921,7 @@ int get_c_indent(void) * otherwise, add the amount specified by "c" in 'cino' */ amount = -1; - for (lnum = cur_curpos.lnum - 1; lnum > trypos->lnum; --lnum) { + for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) { if (linewhite(lnum)) /* skip blank lines */ continue; amount = get_indent_lnum(lnum); /* XXX */ @@ -1766,28 +1929,30 @@ int get_c_indent(void) } if (amount == -1) { /* use the comment opener */ if (!curbuf->b_ind_in_comment2) { - start = ml_get(trypos->lnum); - look = start + trypos->col + 2; /* skip / and * */ + start = ml_get(comment_pos->lnum); + look = start + comment_pos->col + 2; /* skip / and * */ if (*look != NUL) /* if something after it */ - trypos->col = (colnr_T)(skipwhite(look) - start); + comment_pos->col = (colnr_T)(skipwhite(look) - start); } - getvcol(curwin, trypos, &col, NULL, NULL); + getvcol(curwin, comment_pos, &col, NULL, NULL); amount = col; if (curbuf->b_ind_in_comment2 || *look == NUL) amount += curbuf->b_ind_in_comment; } } + goto theend; } // Are we looking at a ']' that has a match? - else if (*skipwhite(theline) == ']' + if (*skipwhite(theline) == ']' && (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) { // align with the line containing the '['. amount = get_indent_lnum(trypos->lnum); + goto theend; } /* * Are we inside parentheses or braces? */ /* XXX */ - else if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL + if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL && curbuf->b_ind_java == 0) || (tryposBrace = find_start_brace()) != NULL || trypos != NULL) { @@ -1821,8 +1986,8 @@ int get_c_indent(void) continue; /* ignore #define, #if, etc. */ curwin->w_cursor.lnum = lnum; - /* Skip a comment. XXX */ - if ((trypos = ind_find_start_comment()) != NULL) { + /* Skip a comment or raw string. XXX */ + if ((trypos = ind_find_start_CORS()) != NULL) { lnum = trypos->lnum + 1; continue; } @@ -2023,8 +2188,8 @@ int get_c_indent(void) // stored in tryposBrace. // Make a copy of tryposBrace, it may point to pos_copy inside // find_start_brace(), which may be changed somewhere. - tryposBraceCopy = *tryposBrace; - tryposBrace = &tryposBraceCopy; + tryposCopy = *tryposBrace; + tryposBrace = &tryposCopy; trypos = tryposBrace; ourscope = trypos->lnum; start = ml_get(ourscope); @@ -2117,15 +2282,14 @@ int get_c_indent(void) * location for b_ind_open_extra. */ - if (start_brace == BRACE_IN_COL0) { /* '{' is in column 0 */ + if (start_brace == BRACE_IN_COL0) { // '{' is in column 0 amount = curbuf->b_ind_open_left_imag; - lookfor_cpp_namespace = TRUE; - } else if (start_brace == BRACE_AT_START && - lookfor_cpp_namespace) { /* '{' is at start */ - - lookfor_cpp_namespace = TRUE; + lookfor_cpp_namespace = true; + } else if (start_brace == BRACE_AT_START + && lookfor_cpp_namespace) { // '{' is at start + lookfor_cpp_namespace = true; } else { - if (start_brace == BRACE_AT_END) { /* '{' is at end of line */ + if (start_brace == BRACE_AT_END) { // '{' is at end of line amount += curbuf->b_ind_open_imag; l = skipwhite(get_cursor_line_ptr()); @@ -2201,10 +2365,10 @@ int get_c_indent(void) l = get_cursor_line_ptr(); /* - * If we're in a comment now, skip to the start of the - * comment. + * If we're in a comment or raw string now, skip to + * the start of it. */ - trypos = ind_find_start_comment(); + trypos = ind_find_start_CORS(); if (trypos != NULL) { curwin->w_cursor.lnum = trypos->lnum + 1; curwin->w_cursor.col = 0; @@ -2286,7 +2450,8 @@ int get_c_indent(void) amount += ind_continuation; } else { if (lookfor != LOOKFOR_TERM - && lookfor != LOOKFOR_CPP_BASECLASS) { + && lookfor != LOOKFOR_CPP_BASECLASS + && lookfor != LOOKFOR_COMMA) { amount = scope_amount; if (theline[0] == '{') { amount += curbuf->b_ind_open_extra; @@ -2309,9 +2474,9 @@ int get_c_indent(void) l = get_cursor_line_ptr(); - /* If we're in a comment now, skip to the start of - * the comment. */ - trypos = ind_find_start_comment(); + /* If we're in a comment or raw string now, skip + * to the start of it. */ + trypos = ind_find_start_CORS(); if (trypos != NULL) { curwin->w_cursor.lnum = trypos->lnum + 1; curwin->w_cursor.col = 0; @@ -2337,9 +2502,10 @@ int get_c_indent(void) } /* - * If we're in a comment now, skip to the start of the comment. + * If we're in a comment or raw string now, skip to the start + * of it. */ /* XXX */ - if ((trypos = ind_find_start_comment()) != NULL) { + if ((trypos = ind_find_start_CORS()) != NULL) { curwin->w_cursor.lnum = trypos->lnum + 1; curwin->w_cursor.col = 0; continue; @@ -2492,7 +2658,7 @@ int get_c_indent(void) */ /* XXX */ n = FALSE; if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0) { - n = cin_is_cpp_baseclass(&col); + n = cin_is_cpp_baseclass(&cache_cpp_baseclass); l = get_cursor_line_ptr(); } if (n) { @@ -2508,7 +2674,7 @@ int get_c_indent(void) continue; } else /* XXX */ - amount = get_baseclass_amount(col); + amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); break; } else if (lookfor == LOOKFOR_CPP_BASECLASS) { /* only look, whether there is a cpp base class @@ -2550,23 +2716,32 @@ int get_c_indent(void) amount = get_indent(); break; } - if (lookfor == LOOKFOR_NO_COMMA) { - if (terminated != ',') { - // Line below current line is the one that starts a - // (possibly broken) line ending in a comma. + if (lookfor == LOOKFOR_COMMA) { + if (tryposBrace != NULL && tryposBrace->lnum + >= curwin->w_cursor.lnum) { break; } - amount = get_indent(); - if (curwin->w_cursor.lnum - 1 == ourscope) { - // line above is start of the scope, thus current line - // is the one that stars a (possibly broken) line - // ending in a comma. + if (terminated == ',') { + // Line below current line is the one that starts a + // (possibly broken) line ending in a comma. break; + } else { + amount = get_indent(); + if (curwin->w_cursor.lnum - 1 == ourscope) { + // line above is start of the scope, thus current + // line is the one that stars a (possibly broken) + // line ending in a comma. + break; + } } } if (terminated == 0 || (lookfor != LOOKFOR_UNTERM && terminated == ',')) { + if (lookfor != LOOKFOR_ENUM_OR_INIT + && (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) { + amount += ind_continuation; + } // If we're in the middle of a paren thing, Go back to the line // that starts it so we can get the right prevailing indent // if ( foo && @@ -2772,30 +2947,35 @@ int get_c_indent(void) continue; } - /* Ignore unterminated lines in between, but - * reduce indent. */ - if (amount > cur_amount) + // Ignore unterminated lines in between, but + // reduce indent. + if (amount > cur_amount) { amount = cur_amount; + } } else { - /* - * Found first unterminated line on a row, may - * line up with this line, remember its indent - * 100 + - * -> here; - */ + // Found first unterminated line on a row, may + // line up with this line, remember its indent + // 100 + // NOLINT(whitespace/tab) + // -> here; // NOLINT(whitespace/tab) + l = get_cursor_line_ptr(); amount = cur_amount; - /* - * If previous line ends in ',', check whether we - * are in an initialization or enum - * struct xxx = - * { - * sizeof a, - * 124 }; - * or a normal possible continuation line. - * but only, of no other statement has been found - * yet. - */ + n = (int)STRLEN(l); + if (terminated == ',' + && (*skipwhite(l) == ']' + || (n >=2 && l[n - 2] == ']'))) { + break; + } + + // If previous line ends in ',', check whether we + // are in an initialization or enum + // struct xxx = + // { + // sizeof a, + // 124 }; + // or a normal possible continuation line. + // but only, of no other statement has been found + // yet. if (lookfor == LOOKFOR_INITIAL && terminated == ',') { if (curbuf->b_ind_js) { // Search for a line ending in a comma @@ -2809,8 +2989,10 @@ int get_c_indent(void) // 4 * // 5, // 6, - lookfor = LOOKFOR_NO_COMMA; - amount = get_indent(); // XXX + if (cin_iscomment(skipwhite(l))) { + break; + } + lookfor = LOOKFOR_COMMA; trypos = find_match_char('[', curbuf->b_ind_maxparen); if (trypos != NULL) { if (trypos->lnum == curwin->w_cursor.lnum - 1) { @@ -2831,7 +3013,9 @@ int get_c_indent(void) // XXX cont_amount = cin_get_equal_amount( curwin->w_cursor.lnum); } - if (lookfor != LOOKFOR_TERM && lookfor != LOOKFOR_JS_KEY) { + if (lookfor != LOOKFOR_TERM + && lookfor != LOOKFOR_JS_KEY + && lookfor != LOOKFOR_COMMA) { lookfor = LOOKFOR_UNTERM; } } @@ -3038,252 +3222,257 @@ term_again: /* subtract extra left-shift for jump labels */ if (curbuf->b_ind_jump_label > 0 && original_line_islabel) amount -= curbuf->b_ind_jump_label; + + goto theend; } - else { - // Ok -- we're not inside any sort of structure at all! - // - // this means we're at the top level, and everything should - // basically just match where the previous line is, except - // for the lines immediately following a function declaration, - // which are K&R-style parameters and need to be indented. - - // if our line starts with an open brace, forget about any - // prevailing indent and make sure it looks like the start - // of a function - if (theline[0] == '{') { - amount = curbuf->b_ind_first_open; - } - /* - * If the NEXT line is a function declaration, the current - * line needs to be indented as a function type spec. - * Don't do this if the current line looks like a comment or if the - * current line is terminated, ie. ends in ';', or if the current line - * contains { or }: "void f() {\n if (1)" - */ - else if (cur_curpos.lnum < curbuf->b_ml.ml_line_count - && !cin_nocode(theline) - && vim_strchr(theline, '{') == NULL - && vim_strchr(theline, '}') == NULL - && !cin_ends_in(theline, (char_u *)":", NULL) - && !cin_ends_in(theline, (char_u *)",", NULL) - && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, - cur_curpos.lnum + 1) - && !cin_isterminated(theline, FALSE, TRUE)) { - amount = curbuf->b_ind_func_type; - } else { - amount = 0; - curwin->w_cursor = cur_curpos; - /* search backwards until we find something we recognize */ + // Ok -- we're not inside any sort of structure at all! + // + // this means we're at the top level, and everything should + // basically just match where the previous line is, except + // for the lines immediately following a function declaration, + // which are K&R-style parameters and need to be indented. - while (curwin->w_cursor.lnum > 1) { - curwin->w_cursor.lnum--; - curwin->w_cursor.col = 0; + // if our line starts with an open brace, forget about any + // prevailing indent and make sure it looks like the start + // of a function - l = get_cursor_line_ptr(); + if (theline[0] == '{') { + amount = curbuf->b_ind_first_open; + goto theend; + } + /* + * If the NEXT line is a function declaration, the current + * line needs to be indented as a function type spec. + * Don't do this if the current line looks like a comment or if the + * current line is terminated, ie. ends in ';', or if the current line + * contains { or }: "void f() {\n if (1)" + */ + if (cur_curpos.lnum < curbuf->b_ml.ml_line_count + && !cin_nocode(theline) + && vim_strchr(theline, '{') == NULL + && vim_strchr(theline, '}') == NULL + && !cin_ends_in(theline, (char_u *)":", NULL) + && !cin_ends_in(theline, (char_u *)",", NULL) + && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, + cur_curpos.lnum + 1) + && !cin_isterminated(theline, false, true)) { + amount = curbuf->b_ind_func_type; + goto theend; + } - /* - * If we're in a comment now, skip to the start of the comment. - */ /* XXX */ - if ((trypos = ind_find_start_comment()) != NULL) { - curwin->w_cursor.lnum = trypos->lnum + 1; - curwin->w_cursor.col = 0; - continue; - } + /* search backwards until we find something we recognize */ + amount = 0; + curwin->w_cursor = cur_curpos; + while (curwin->w_cursor.lnum > 1) { + curwin->w_cursor.lnum--; + curwin->w_cursor.col = 0; - /* - * Are we at the start of a cpp base class declaration or - * constructor initialization? - */ /* XXX */ - n = FALSE; - if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') { - n = cin_is_cpp_baseclass(&col); - l = get_cursor_line_ptr(); - } - if (n) { - /* XXX */ - amount = get_baseclass_amount(col); - break; - } + l = get_cursor_line_ptr(); - /* - * Skip preprocessor directives and blank lines. - */ - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) - continue; + /* + * If we're in a comment or raw string now, skip to the start + * of it. + */ /* XXX */ + if ((trypos = ind_find_start_CORS()) != NULL) { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } - if (cin_nocode(l)) - continue; + /* + * Are we at the start of a cpp base class declaration or + * constructor initialization? + */ /* XXX */ + n = false; + if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') { + n = cin_is_cpp_baseclass(&cache_cpp_baseclass); + l = get_cursor_line_ptr(); + } + if (n) { + /* XXX */ + amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); + break; + } - /* - * If the previous line ends in ',', use one level of - * indentation: - * int foo, - * bar; - * do this before checking for '}' in case of eg. - * enum foobar - * { - * ... - * } foo, - * bar; - */ - n = 0; - if (cin_ends_in(l, (char_u *)",", NULL) - || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) { - /* take us back to opening paren */ - if (find_last_paren(l, '(', ')') - && (trypos = find_match_paren( - curbuf->b_ind_maxparen)) != NULL) - curwin->w_cursor = *trypos; - - /* For a line ending in ',' that is a continuation line go - * back to the first line with a backslash: - * char *foo = "bla\ - * bla", - * here; - */ - while (n == 0 && curwin->w_cursor.lnum > 1) { - l = ml_get(curwin->w_cursor.lnum - 1); - if (*l == NUL || l[STRLEN(l) - 1] != '\\') - break; - --curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; - } + /* + * Skip preprocessor directives and blank lines. + */ + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) + continue; - amount = get_indent(); /* XXX */ + if (cin_nocode(l)) + continue; - if (amount == 0) - amount = cin_first_id_amount(); - if (amount == 0) - amount = ind_continuation; + /* + * If the previous line ends in ',', use one level of + * indentation: + * int foo, + * bar; + * do this before checking for '}' in case of eg. + * enum foobar + * { + * ... + * } foo, + * bar; + */ + n = 0; + if (cin_ends_in(l, (char_u *)",", NULL) + || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) { + /* take us back to opening paren */ + if (find_last_paren(l, '(', ')') + && (trypos = find_match_paren( + curbuf->b_ind_maxparen)) != NULL) + curwin->w_cursor = *trypos; + + /* For a line ending in ',' that is a continuation line go + * back to the first line with a backslash: + * char *foo = "bla\ + * bla", + * here; + */ + while (n == 0 && curwin->w_cursor.lnum > 1) { + l = ml_get(curwin->w_cursor.lnum - 1); + if (*l == NUL || l[STRLEN(l) - 1] != '\\') break; - } + --curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + } - /* - * If the line looks like a function declaration, and we're - * not in a comment, put it the left margin. - */ - if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) /* XXX */ - break; - l = get_cursor_line_ptr(); + amount = get_indent(); /* XXX */ - /* - * Finding the closing '}' of a previous function. Put - * current line at the left margin. For when 'cino' has "fs". - */ - if (*skipwhite(l) == '}') - break; + if (amount == 0) + amount = cin_first_id_amount(); + if (amount == 0) + amount = ind_continuation; + break; + } - /* (matching {) - * If the previous line ends on '};' (maybe followed by - * comments) align at column 0. For example: - * char *string_array[] = { "foo", - * / * x * / "b};ar" }; / * foobar * / - */ - if (cin_ends_in(l, (char_u *)"};", NULL)) - break; + /* + * If the line looks like a function declaration, and we're + * not in a comment, put it the left margin. + */ + if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) /* XXX */ + break; + l = get_cursor_line_ptr(); - // If the previous line ends on '[' we are probably in an - // array constant: - // something = [ - // 234, <- extra indent - if (cin_ends_in(l, (char_u *)"[", NULL)) { - amount = get_indent() + ind_continuation; - break; - } + /* + * Finding the closing '}' of a previous function. Put + * current line at the left margin. For when 'cino' has "fs". + */ + if (*skipwhite(l) == '}') + break; - /* - * Find a line only has a semicolon that belongs to a previous - * line ending in '}', e.g. before an #endif. Don't increase - * indent then. - */ - if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) { - pos_T curpos_save = curwin->w_cursor; + /* (matching {) + * If the previous line ends on '};' (maybe followed by + * comments) align at column 0. For example: + * char *string_array[] = { "foo", + * / * x * / "b};ar" }; / * foobar * / + */ + if (cin_ends_in(l, (char_u *)"};", NULL)) + break; - while (curwin->w_cursor.lnum > 1) { - look = ml_get(--curwin->w_cursor.lnum); - if (!(cin_nocode(look) || cin_ispreproc_cont( - &look, &curwin->w_cursor.lnum))) - break; - } - if (curwin->w_cursor.lnum > 0 - && cin_ends_in(look, (char_u *)"}", NULL)) - break; + // If the previous line ends on '[' we are probably in an + // array constant: + // something = [ + // 234, <- extra indent + if (cin_ends_in(l, (char_u *)"[", NULL)) { + amount = get_indent() + ind_continuation; + break; + } - curwin->w_cursor = curpos_save; - } + /* + * Find a line only has a semicolon that belongs to a previous + * line ending in '}', e.g. before an #endif. Don't increase + * indent then. + */ + if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) { + pos_T curpos_save = curwin->w_cursor; - /* - * If the PREVIOUS line is a function declaration, the current - * line (and the ones that follow) needs to be indented as - * parameters. - */ - if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) { - amount = curbuf->b_ind_param; + while (curwin->w_cursor.lnum > 1) { + look = ml_get(--curwin->w_cursor.lnum); + if (!(cin_nocode(look) || cin_ispreproc_cont( + &look, &curwin->w_cursor.lnum))) break; - } + } + if (curwin->w_cursor.lnum > 0 + && cin_ends_in(look, (char_u *)"}", NULL)) + break; - /* - * If the previous line ends in ';' and the line before the - * previous line ends in ',' or '\', ident to column zero: - * int foo, - * bar; - * indent_to_0 here; - */ - if (cin_ends_in(l, (char_u *)";", NULL)) { - l = ml_get(curwin->w_cursor.lnum - 1); - if (cin_ends_in(l, (char_u *)",", NULL) - || (*l != NUL && l[STRLEN(l) - 1] == '\\')) - break; - l = get_cursor_line_ptr(); - } + curwin->w_cursor = curpos_save; + } - /* - * Doesn't look like anything interesting -- so just - * use the indent of this line. - * - * Position the cursor over the rightmost paren, so that - * matching it will take us back to the start of the line. - */ - find_last_paren(l, '(', ')'); + /* + * If the PREVIOUS line is a function declaration, the current + * line (and the ones that follow) needs to be indented as + * parameters. + */ + if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) { + amount = curbuf->b_ind_param; + break; + } - if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) - curwin->w_cursor = *trypos; - amount = get_indent(); /* XXX */ + /* + * If the previous line ends in ';' and the line before the + * previous line ends in ',' or '\', ident to column zero: + * int foo, + * bar; + * indent_to_0 here; + */ + if (cin_ends_in(l, (char_u *)";", NULL)) { + l = ml_get(curwin->w_cursor.lnum - 1); + if (cin_ends_in(l, (char_u *)",", NULL) + || (*l != NUL && l[STRLEN(l) - 1] == '\\')) break; - } + l = get_cursor_line_ptr(); + } - /* add extra indent for a comment */ - if (cin_iscomment(theline)) - amount += curbuf->b_ind_comment; + /* + * Doesn't look like anything interesting -- so just + * use the indent of this line. + * + * Position the cursor over the rightmost paren, so that + * matching it will take us back to the start of the line. + */ + find_last_paren(l, '(', ')'); - /* add extra indent if the previous line ended in a backslash: - * "asdfasdf\ - * here"; - * char *foo = "asdf\ - * here"; - */ - if (cur_curpos.lnum > 1) { - l = ml_get(cur_curpos.lnum - 1); - if (*l != NUL && l[STRLEN(l) - 1] == '\\') { - cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1); - if (cur_amount > 0) - amount = cur_amount; - else if (cur_amount == 0) - amount += ind_continuation; - } - } + if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) + curwin->w_cursor = *trypos; + amount = get_indent(); /* XXX */ + break; + } + + /* add extra indent for a comment */ + if (cin_iscomment(theline)) + amount += curbuf->b_ind_comment; + + /* add extra indent if the previous line ended in a backslash: + * "asdfasdf\ + * here"; + * char *foo = "asdf\ + * here"; + */ + if (cur_curpos.lnum > 1) { + l = ml_get(cur_curpos.lnum - 1); + if (*l != NUL && l[STRLEN(l) - 1] == '\\') { + cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1); + if (cur_amount > 0) + amount = cur_amount; + else if (cur_amount == 0) + amount += ind_continuation; } } theend: + if (amount < 0) + amount = 0; + +laterend: /* put the cursor back where it belongs */ curwin->w_cursor = cur_curpos; xfree(linecopy); - if (amount < 0) - return 0; return amount; } diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 04d6cbf5e3..99e94fc60f 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -1,8 +1,3 @@ -/* - * functions that use lookup tables for various things, generally to do with - * special key codes. - */ - #include <assert.h> #include <inttypes.h> #include <limits.h> @@ -18,6 +13,9 @@ #include "nvim/strings.h" #include "nvim/mouse.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "keymap.c.generated.h" +#endif /* * Some useful tables. @@ -36,7 +34,8 @@ static struct modmasktable { {MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2'}, {MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3'}, {MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4'}, - /* 'A' must be the last one */ + {MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D'}, + // 'A' must be the last one {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A'}, {0, 0, NUL} }; @@ -483,26 +482,28 @@ char_u *get_special_key_name(int c, int modifiers) return string; } -/* - * Try translating a <> name at (*srcp)[] to dst[]. - * Return the number of characters added to dst[], zero for no match. - * If there is a match, srcp is advanced to after the <> name. - * dst[] must be big enough to hold the result (up to six characters)! - */ -unsigned int -trans_special ( - char_u **srcp, - char_u *dst, - int keycode /* prefer key code, e.g. K_DEL instead of DEL */ -) +/// Try translating a <> name +/// +/// @param[in,out] srcp Source from which <> are translated. Is advanced to +/// after the <> name if there is a match. +/// @param[in] src_len Length of the srcp. +/// @param[out] dst Location where translation result will be kept. Must have +/// at least six bytes. +/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. +/// +/// @return Number of characters added to dst, zero for no match. +unsigned int trans_special(const char_u **srcp, const size_t src_len, + char_u *const dst, const bool keycode) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int modifiers = 0; int key; unsigned int dlen = 0; - key = find_special_key(srcp, &modifiers, keycode, FALSE); - if (key == 0) + key = find_special_key(srcp, src_len, &modifiers, keycode, false); + if (key == 0) { return 0; + } /* Put the appropriate modifier in a string */ if (modifiers != 0) { @@ -515,70 +516,78 @@ trans_special ( dst[dlen++] = K_SPECIAL; dst[dlen++] = (char_u)KEY2TERMCAP0(key); dst[dlen++] = KEY2TERMCAP1(key); - } else if (has_mbyte && !keycode) + } else if (has_mbyte && !keycode) { dlen += (unsigned int)(*mb_char2bytes)(key, dst + dlen); - else if (keycode) { + } else if (keycode) { char_u *after = add_char2buf(key, dst + dlen); assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX); dlen = (unsigned int)(after - dst); - } - else + } else { dst[dlen++] = (char_u)key; + } return dlen; } -/* - * Try translating a <> name at (*srcp)[], return the key and modifiers. - * srcp is advanced to after the <> name. - * returns 0 if there is no match. - */ -int -find_special_key ( - char_u **srcp, - int *modp, - int keycode, /* prefer key code, e.g. K_DEL instead of DEL */ - int keep_x_key /* don't translate xHome to Home key */ -) +/// Try translating a <> name +/// +/// @param[in,out] srcp Translated <> name. Is advanced to after the <> name. +/// @param[in] src_len srcp length. +/// @param[out] modp Location where information about modifiers is saved. +/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. +/// @param[in] keep_x_key Don’t translate xHome to Home key. +/// +/// @return Key and modifiers or 0 if there is no match. +int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, + const bool keycode, const bool keep_x_key) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char_u *last_dash; - char_u *end_of_name; - char_u *src; - char_u *bp; + const char_u *last_dash; + const char_u *end_of_name; + const char_u *src; + const char_u *bp; + const char_u *const end = *srcp + src_len - 1; int modifiers; int bit; int key; unsigned long n; int l; + if (src_len == 0) { + return 0; + } + src = *srcp; - if (src[0] != '<') + if (src[0] != '<') { return 0; + } - /* Find end of modifier list */ + // Find end of modifier list last_dash = src; - for (bp = src + 1; *bp == '-' || vim_isIDc(*bp); bp++) { + for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) { if (*bp == '-') { last_dash = bp; - if (bp[1] != NUL) { - if (has_mbyte) - l = mb_ptr2len(bp + 1); - else + if (bp + 1 <= end) { + if (has_mbyte) { + l = mb_ptr2len_len(bp + 1, (int) (end - bp) + 1); + } else { l = 1; - if (bp[l + 1] == '>') - bp += l; /* anything accepted, like <C-?> */ + } + if (end - bp > l && bp[l + 1] == '>') { + bp += l; // anything accepted, like <C-?> + } } } - if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) - bp += 3; /* skip t_xx, xx may be '-' or '>' */ - else if (STRNICMP(bp, "char-", 5) == 0) { - vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, NULL, NULL); + if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { + bp += 3; // skip t_xx, xx may be '-' or '>' + } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { + vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); bp += l + 5; break; } } - if (*bp == '>') { /* found matching '>' */ + if (bp <= end && *bp == '>') { // found matching '>' end_of_name = bp + 1; /* Which modifiers are given? */ @@ -586,62 +595,60 @@ find_special_key ( for (bp = src + 1; bp < last_dash; bp++) { if (*bp != '-') { bit = name_to_mod_mask(*bp); - if (bit == 0x0) - break; /* Illegal modifier name */ + if (bit == 0x0) { + break; // Illegal modifier name + } modifiers |= bit; } } - /* - * Legal modifier name. - */ + // Legal modifier name. if (bp >= last_dash) { if (STRNICMP(last_dash + 1, "char-", 5) == 0 && ascii_isdigit(last_dash[6])) { - /* <Char-123> or <Char-033> or <Char-0x33> */ - vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, NULL, &n); + // <Char-123> or <Char-033> or <Char-0x33> + vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0); key = (int)n; } else { /* * Modifier with single letter, or special key name. */ - if (has_mbyte) + if (has_mbyte) { l = mb_ptr2len(last_dash + 1); - else + } else { l = 1; - if (modifiers != 0 && last_dash[l + 1] == '>') + } + if (modifiers != 0 && last_dash[l + 1] == '>') { key = PTR2CHAR(last_dash + 1); - else { + } else { key = get_special_key_code(last_dash + 1); - if (!keep_x_key) + if (!keep_x_key) { key = handle_x_keys(key); + } } } - /* - * get_special_key_code() may return NUL for invalid - * special key name. - */ + // get_special_key_code() may return NUL for invalid + // special key name. if (key != NUL) { - /* - * Only use a modifier when there is no special key code that - * includes the modifier. - */ + // Only use a modifier when there is no special key code that + // includes the modifier. key = simplify_key(key, &modifiers); if (!keycode) { - /* don't want keycode, use single byte code */ - if (key == K_BS) + // don't want keycode, use single byte code + if (key == K_BS) { key = BS; - else if (key == K_DEL || key == K_KDEL) + } else if (key == K_DEL || key == K_KDEL) { key = DEL; + } } - /* - * Normal Key with modifier: Try to make a single byte code. - */ - if (!IS_SPECIAL(key)) + // Normal Key with modifier: + // Try to make a single byte code (except for Alt/Meta modifiers). + if (!IS_SPECIAL(key)) { key = extract_modifiers(key, &modifiers); + } *modp = modifiers; *srcp = end_of_name; @@ -652,32 +659,25 @@ find_special_key ( return 0; } -/* - * Try to include modifiers in the key. - * Changes "Shift-a" to 'A', "Alt-A" to 0xc0, etc. - */ -int extract_modifiers(int key, int *modp) +/// Try to include modifiers (except alt/meta) in the key. +/// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc. +static int extract_modifiers(int key, int *modp) { int modifiers = *modp; - if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { - key = TOUPPER_ASC(key); - modifiers &= ~MOD_MASK_SHIFT; + if (!(modifiers & MOD_MASK_CMD)) { // Command-key is special + if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { + key = TOUPPER_ASC(key); + modifiers &= ~MOD_MASK_SHIFT; + } } if ((modifiers & MOD_MASK_CTRL) - && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key)) - ) { + && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { key = Ctrl_chr(key); modifiers &= ~MOD_MASK_CTRL; - /* <C-@> is <Nul> */ - if (key == 0) + if (key == 0) { // <C-@> is <Nul> key = K_ZERO; - } - if ((modifiers & MOD_MASK_ALT) && key < 0x80 - && !enc_dbcs /* avoid creating a lead byte */ - ) { - key |= 0x80; - modifiers &= ~MOD_MASK_ALT; /* remove the META modifier */ + } } *modp = modifiers; @@ -707,7 +707,7 @@ int find_special_key_in_table(int c) * termcap name. * Return the key code, or 0 if not found. */ -int get_special_key_code(char_u *name) +int get_special_key_code(const char_u *name) { char_u *table_name; int i, j; @@ -741,50 +741,58 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) return 0; /* Shouldn't get here */ } -// Replace any terminal code strings in from[] with the equivalent internal -// vim representation. This is used for the "from" and "to" part of a -// mapping, and the "to" part of a menu command. -// Any strings like "<C-UP>" are also replaced, unless 'cpoptions' contains -// '<'. -// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER. -// -// The replacement is done in result[] and finally copied into allocated -// memory. If this all works well *bufp is set to the allocated memory and a -// pointer to it is returned. If something fails *bufp is set to NULL and from -// is returned. -// -// CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V -// is included, otherwise it is removed (for ":map xx ^V", maps xx to -// nothing). When 'cpoptions' does not contain 'B', a backslash can be used -// instead of a CTRL-V. -char_u * replace_termcodes ( - char_u *from, - char_u **bufp, - int from_part, - int do_lt, // also translate <lt> - int special // always accept <key> notation -) +/// Replace any terminal code strings with the equivalent internal +/// representation +/// +/// This is used for the "from" and "to" part of a mapping, and the "to" part of +/// a menu command. Any strings like "<C-UP>" are also replaced, unless +/// 'cpoptions' contains '<'. K_SPECIAL by itself is replaced by K_SPECIAL +/// KS_SPECIAL KE_FILLER. +/// +/// @param[in] from What characters to replace. +/// @param[in] from_len Length of the "from" argument. +/// @param[out] bufp Location where results were saved in case of success +/// (allocated). Will be set to NULL in case of failure. +/// @param[in] do_lt If true, also translate <lt>. +/// @param[in] from_part If true, trailing <C-v> is included, otherwise it is +/// removed (to make ":map xx ^V" map xx to nothing). +/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash +/// can be used in place of <C-v>. All other <C-v> +/// characters are removed. +/// @param[in] special If true, always accept <key> notation. +/// @param[in] cpo_flags Relevant flags derived from p_cpo, see +/// #CPO_TO_CPO_FLAGS. +/// +/// @return Pointer to an allocated memory in case of success, "from" in case of +/// failure. In case of success returned pointer is also saved to +/// "bufp". +char_u *replace_termcodes(const char_u *from, const size_t from_len, + char_u **bufp, const bool from_part, const bool do_lt, + const bool special, int cpo_flags) + FUNC_ATTR_NONNULL_ALL { ssize_t i; size_t slen; char_u key; size_t dlen = 0; - char_u *src; + const char_u *src; + const char_u *const end = from + from_len - 1; int do_backslash; // backslash is a special character int do_special; // recognize <> key codes char_u *result; // buffer for resulting string - do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); - do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) || special; + do_backslash = !(cpo_flags&FLAG_CPO_BSLASH); + do_special = !(cpo_flags&FLAG_CPO_SPECI) || special; // Allocate space for the translation. Worst case a single character is // replaced by 6 bytes (shifted special key), plus a NUL at the end. - result = xmalloc(STRLEN(from) * 6 + 1); + result = xmalloc(from_len * 6 + 1); src = from; // Check for #n at start only: function key n - if (from_part && src[0] == '#' && ascii_isdigit(src[1])) { // function key + if (from_part && from_len > 1 && src[0] == '#' + && ascii_isdigit(src[1])) { // function key result[dlen++] = K_SPECIAL; result[dlen++] = 'k'; if (src[1] == '0') { @@ -796,13 +804,14 @@ char_u * replace_termcodes ( } // Copy each byte from *from to result[dlen] - while (*src != NUL) { + while (src <= end) { // If 'cpoptions' does not contain '<', check for special key codes, // like "<C-S-LeftMouse>" - if (do_special && (do_lt || STRNCMP(src, "<lt>", 4) != 0)) { + if (do_special && (do_lt || ((end - src) >= 3 + && STRNCMP(src, "<lt>", 4) != 0))) { // Replace <SID> by K_SNR <script-nr> _. // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) - if (STRNICMP(src, "<SID>", 5) == 0) { + if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { if (current_SID <= 0) { EMSG(_(e_usingsid)); } else { @@ -817,7 +826,7 @@ char_u * replace_termcodes ( } } - slen = trans_special(&src, result + dlen, TRUE); + slen = trans_special(&src, (size_t) (end - src) + 1, result + dlen, true); if (slen) { dlen += slen; continue; @@ -830,10 +839,10 @@ char_u * replace_termcodes ( // Replace <Leader> by the value of "mapleader". // Replace <LocalLeader> by the value of "maplocalleader". // If "mapleader" or "maplocalleader" isn't set use a backslash. - if (STRNICMP(src, "<Leader>", 8) == 0) { + if (end - src >= 7 && STRNICMP(src, "<Leader>", 8) == 0) { len = 8; p = get_var_value((char_u *)"g:mapleader"); - } else if (STRNICMP(src, "<LocalLeader>", 13) == 0) { + } else if (end - src >= 12 && STRNICMP(src, "<LocalLeader>", 13) == 0) { len = 13; p = get_var_value((char_u *)"g:maplocalleader"); } else { @@ -862,8 +871,8 @@ char_u * replace_termcodes ( // If 'cpoptions' does not contain 'B', also accept a backslash. key = *src; if (key == Ctrl_V || (do_backslash && key == '\\')) { - ++src; // skip CTRL-V or backslash - if (*src == NUL) { + src++; // skip CTRL-V or backslash + if (src > end) { if (from_part) { result[dlen++] = key; } @@ -872,7 +881,7 @@ char_u * replace_termcodes ( } // skip multibyte char correctly - for (i = (*mb_ptr2len)(src); i > 0; --i) { + for (i = (*mb_ptr2len_len)(src, (int) (end - src) + 1); i > 0; i--) { // If the character is K_SPECIAL, replace it with K_SPECIAL // KS_SPECIAL KE_FILLER. // If compiled with the GUI replace CSI with K_CSI. diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 766362d145..bb8ba84a6a 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -1,6 +1,8 @@ #ifndef NVIM_KEYMAP_H #define NVIM_KEYMAP_H +#include "nvim/strings.h" + /* * Keycode definitions for special keys. * @@ -112,11 +114,11 @@ #define TO_SPECIAL(a, b) ((a) == KS_SPECIAL ? K_SPECIAL : (a) == \ KS_ZERO ? K_ZERO : TERMCAP2KEY(a, b)) -/* - * Codes for keys that do not have a termcap name. - * - * K_SPECIAL KS_EXTRA KE_xxx - */ +// Codes for keys that do not have a termcap name. +// +// K_SPECIAL KS_EXTRA KE_xxx +// +// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). enum key_extra { KE_NAME = 3 /* name of this terminal entry */ @@ -436,11 +438,12 @@ enum key_extra { /* 0x01 cannot be used, because the modifier must be 0x02 or higher */ #define MOD_MASK_SHIFT 0x02 #define MOD_MASK_CTRL 0x04 -#define MOD_MASK_ALT 0x08 /* aka META */ -#define MOD_MASK_META 0x10 /* META when it's different from ALT */ -#define MOD_MASK_2CLICK 0x20 /* use MOD_MASK_MULTI_CLICK */ -#define MOD_MASK_3CLICK 0x40 /* use MOD_MASK_MULTI_CLICK */ -#define MOD_MASK_4CLICK 0x60 /* use MOD_MASK_MULTI_CLICK */ +#define MOD_MASK_ALT 0x08 // aka META +#define MOD_MASK_META 0x10 // META when it's different from ALT +#define MOD_MASK_2CLICK 0x20 // use MOD_MASK_MULTI_CLICK +#define MOD_MASK_3CLICK 0x40 // use MOD_MASK_MULTI_CLICK +#define MOD_MASK_4CLICK 0x60 // use MOD_MASK_MULTI_CLICK +#define MOD_MASK_CMD 0x80 // "super" key (OSX/Mac: command-key) #define MOD_MASK_MULTI_CLICK (MOD_MASK_2CLICK|MOD_MASK_3CLICK| \ MOD_MASK_4CLICK) @@ -451,16 +454,23 @@ enum key_extra { */ #define MAX_KEY_NAME_LEN 25 -/* Maximum length of a special key event as tokens. This includes modifiers. - * The longest event is something like <M-C-S-T-4-LeftDrag> which would be the - * following string of tokens: - * - * <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KT_LEFTDRAG>. - * - * This is a total of 6 tokens, and is currently the longest one possible. - */ +// Maximum length of a special key event as tokens. This includes modifiers. +// The longest event is something like <M-C-S-T-4-LeftDrag> which would be the +// following string of tokens: +// +// <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KE_LEFTDRAG>. +// +// This is a total of 6 tokens, and is currently the longest one possible. #define MAX_KEY_CODE_LEN 6 +#define FLAG_CPO_BSLASH 0x01 +#define FLAG_CPO_SPECI 0x02 +#define CPO_TO_CPO_FLAGS (((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \ + ? 0 \ + : FLAG_CPO_BSLASH)| \ + (vim_strchr(p_cpo, CPO_SPECI) == NULL \ + ? 0 \ + : FLAG_CPO_SPECI)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "keymap.h.generated.h" diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h index 56be29d14c..8287cb14da 100644 --- a/src/nvim/lib/khash.h +++ b/src/nvim/lib/khash.h @@ -184,7 +184,7 @@ typedef khint_t khiter_t; #define kfree(P) xfree(P) #endif -static const double __ac_HASH_UPPER = 0.77; +#define __ac_HASH_UPPER 0.77 #define __KHASH_TYPE(name, khkey_t, khval_t) \ typedef struct { \ diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 0466cb229c..b41ef0cc9f 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -60,6 +60,7 @@ int main() { #define kv_pop(v) ((v).items[--(v).size]) #define kv_size(v) ((v).size) #define kv_max(v) ((v).capacity) +#define kv_last(v) kv_A(v, kv_size(v) - 1) #define kv_resize(type, v, s) ((v).capacity = (s), (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity)) @@ -77,10 +78,10 @@ int main() { (v).items[(v).size++] = (x); \ } while (0) -#define kv_pushp(type, v) (((v).size == (v).capacity)? \ +#define kv_pushp(type, v) ((((v).size == (v).capacity)? \ ((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \ (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \ - : 0), ((v).items + ((v).size++)) + : 0), ((v).items + ((v).size++))) #define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \ ((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \ diff --git a/src/nvim/log.c b/src/nvim/log.c index 5767da03af..773d497881 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -10,10 +10,6 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif - #define USR_LOG_FILE "$HOME" _PATHSEPSTR ".nvimlog" static uv_mutex_t mutex; diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 26ab5a7de7..5f69fa2f6a 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -1,6 +1,17 @@ #ifndef NVIM_MACROS_H #define NVIM_MACROS_H +// EXTERN is only defined in main.c. That's where global variables are +// actually defined and initialized. +#ifndef EXTERN +# define EXTERN extern +# define INIT(...) +#else +# ifndef INIT +# define INIT(...) __VA_ARGS__ +# endif +#endif + #ifndef MIN # define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #endif diff --git a/src/nvim/main.c b/src/nvim/main.c index cef10d12d5..71a972e8f6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -219,9 +219,10 @@ int main(int argc, char **argv) { argv0 = (char *)path_tail((char_u *)argv[0]); - char_u *fname = NULL; /* file name from command line */ - mparm_T params; /* various parameters passed between - * main() and other functions. */ + char_u *fname = NULL; // file name from command line + mparm_T params; // various parameters passed between + // main() and other functions. + char_u *cwd = NULL; // current workding dir on startup time_init(); /* Many variables are in "params" so that we can pass them to invoked @@ -237,8 +238,8 @@ int main(int argc, char **argv) check_and_set_isatty(¶ms); // Get the name with which Nvim was invoked, with and without path. - set_vim_var_string(VV_PROGPATH, (char_u *)argv[0], -1); - set_vim_var_string(VV_PROGNAME, path_tail((char_u *)argv[0]), -1); + set_vim_var_string(VV_PROGPATH, argv[0], -1); + set_vim_var_string(VV_PROGNAME, (char *) path_tail((char_u *) argv[0]), -1); event_init(); /* @@ -316,14 +317,16 @@ int main(int argc, char **argv) } // open terminals when opening files that start with term:// - do_cmdline_cmd("autocmd BufReadCmd term://* " +#define PROTO "term://" + do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested " ":call termopen( " // Capture the command string "matchstr(expand(\"<amatch>\"), " - "'\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " + "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " // capture the working directory "{'cwd': get(matchlist(expand(\"<amatch>\"), " - "'\\c\\mterm://\\(.\\{-}\\)//'), 1, '')})"); + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})"); +#undef PROTO /* Execute --cmd arguments. */ exe_pre_commands(¶ms); @@ -331,6 +334,14 @@ int main(int argc, char **argv) /* Source startup scripts. */ source_startup_scripts(¶ms); + // If using the runtime (-u is not NONE), enable syntax & filetype plugins. + if (params.use_vimrc == NULL || strcmp(params.use_vimrc, "NONE") != 0) { + // Does ":filetype plugin indent on". + filetype_maybe_enable(); + // Sources syntax/syntax.vim, which calls `:filetype on`. + syn_maybe_on(); + } + /* * Read all the plugin files. * Only when compiled with +eval, since most plugins need it. @@ -461,11 +472,10 @@ int main(int argc, char **argv) TIME_MSG("jump to first error"); } - /* - * If opened more than one window, start editing files in the other - * windows. - */ - edit_buffers(¶ms); + // If opened more than one window, start editing files in the other + // windows. + edit_buffers(¶ms, cwd); + xfree(cwd); if (params.diff_mode) { /* set options in each window for "nvim -d". */ @@ -649,6 +659,9 @@ static void init_locale(void) setlocale(LC_NUMERIC, "C"); # endif +# ifdef LOCALE_INSTALL_DIR // gnu/linux standard: $prefix/share/locale + bindtextdomain(PROJECT_NAME, LOCALE_INSTALL_DIR); +# else // old vim style: $runtime/lang { char_u *p; @@ -657,11 +670,12 @@ static void init_locale(void) p = (char_u *)vim_getenv("VIMRUNTIME"); if (p != NULL && *p != NUL) { vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); - bindtextdomain(VIMPACKAGE, (char *)NameBuff); + bindtextdomain(PROJECT_NAME, (char *)NameBuff); } xfree(p); - textdomain(VIMPACKAGE); } +# endif + textdomain(PROJECT_NAME); TIME_MSG("locale set"); } #endif @@ -739,6 +753,7 @@ static void command_line_scan(mparm_T *parmp) putchar(b->data[i]); } + msgpack_packer_free(p); mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) { parmp->headless = true; @@ -1126,10 +1141,11 @@ scripterror: /* If there is a "+123" or "-c" command, set v:swapcommand to the first * one. */ if (parmp->n_commands > 0) { - p = xmalloc(STRLEN(parmp->commands[0]) + 3); - sprintf((char *)p, ":%s\r", parmp->commands[0]); - set_vim_var_string(VV_SWAPCOMMAND, p, -1); - xfree(p); + const size_t swcmd_len = STRLEN(parmp->commands[0]) + 3; + char *const swcmd = xmalloc(swcmd_len); + snprintf(swcmd, swcmd_len, ":%s\r", parmp->commands[0]); + set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1); + xfree(swcmd); } TIME_MSG("parsing arguments"); } @@ -1182,12 +1198,19 @@ static char_u *get_fname(mparm_T *parmp) * Expand wildcards in file names. */ if (!parmp->literal) { - /* Temporarily add '(' and ')' to 'isfname'. These are valid - * filename characters but are excluded from 'isfname' to make - * "gf" work on a file name in parenthesis (e.g.: see vim.h). */ + cwd = xmalloc(MAXPATHL); + if (cwd != NULL) { + os_dirname(cwd, MAXPATHL); + } + // Temporarily add '(' and ')' to 'isfname'. These are valid + // filename characters but are excluded from 'isfname' to make + // "gf" work on a file name in parenthesis (e.g.: see vim.h). do_cmdline_cmd(":set isf+=(,)"); alist_expand(NULL, 0); do_cmdline_cmd(":set isf&"); + if (cwd != NULL) { + os_chdir((char *)cwd); + } } #endif return alist_name(&GARGLIST[0]); @@ -1417,11 +1440,9 @@ static void create_windows(mparm_T *parmp) } } -/* - * If opened more than one window, start editing files in the other - * windows. make_windows() has already opened the windows. - */ -static void edit_buffers(mparm_T *parmp) +/// If opened more than one window, start editing files in the other +/// windows. make_windows() has already opened the windows. +static void edit_buffers(mparm_T *parmp, char_u *cwd) { int arg_idx; /* index in argument list */ int i; @@ -1442,7 +1463,10 @@ static void edit_buffers(mparm_T *parmp) arg_idx = 1; for (i = 1; i < parmp->window_count; ++i) { - /* When w_arg_idx is -1 remove the window (see create_windows()). */ + if (cwd != NULL) { + os_chdir((char *)cwd); + } + // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { ++arg_idx; win_close(curwin, TRUE); diff --git a/src/nvim/map.c b/src/nvim/map.c index ed7bda4cce..d4262ae9a8 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -18,6 +18,9 @@ #define uint32_t_eq kh_int_hash_equal #define int_hash kh_int_hash_func #define int_eq kh_int_hash_equal +#define linenr_T_hash kh_int_hash_func +#define linenr_T_eq kh_int_hash_equal + #if defined(ARCH_64) #define ptr_t_hash(key) uint64_t_hash((uint64_t)key) @@ -78,6 +81,25 @@ return rv; \ } \ \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \ + { \ + int ret; \ + khiter_t k; \ + if (put) { \ + k = kh_put(T##_##U##_map, map->table, key, &ret); \ + if (ret) { \ + kh_val(map->table, k) = INITIALIZER(T, U); \ + } \ + } else { \ + k = kh_get(T##_##U##_map, map->table, key); \ + if (k == kh_end(map->table)) { \ + return NULL; \ + } \ + } \ + \ + return &kh_val(map->table, k); \ + } \ + \ U map_##T##_##U##_del(Map(T, U) *map, T key) \ { \ U rv = INITIALIZER(T, U); \ @@ -118,3 +140,5 @@ MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) #define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .async = false} MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) +#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL } +MAP_IMPL(linenr_T, bufhl_vec_T, KVEC_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index c0e2ca3aac..e90cc360ce 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -6,6 +6,7 @@ #include "nvim/map_defs.h" #include "nvim/api/private/defs.h" #include "nvim/msgpack_rpc/defs.h" +#include "nvim/bufhl_defs.h" #define MAP_DECLS(T, U) \ KHASH_DECLARE(T##_##U##_map, T, U) \ @@ -19,6 +20,7 @@ U map_##T##_##U##_get(Map(T, U) *map, T key); \ bool map_##T##_##U##_has(Map(T, U) *map, T key); \ U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \ U map_##T##_##U##_del(Map(T, U) *map, T key); \ void map_##T##_##U##_clear(Map(T, U) *map); @@ -28,12 +30,14 @@ MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) MAP_DECLS(String, MsgpackRpcRequestHandler) +MAP_DECLS(linenr_T, bufhl_vec_T) #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free #define map_get(T, U) map_##T##_##U##_get #define map_has(T, U) map_##T##_##U##_has #define map_put(T, U) map_##T##_##U##_put +#define map_ref(T, U) map_##T##_##U##_ref #define map_del(T, U) map_##T##_##U##_del #define map_clear(T, U) map_##T##_##U##_clear diff --git a/src/nvim/mark.c b/src/nvim/mark.c index e2f212340c..fe802e48ba 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -922,6 +922,7 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) } sign_mark_adjust(line1, line2, amount, amount_after); + bufhl_mark_adjust(curbuf, line1, line2, amount, amount_after); } /* previous context mark */ diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index b02c18c53b..3495203c43 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -568,16 +568,11 @@ char_u * mb_init(void) /* When enc_utf8 is set or reset, (de)allocate ScreenLinesUC[] */ screenalloc(false); - /* When using Unicode, set default for 'fileencodings'. */ - if (enc_utf8 && !option_was_set((char_u *)"fencs")) - set_string_option_direct((char_u *)"fencs", -1, - (char_u *)"ucs-bom,utf-8,default,latin1", OPT_FREE, 0); - #ifdef HAVE_WORKING_LIBINTL /* GNU gettext 0.10.37 supports this feature: set the codeset used for * translated messages independently from the current locale. */ - (void)bind_textdomain_codeset(VIMPACKAGE, - enc_utf8 ? "utf-8" : (char *)p_enc); + (void)bind_textdomain_codeset(PROJECT_NAME, + enc_utf8 ? "utf-8" : (char *)p_enc); #endif @@ -1309,35 +1304,38 @@ int utfc_ptr2char(const char_u *p, int *pcc) */ int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen) { - int len; - int c; - int cc; +#define IS_COMPOSING(s1, s2, s3) \ + (i == 0 ? UTF_COMPOSINGLIKE((s1), (s2)) : utf_iscomposing((s3))) + + assert(maxlen > 0); + int i = 0; - c = utf_ptr2char(p); - len = utf_ptr2len_len(p, maxlen); - /* Only accept a composing char when the first char isn't illegal. */ - if ((len > 1 || *p < 0x80) - && len < maxlen - && p[len] >= 0x80 - && UTF_COMPOSINGLIKE(p, p + len)) { - cc = utf_ptr2char(p + len); - for (;; ) { - pcc[i++] = cc; - if (i == MAX_MCO) - break; - len += utf_ptr2len_len(p + len, maxlen - len); - if (len >= maxlen - || p[len] < 0x80 - || !utf_iscomposing(cc = utf_ptr2char(p + len))) + int len = utf_ptr2len_len(p, maxlen); + // Is it safe to use utf_ptr2char()? + bool safe = len > 1 && len <= maxlen; + int c = safe ? utf_ptr2char(p) : *p; + + // Only accept a composing char when the first char isn't illegal. + if ((safe || c < 0x80) && len < maxlen && p[len] >= 0x80) { + for (; i < MAX_MCO; i++) { + int len_cc = utf_ptr2len_len(p + len, maxlen - len); + safe = len_cc > 1 && len_cc <= maxlen - len; + if (!safe || (pcc[i] = utf_ptr2char(p + len)) < 0x80 + || !IS_COMPOSING(p, p + len, pcc[i])) { break; + } + len += len_cc; } } - if (i < MAX_MCO) /* last composing char must be 0 */ + if (i < MAX_MCO) { + // last composing char must be 0 pcc[i] = 0; + } return c; +#undef ISCOMPOSING } /* @@ -2417,11 +2415,8 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET int i; if (STRCMP(enc, "default") == 0) { - /* Use the default encoding as it's found by set_init_1(). */ - char_u *r = get_encoding_default(); - if (r == NULL) - r = (char_u *)"latin1"; - return vim_strsave(r); + // Use the default encoding as found by set_init_1(). + return vim_strsave(fenc_default); } /* copy "enc" to allocated memory, with room for two '-' */ diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 3df4cbc4a3..6599db787f 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -40,6 +40,7 @@ #include <limits.h> #include <string.h> #include <stdbool.h> +#include <fcntl.h> #include "nvim/vim.h" #include "nvim/ascii.h" @@ -78,8 +79,6 @@ static size_t total_mem_used = 0; /// total memory used for memfiles /// - NULL, on failure. memfile_T *mf_open(char_u *fname, int flags) { - off_t size; - memfile_T *mfp = xmalloc(sizeof(memfile_T)); if (fname == NULL) { // no file, use memory only @@ -87,11 +86,9 @@ memfile_T *mf_open(char_u *fname, int flags) mfp->mf_ffname = NULL; mfp->mf_fd = -1; } else { // try to open the file - mf_do_open(mfp, fname, flags); - - if (mfp->mf_fd < 0) { // fail if file could not be opened + if (!mf_do_open(mfp, fname, flags)) { xfree(mfp); - return NULL; + return NULL; // fail if file could not be opened } } @@ -114,6 +111,8 @@ memfile_T *mf_open(char_u *fname, int flags) } } + off_t size; + // When recovering, the actual block size will be retrieved from block 0 // in ml_recover(). The size used here may be wrong, therefore mf_blocknr_max // must be rounded up. @@ -170,13 +169,12 @@ memfile_T *mf_open(char_u *fname, int flags) /// FAIL If file could not be opened. int mf_open_file(memfile_T *mfp, char_u *fname) { - mf_do_open(mfp, fname, O_RDWR|O_CREAT|O_EXCL); // try to open the file - - if (mfp->mf_fd < 0) - return FAIL; + if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) { + mfp->mf_dirty = true; + return OK; + } - mfp->mf_dirty = true; - return OK; + return FAIL; } /// Close a memory file and optionally delete the associated file. @@ -184,28 +182,28 @@ int mf_open_file(memfile_T *mfp, char_u *fname) /// @param del_file Whether to delete associated file. void mf_close(memfile_T *mfp, bool del_file) { - bhdr_T *hp, *nextp; - if (mfp == NULL) { // safety check return; } if (mfp->mf_fd >= 0 && close(mfp->mf_fd) < 0) { EMSG(_(e_swapclose)); } - if (del_file && mfp->mf_fname != NULL) + if (del_file && mfp->mf_fname != NULL) { os_remove((char *)mfp->mf_fname); + } + // free entries in used list - for (hp = mfp->mf_used_first; hp != NULL; hp = nextp) { + for (bhdr_T *hp = mfp->mf_used_first, *nextp; hp != NULL; hp = nextp) { total_mem_used -= hp->bh_page_count * mfp->mf_page_size; nextp = hp->bh_next; mf_free_bhdr(hp); } - while (mfp->mf_free_first != NULL) // free entries in free list + while (mfp->mf_free_first != NULL) { // free entries in free list xfree(mf_rem_free(mfp)); + } mf_hash_free(&mfp->mf_hash); mf_hash_free_all(&mfp->mf_trans); // free hashtable and its items - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); + mf_free_fnames(mfp); xfree(mfp); } @@ -215,28 +213,28 @@ void mf_close(memfile_T *mfp, bool del_file) void mf_close_file(buf_T *buf, bool getlines) { memfile_T *mfp = buf->b_ml.ml_mfp; - if (mfp == NULL || mfp->mf_fd < 0) // nothing to close + if (mfp == NULL || mfp->mf_fd < 0) { // nothing to close return; + } if (getlines) { // get all blocks in memory by accessing all lines (clumsy!) mf_dont_release = true; - for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) { (void)ml_get_buf(buf, lnum, false); + } mf_dont_release = false; // TODO(elmart): should check if all blocks are really in core } - if (close(mfp->mf_fd) < 0) // close the file + if (close(mfp->mf_fd) < 0) { // close the file EMSG(_(e_swapclose)); + } mfp->mf_fd = -1; if (mfp->mf_fname != NULL) { os_remove((char *)mfp->mf_fname); // delete the swap file - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); - mfp->mf_fname = NULL; - mfp->mf_ffname = NULL; + mf_free_fnames(mfp); } } @@ -389,11 +387,11 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile) /// Signal block as no longer used (may put it in the free list). void mf_free(memfile_T *mfp, bhdr_T *hp) { - xfree(hp->bh_data); // free data + xfree(hp->bh_data); // free data mf_rem_hash(mfp, hp); // get *hp out of the hash list mf_rem_used(mfp, hp); // get *hp out of the used list if (hp->bh_bnum < 0) { - xfree(hp); // don't want negative numbers in free list + xfree(hp); // don't want negative numbers in free list mfp->mf_neg_count--; } else { mf_ins_free(mfp, hp); // put *hp in the free list @@ -459,32 +457,10 @@ int mf_sync(memfile_T *mfp, int flags) if (hp == NULL || status == FAIL) mfp->mf_dirty = false; - if ((flags & MFS_FLUSH) && *p_sws != NUL) { -#if defined(UNIX) -# ifdef HAVE_FSYNC - if (STRCMP(p_sws, "fsync") == 0) { - if (fsync(mfp->mf_fd)) - status = FAIL; - } else { -# endif - // OpenNT is strictly POSIX (Benzinger). - // Tandem/Himalaya NSK-OSS doesn't have sync() -# if defined(__OPENNT) || defined(__TANDEM) - fflush(NULL); -# else - sync(); -# endif -# ifdef HAVE_FSYNC + if (flags & MFS_FLUSH) { + if (os_fsync(mfp->mf_fd)) { + status = FAIL; } -# endif -#endif -# ifdef SYNC_DUP_CLOSE - // Win32 is a bit more work: Duplicate the file handle and close it. - // This should flush the file to disk. - int fd; - if ((fd = dup(mfp->mf_fd)) >= 0) - close(fd); -# endif } got_int |= got_int_save; @@ -496,10 +472,11 @@ int mf_sync(memfile_T *mfp, int flags) /// These are blocks that need to be written to a newly created swapfile. void mf_set_dirty(memfile_T *mfp) { - bhdr_T *hp; - for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) - if (hp->bh_bnum > 0) + for (bhdr_T *hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) { + if (hp->bh_bnum > 0) { hp->bh_flags |= BH_DIRTY; + } + } mfp->mf_dirty = true; } @@ -527,10 +504,11 @@ static void mf_ins_used(memfile_T *mfp, bhdr_T *hp) hp->bh_next = mfp->mf_used_first; mfp->mf_used_first = hp; hp->bh_prev = NULL; - if (hp->bh_next == NULL) // list was empty, adjust last pointer + if (hp->bh_next == NULL) { // list was empty, adjust last pointer mfp->mf_used_last = hp; - else + } else { hp->bh_next->bh_prev = hp; + } mfp->mf_used_count += hp->bh_page_count; total_mem_used += hp->bh_page_count * mfp->mf_page_size; } @@ -636,9 +614,10 @@ bool mf_release_all(void) FOR_ALL_BUFFERS(buf) { memfile_T *mfp = buf->b_ml.ml_mfp; if (mfp != NULL) { - // If no swap file yet, may open one. - if (mfp->mf_fd < 0 && buf->b_may_swap) + // If no swap file yet, try to open one. + if (mfp->mf_fd < 0 && buf->b_may_swap) { ml_open_file(buf); + } // Flush as many blocks as possible, only if there is a swapfile. if (mfp->mf_fd >= 0) { @@ -773,7 +752,8 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) else page_count = hp2->bh_page_count; size = page_size * page_count; - if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL) { + void *data = (hp2 == NULL) ? hp->bh_data : hp2->bh_data; + if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) { /// Avoid repeating the error message, this mostly happens when the /// disk is full. We give the message again only after a successful /// write or when hitting a key. We keep on trying, in case some @@ -794,20 +774,6 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) return OK; } -/// Write block to memfile's file. -/// -/// @return OK On success. -/// FAIL On failure. -static int mf_write_block(memfile_T *mfp, bhdr_T *hp, - off_t offset, unsigned size) -{ - void *data = hp->bh_data; - int result = OK; - if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) - result = FAIL; - return result; -} - /// Make block number positive and add it to the translation list. /// /// @return OK On success. @@ -877,13 +843,23 @@ blocknr_T mf_trans_del(memfile_T *mfp, blocknr_T old_nr) return new_bnum; } -/// Set full file name of memfile's swapfile, out of simple file name and some -/// other considerations. +/// Frees mf_fname and mf_ffname. +void mf_free_fnames(memfile_T *mfp) +{ + xfree(mfp->mf_fname); + xfree(mfp->mf_ffname); + mfp->mf_fname = NULL; + mfp->mf_ffname = NULL; +} + +/// Set the simple file name and the full file name of memfile's swapfile, out +/// of simple file name and some other considerations. /// /// Only called when creating or renaming the swapfile. Either way it's a new /// name so we must work out the full path name. -void mf_set_ffname(memfile_T *mfp) +void mf_set_fnames(memfile_T *mfp, char_u *fname) { + mfp->mf_fname = fname; mfp->mf_ffname = (char_u *)FullName_save((char *)mfp->mf_fname, false); } @@ -899,7 +875,7 @@ void mf_fullname(memfile_T *mfp) } } -/// Return TRUE if there are any translations pending for memfile. +/// Return true if there are any translations pending for memfile. bool mf_need_trans(memfile_T *mfp) { return mfp->mf_fname != NULL && mfp->mf_neg_count > 0; @@ -910,11 +886,11 @@ bool mf_need_trans(memfile_T *mfp) /// "fname" must be in allocated memory, and is consumed (also when error). /// /// @param flags Flags for open(). -static void mf_do_open(memfile_T *mfp, char_u *fname, int flags) +/// @return A bool indicating success of the `open` call. +static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags) { // fname cannot be NameBuff, because it must have been allocated. - mfp->mf_fname = fname; - mf_set_ffname(mfp); + mf_set_fnames(mfp, fname); /// Extra security check: When creating a swap file it really shouldn't /// exist yet. If there is a symbolic link, this is most likely an attack. @@ -925,26 +901,26 @@ static void mf_do_open(memfile_T *mfp, char_u *fname, int flags) EMSG(_("E300: Swap file already exists (symlink attack?)")); } else { // try to open the file - flags |= O_NOFOLLOW; - mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags); + mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags | O_NOFOLLOW); } // If the file cannot be opened, use memory only if (mfp->mf_fd < 0) { - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); - mfp->mf_fname = NULL; - mfp->mf_ffname = NULL; - } else { + mf_free_fnames(mfp); + return false; + } + #ifdef HAVE_FD_CLOEXEC - int fdflags = fcntl(mfp->mf_fd, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + int fdflags = fcntl(mfp->mf_fd, F_GETFD); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + } #endif #ifdef HAVE_SELINUX - mch_copy_sec(fname, mfp->mf_fname); + mch_copy_sec(fname, mfp->mf_fname); #endif - } + + return true; } // @@ -969,20 +945,21 @@ static void mf_hash_init(mf_hashtab_T *mht) /// The hash table must not be used again without another mf_hash_init() call. static void mf_hash_free(mf_hashtab_T *mht) { - if (mht->mht_buckets != mht->mht_small_buckets) + if (mht->mht_buckets != mht->mht_small_buckets) { xfree(mht->mht_buckets); + } } /// Free the array of a hash table and all the items it contains. static void mf_hash_free_all(mf_hashtab_T *mht) { - mf_hashitem_T *next; - - for (size_t idx = 0; idx <= mht->mht_mask; idx++) + for (size_t idx = 0; idx <= mht->mht_mask; idx++) { + mf_hashitem_T *next; for (mf_hashitem_T *mhi = mht->mht_buckets[idx]; mhi != NULL; mhi = next) { next = mhi->mhi_next; xfree(mhi); } + } mf_hash_free(mht); } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 0ba8dd98d0..4e35dd481f 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -38,6 +38,7 @@ #include <inttypes.h> #include <string.h> #include <stdbool.h> +#include <fcntl.h> #include "nvim/ascii.h" #include "nvim/vim.h" @@ -64,7 +65,6 @@ #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/version.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" @@ -425,10 +425,8 @@ void ml_setname(buf_T *buf) /* try to rename the swap file */ if (vim_rename(mfp->mf_fname, fname) == 0) { success = TRUE; - xfree(mfp->mf_fname); - mfp->mf_fname = fname; - xfree(mfp->mf_ffname); - mf_set_ffname(mfp); + mf_free_fnames(mfp); + mf_set_fnames(mfp, fname); ml_upd_block0(buf, UB_SAME_DIR); break; } @@ -788,9 +786,8 @@ void ml_recover(void) if (fname == NULL) /* When there is no file name */ fname = (char_u *)""; len = (int)STRLEN(fname); - if (len >= 4 && - STRNICMP(fname + len - 4, ".s", 2) - == 0 + if (len >= 4 + && STRNICMP(fname + len - 4, ".s", 2) == 0 && vim_strchr((char_u *)"UVWuvw", fname[len - 2]) != NULL && ASCII_ISALPHA(fname[len - 1])) { directly = TRUE; @@ -3195,7 +3192,7 @@ attention_message ( */ static int do_swapexists(buf_T *buf, char_u *fname) { - set_vim_var_string(VV_SWAPNAME, fname, -1); + set_vim_var_string(VV_SWAPNAME, (char *) fname, -1); set_vim_var_string(VV_SWAPCHOICE, NULL, -1); /* Trigger SwapExists autocommands with <afile> set to the file being @@ -3954,8 +3951,10 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp) if (ffdos) size += lnum - 1; - /* Don't count the last line break if 'bin' and 'noeol'. */ - if (buf->b_p_bin && !buf->b_p_eol && buf->b_ml.ml_line_count == lnum) { + /* Don't count the last line break if 'noeol' and ('bin' or + * 'nofixeol'). */ + if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol + && buf->b_ml.ml_line_count == lnum) { size -= ffdos + 1; } } diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h index bcc1c673d2..34a002af5d 100644 --- a/src/nvim/memline_defs.h +++ b/src/nvim/memline_defs.h @@ -41,7 +41,7 @@ typedef struct memline { int ml_flags; infoptr_T *ml_stack; /* stack of pointer blocks (array of IPTRs) */ - int ml_stack_top; /* current top if ml_stack */ + int ml_stack_top; /* current top of ml_stack */ int ml_stack_size; /* total number of entries in ml_stack */ linenr_T ml_line_lnum; /* line number of cached line, 0 if not valid */ diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 91a72abfc5..3c2394d579 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -215,10 +215,12 @@ ex_menu ( if (STRICMP(map_to, "<nop>") == 0) { /* "<Nop>" means nothing */ map_to = (char_u *)""; map_buf = NULL; - } else if (modes & MENU_TIP_MODE) - map_buf = NULL; /* Menu tips are plain text. */ - else - map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE, special); + } else if (modes & MENU_TIP_MODE) { + map_buf = NULL; // Menu tips are plain text. + } else { + map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true, + special, CPO_TO_CPO_FLAGS); + } menuarg.modes = modes; menuarg.noremap[0] = noremap; menuarg.silent[0] = silent; diff --git a/src/nvim/message.c b/src/nvim/message.c index 66b8b9b5d2..265f8c00c0 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -61,14 +61,8 @@ static int confirm_msg_used = FALSE; /* displaying confirm_msg */ static char_u *confirm_msg = NULL; /* ":confirm" message */ static char_u *confirm_msg_tail; /* tail of confirm_msg */ -struct msg_hist { - struct msg_hist *next; - char_u *msg; - int attr; -}; - -static struct msg_hist *first_msg_hist = NULL; -static struct msg_hist *last_msg_hist = NULL; +MessageHistoryEntry *first_msg_hist = NULL; +MessageHistoryEntry *last_msg_hist = NULL; static int msg_hist_len = 0; static FILE *verbose_fd = NULL; @@ -149,10 +143,11 @@ msg_attr_keep ( { static int entered = 0; int retval; - char_u *buf = NULL; + char_u *buf = NULL; - if (attr == 0) - set_vim_var_string(VV_STATUSMSG, s, -1); + if (attr == 0) { + set_vim_var_string(VV_STATUSMSG, (char *) s, -1); + } /* * It is possible that displaying a messages causes a problem (e.g., @@ -472,22 +467,23 @@ int emsg(char_u *s) { int attr; char_u *p; - int ignore = FALSE; + int ignore = false; int severe; - /* Skip this if not giving error messages at the moment. */ - if (emsg_not_now()) - return TRUE; + // Skip this if not giving error messages at the moment. + if (emsg_not_now()) { + return true; + } - called_emsg = TRUE; - ex_exitval = 1; + called_emsg = true; + if (emsg_silent == 0) { + ex_exitval = 1; + } - /* - * If "emsg_severe" is TRUE: When an error exception is to be thrown, - * prefer this message over previous messages for the same command. - */ + // If "emsg_severe" is TRUE: When an error exception is to be thrown, + // prefer this message over previous messages for the same command. severe = emsg_severe; - emsg_severe = FALSE; + emsg_severe = false; if (!emsg_off || vim_strchr(p_debug, 't') != NULL) { /* @@ -503,8 +499,8 @@ int emsg(char_u *s) return TRUE; } - /* set "v:errmsg", also when using ":silent! cmd" */ - set_vim_var_string(VV_ERRMSG, s, -1); + // set "v:errmsg", also when using ":silent! cmd" + set_vim_var_string(VV_ERRMSG, (char *) s, -1); /* * When using ":silent! cmd" ignore error messages. @@ -563,49 +559,23 @@ int emsg(char_u *s) return msg_attr(s, attr); } -/* - * Print an error message with one "%s" and one string argument. - */ -int emsg2(char_u *s, char_u *a1) -{ - return emsg3(s, a1, NULL); -} - void emsg_invreg(int name) { EMSG2(_("E354: Invalid register name: '%s'"), transchar(name)); } -/// Print an error message with one or two "%s" and one or two string arguments. -int emsg3(char_u *s, char_u *a1, char_u *a2) +/// Print an error message with unknown number of arguments +bool emsgf(const char *const fmt, ...) { if (emsg_not_now()) { - return TRUE; // no error messages at the moment + return true; } - vim_snprintf((char *)IObuff, IOSIZE, (char *)s, a1, a2); - return emsg(IObuff); -} - -/// Print an error message with one "%" PRId64 and one (int64_t) argument. -int emsgn(char_u *s, int64_t n) -{ - if (emsg_not_now()) { - return TRUE; // no error messages at the moment - } - - vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n); - return emsg(IObuff); -} - -/// Print an error message with one "%" PRIu64 and one (uint64_t) argument. -int emsgu(char_u *s, uint64_t n) -{ - if (emsg_not_now()) { - return TRUE; // no error messages at the moment - } + va_list ap; + va_start(ap, fmt); + vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL); + va_end(ap); - vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n); return emsg(IObuff); } @@ -1396,7 +1366,8 @@ void msg_prt_line(char_u *s, int list) c = *p_extra++; } else if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1) { col += (*mb_ptr2cells)(s); - if (lcs_nbsp != NUL && list && mb_ptr2char(s) == 160) { + if (lcs_nbsp != NUL && list + && (mb_ptr2char(s) == 160 || mb_ptr2char(s) == 0x202f)) { mb_char2bytes(lcs_nbsp, buf); buf[(*mb_ptr2len)(buf)] = NUL; } else { @@ -1600,39 +1571,31 @@ static void msg_puts_display(char_u *str, int maxlen, int attr, int recurse) int wrap; int did_last_char; - did_wait_return = FALSE; + did_wait_return = false; while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) { - /* - * We are at the end of the screen line when: - * - When outputting a newline. - * - When outputting a character in the last column. - */ - if (!recurse && msg_row >= Rows - 1 && (*s == '\n' || ( - cmdmsg_rl - ? ( - msg_col <= 1 - || (*s == TAB && msg_col <= 7) - || (has_mbyte && - (*mb_ptr2cells)(s) > 1 && - msg_col <= 2) - ) - : - (msg_col + t_col >= Columns - 1 - || (*s == TAB && msg_col + - t_col >= ((Columns - 1) & ~7)) - || (has_mbyte && - (*mb_ptr2cells)(s) > 1 - && msg_col + t_col >= - Columns - 2) - )))) { - /* - * The screen is scrolled up when at the last row (some terminals - * scroll automatically, some don't. To avoid problems we scroll - * ourselves). - */ - if (t_col > 0) - /* output postponed text */ + // We are at the end of the screen line when: + // - When outputting a newline. + // - When outputting a character in the last column. + if (!recurse && msg_row >= Rows - 1 + && (*s == '\n' || (cmdmsg_rl + ? (msg_col <= 1 + || (*s == TAB && msg_col <= 7) + || (has_mbyte + && (*mb_ptr2cells)(s) > 1 + && msg_col <= 2)) + : (msg_col + t_col >= Columns - 1 + || (*s == TAB + && msg_col + t_col >= ((Columns - 1) & ~7)) + || (has_mbyte + && (*mb_ptr2cells)(s) > 1 + && msg_col + t_col >= Columns - 2))))) { + // The screen is scrolled up when at the last row (some terminals + // scroll automatically, some don't. To avoid problems we scroll + // ourselves). + if (t_col > 0) { + // output postponed text t_puts(&t_col, t_s, s, attr); + } /* When no more prompt and no more room, truncate here */ if (msg_no_more && lines_left == 0) @@ -1739,18 +1702,15 @@ static void msg_puts_display(char_u *str, int maxlen, int attr, int recurse) cw = 1; l = 1; } - /* When drawing from right to left or when a double-wide character - * doesn't fit, draw a single character here. Otherwise collect - * characters and draw them all at once later. */ - if ( - cmdmsg_rl - || - (cw > 1 && msg_col + t_col >= Columns - 1) - ) { - if (l > 1) + // When drawing from right to left or when a double-wide character + // doesn't fit, draw a single character here. Otherwise collect + // characters and draw them all at once later. + if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) { + if (l > 1) { s = screen_puts_mbyte(s, l, attr) - 1; - else + } else { msg_screen_putchar(*s, attr); + } } else { /* postpone this character until later */ if (t_col == 0) @@ -1786,25 +1746,24 @@ static void msg_scroll_up(void) static void inc_msg_scrolled(void) { if (*get_vim_var_str(VV_SCROLLSTART) == NUL) { - char_u *p = sourcing_name; - char_u *tofree = NULL; - int len; - - /* v:scrollstart is empty, set it to the script/function name and line - * number */ - if (p == NULL) - p = (char_u *)_("Unknown"); - else { - len = (int)STRLEN(p) + 40; + char *p = (char *) sourcing_name; + char *tofree = NULL; + + // v:scrollstart is empty, set it to the script/function name and line + // number + if (p == NULL) { + p = _("Unknown"); + } else { + size_t len = strlen(p) + 40; tofree = xmalloc(len); - vim_snprintf((char *)tofree, len, _("%s line %" PRId64), - p, (int64_t)sourcing_lnum); + vim_snprintf(tofree, len, _("%s line %" PRId64), + p, (int64_t) sourcing_lnum); p = tofree; } set_vim_var_string(VV_SCROLLSTART, p, -1); xfree(tofree); } - ++msg_scrolled; + msg_scrolled++; } static msgchunk_T *last_msgchunk = NULL; /* last displayed text */ @@ -2571,7 +2530,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) /* Don't want a hit-enter prompt here. */ ++no_wait_return; - set_vim_var_string(VV_WARNINGMSG, message, -1); + set_vim_var_string(VV_WARNINGMSG, (char *) message, -1); xfree(keep_msg); keep_msg = NULL; if (hl) @@ -3037,7 +2996,7 @@ static double tv_float(typval_T *tvs, int *idxp) * http://www.ijs.si/software/snprintf/ * * This snprintf() only supports the following conversion specifiers: - * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * s, c, b, B, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) * with flags: '-', '+', ' ', '0' and '#'. * An asterisk is supported for field width as well as precision. * @@ -3085,7 +3044,7 @@ int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...) return str_l; } -int vim_snprintf(char *str, size_t str_m, char *fmt, ...) +int vim_snprintf(char *str, size_t str_m, const char *fmt, ...) { va_list ap; int str_l; @@ -3096,15 +3055,17 @@ int vim_snprintf(char *str, size_t str_m, char *fmt, ...) return str_l; } -int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) +int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, + typval_T *tvs) { size_t str_l = 0; bool str_avail = str_l < str_m; - char *p = fmt; + const char *p = fmt; int arg_idx = 1; - if (!p) + if (!p) { p = ""; + } while (*p) { if (*p != '%') { // copy up to the next '%' or NUL without any changes @@ -3133,7 +3094,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) char tmp[TMP_LEN]; // string address in case of string argument - char *str_arg; + const char *str_arg; // natural field width of arg without padding and sign size_t str_arg_l; @@ -3176,9 +3137,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) if (*p == '*') { p++; int j = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int); - if (j >= 0) + if (j >= 0) { min_field_width = j; - else { + } else { min_field_width = -j; justify_left = 1; } @@ -3187,8 +3148,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) // argument like common implementations do unsigned int uj = *p++ - '0'; - while (ascii_isdigit((int)(*p))) + while (ascii_isdigit((int)(*p))) { uj = 10 * uj + (unsigned int)(*p++ - '0'); + } min_field_width = uj; } @@ -3199,9 +3161,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) if (*p == '*') { int j = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int); p++; - if (j >= 0) + if (j >= 0) { precision = j; - else { + } else { precision_specified = 0; precision = 0; } @@ -3210,8 +3172,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) // treat argument like common implementations do unsigned int uj = *p++ - '0'; - while (ascii_isdigit((int)(*p))) + while (ascii_isdigit((int)(*p))) { uj = 10 * uj + (unsigned int)(*p++ - '0'); + } precision = uj; } } @@ -3262,14 +3225,13 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) if (!str_arg) { str_arg = "[NULL]"; str_arg_l = 6; - } - // make sure not to address string beyond the specified precision - else if (!precision_specified) + } else if (!precision_specified) { + // make sure not to address string beyond the specified precision str_arg_l = strlen(str_arg); - // truncate string if necessary as requested by precision - else if (precision == 0) + } else if (precision == 0) { + // truncate string if necessary as requested by precision str_arg_l = 0; - else { + } else { // memchr on HP does not like n > 2^31 // TODO(elmart): check if this still holds / is relevant str_arg_l = (size_t)((char *)xmemscan(str_arg, @@ -3283,8 +3245,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) - mb_string2cells((char_u *) str_arg); if (precision) { char_u *p1 = (char_u *)str_arg; - for (size_t i = 0; i < precision && *p1; i++) + for (size_t i = 0; i < precision && *p1; i++) { p1 += mb_ptr2len(p1); + } str_arg_l = precision = p1 - (char_u *)str_arg; } } @@ -3295,9 +3258,14 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) } break; - case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { - // u, o, x, X and p conversion specifiers imply the value is unsigned; - // d implies a signed value + case 'd': + case 'u': + case 'b': case 'B': + case 'o': + case 'x': case 'X': + case 'p': { + // u, b, B, o, x, X and p conversion specifiers imply + // the value is unsigned; d implies a signed value // 0 if numeric argument is zero (or if pointer is NULL for 'p'), // +1 if greater than zero (or non NULL for 'p'), @@ -3325,8 +3293,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) if (fmt_spec == 'p') { length_modifier = '\0'; ptr_arg = tvs ? (void *)tv_str(tvs, &arg_idx) : va_arg(ap, void *); - if (ptr_arg) + if (ptr_arg) { arg_sign = 1; + } } else if (fmt_spec == 'd') { // signed switch (length_modifier) { @@ -3334,25 +3303,28 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) case 'h': // char and short arguments are passed as int int_arg = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int); - if (int_arg > 0) + if (int_arg > 0) { arg_sign = 1; - else if (int_arg < 0) + } else if (int_arg < 0) { arg_sign = -1; + } break; case 'l': long_arg = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, long int); - if (long_arg > 0) + if (long_arg > 0) { arg_sign = 1; - else if (long_arg < 0) + } else if (long_arg < 0) { arg_sign = -1; + } break; case '2': long_long_arg = tvs ? tv_nr(tvs, &arg_idx) - : va_arg(ap, long long int); - if (long_long_arg > 0) + : va_arg(ap, long long int); // NOLINT (runtime/int) + if (long_long_arg > 0) { arg_sign = 1; - else if (long_long_arg < 0) + } else if (long_long_arg < 0) { arg_sign = -1; + } break; } } else { @@ -3362,24 +3334,23 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) case 'h': uint_arg = tvs ? (unsigned)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int); - if (uint_arg != 0) - arg_sign = 1; + if (uint_arg != 0) { arg_sign = 1; } break; case 'l': ulong_arg = tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long int); - if (ulong_arg != 0) - arg_sign = 1; + if (ulong_arg != 0) { arg_sign = 1; } break; case '2': - ulong_long_arg = tvs ? (unsigned long long)tv_nr(tvs, &arg_idx) - : va_arg(ap, unsigned long long int); - if (ulong_long_arg) arg_sign = 1; + ulong_long_arg = tvs + ? (unsigned long long)tv_nr(tvs, &arg_idx) // NOLINT (runtime/int) + : va_arg(ap, unsigned long long int); // NOLINT (runtime/int) + if (ulong_long_arg) { arg_sign = 1; } break; case 'z': size_t_arg = tvs ? (size_t)tv_nr(tvs, &arg_idx) : va_arg(ap, size_t); - if (size_t_arg) arg_sign = 1; + if (size_t_arg) { arg_sign = 1; } break; } } @@ -3390,16 +3361,19 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) // For d, i, u, o, x, and X conversions, if precision is specified, // '0' flag should be ignored. This is so with Solaris 2.6, Digital UNIX // 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. - if (precision_specified) + if (precision_specified) { zero_padding = 0; + } if (fmt_spec == 'd') { - if (force_sign && arg_sign >= 0) + if (force_sign && arg_sign >= 0) { tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + } // leave negative numbers for sprintf to handle, to // avoid handling tricky cases like (short int)-32768 } else if (alternate_form) { - if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' + || fmt_spec == 'b' || fmt_spec == 'B')) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } @@ -3407,20 +3381,20 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) } zero_padding_insertion_ind = str_arg_l; - if (!precision_specified) + if (!precision_specified) { precision = 1; // default precision is 1 + } if (precision == 0 && arg_sign == 0) { // when zero value is formatted with an explicit precision 0, - // resulting formatted string is empty (d, i, u, o, x, X, p) + // resulting formatted string is empty (d, i, u, b, B, o, x, X, p) } else { char f[5]; int f_l = 0; // construct a simple format string for sprintf f[f_l++] = '%'; - if (!length_modifier) - ; - else if (length_modifier == '2') { + if (!length_modifier) { + } else if (length_modifier == '2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } else @@ -3441,6 +3415,41 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) case '2': str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg); break; } + } else if (fmt_spec == 'b' || fmt_spec == 'B') { + // binary + size_t bits = 0; + switch (length_modifier) { + case '\0': + case 'h': for (bits = sizeof(unsigned) * 8; bits > 0; bits--) { + if ((uint_arg >> (bits - 1)) & 0x1) { break; } } + + while (bits > 0) { + tmp[str_arg_l++] = + ((uint_arg >> --bits) & 0x1) ? '1' : '0'; } + break; + case 'l': for (bits = sizeof(unsigned long) * 8; bits > 0; bits--) { + if ((ulong_arg >> (bits - 1)) & 0x1) { break; } } + + while (bits > 0) { + tmp[str_arg_l++] = + ((ulong_arg >> --bits) & 0x1) ? '1' : '0'; } + break; + case '2': for (bits = sizeof(unsigned long long) * 8; // NOLINT (runtime/int) + bits > 0; bits--) { + if ((ulong_long_arg >> (bits - 1)) & 0x1) { break; } } + + while (bits > 0) { + tmp[str_arg_l++] = + ((ulong_long_arg >> --bits) & 0x1) ? '1' : '0'; } + break; + case 'z': for (bits = sizeof(size_t) * 8; bits > 0; bits--) { + if ((size_t_arg >> (bits - 1)) & 0x1) { break; } } + + while (bits > 0) { + tmp[str_arg_l++] = + ((size_t_arg >> --bits) & 0x1) ? '1' : '0'; } + break; + } } else { // unsigned switch (length_modifier) { @@ -3464,7 +3473,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) if (zero_padding_insertion_ind + 1 < str_arg_l && tmp[zero_padding_insertion_ind] == '0' && (tmp[zero_padding_insertion_ind + 1] == 'x' - || tmp[zero_padding_insertion_ind + 1] == 'X')) + || tmp[zero_padding_insertion_ind + 1] == 'X' + || tmp[zero_padding_insertion_ind + 1] == 'b' + || tmp[zero_padding_insertion_ind + 1] == 'B')) zero_padding_insertion_ind += 2; } @@ -3507,7 +3518,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) // floating point char format[40]; int l; - int remove_trailing_zeroes = FALSE; + int remove_trailing_zeroes = false; double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double); double abs_f = f < 0 ? -f : f; @@ -3518,7 +3529,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) fmt_spec = 'f'; else fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; - remove_trailing_zeroes = TRUE; + remove_trailing_zeroes = true; } if (fmt_spec == 'f' && abs_f > 1.0e307) { diff --git a/src/nvim/message.h b/src/nvim/message.h index 019c7bfb73..d3a16fff93 100644 --- a/src/nvim/message.h +++ b/src/nvim/message.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include <stdarg.h> #include "nvim/eval_defs.h" // for typval_T +#include "nvim/ex_cmds_defs.h" // for exarg_T /* * Types of dialogs passed to do_dialog(). @@ -24,6 +25,56 @@ #define VIM_ALL 5 #define VIM_DISCARDALL 6 +/// Show plain message +#define MSG(s) msg((char_u *)(s)) + +/// Show message highlighted according to the attr +#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr)) + +/// Display error message +/// +/// Sets error flag in process, can be transformed into an exception. +#define EMSG(s) emsg((char_u *)(s)) + +/// Like #EMSG, but for messages with one "%s" inside +#define EMSG2(s, p) emsgf((const char *) (s), (p)) + +/// Like #EMSG, but for messages with two "%s" inside +#define EMSG3(s, p, q) emsgf((const char *) (s), (p), (q)) + +/// Like #EMSG, but for messages with one "%" PRId64 inside +#define EMSGN(s, n) emsgf((const char *) (s), (int64_t)(n)) + +/// Like #EMSG, but for messages with one "%" PRIu64 inside +#define EMSGU(s, n) emsgf((const char *) (s), (uint64_t)(n)) + +/// Display message at the recorded position +#define MSG_PUTS(s) msg_puts((char_u *)(s)) + +/// Display message at the recorded position, highlighted +#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a)) + +/// Like #MSG_PUTS, but highlight like title +#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s)) + +/// Like #MSG_PUTS, but if middle part of too long messages it will be replaced +#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0) + +/// Like #MSG_PUTS_ATTR, but if middle part of long messages will be replaced +#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a)) + +/// Message history for `:messages` +typedef struct msg_hist { + struct msg_hist *next; ///< Next message. + char_u *msg; ///< Message text. + int attr; ///< Message highlighting. +} MessageHistoryEntry; + +/// First message +extern MessageHistoryEntry *first_msg_hist; +/// Last message +extern MessageHistoryEntry *last_msg_hist; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "message.h.generated.h" #endif diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 96ef6cbaef..48791384a6 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -43,7 +43,6 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -86,38 +85,32 @@ open_line ( int second_line_indent ) { - char_u *saved_line; /* copy of the original line */ - char_u *next_line = NULL; /* copy of the next line */ - char_u *p_extra = NULL; /* what goes to next line */ - int less_cols = 0; /* less columns for mark in new line */ - int less_cols_off = 0; /* columns to skip for mark adjust */ - pos_T old_cursor; /* old cursor position */ - int newcol = 0; /* new cursor column */ - int newindent = 0; /* auto-indent of the new line */ - int n; - int trunc_line = FALSE; /* truncate current line afterwards */ - int retval = FALSE; /* return value, default is FAIL */ - int extra_len = 0; /* length of p_extra string */ - int lead_len; /* length of comment leader */ - char_u *lead_flags; /* position in 'comments' for comment leader */ - char_u *leader = NULL; /* copy of comment leader */ - char_u *allocated = NULL; /* allocated memory */ - char_u *p; - int saved_char = NUL; /* init for GCC */ - pos_T *pos; - int do_si = (!p_paste && curbuf->b_p_si - && !curbuf->b_p_cin - ); - int no_si = FALSE; /* reset did_si afterwards */ - int first_char = NUL; /* init for GCC */ + char_u *next_line = NULL; // copy of the next line + char_u *p_extra = NULL; // what goes to next line + colnr_T less_cols = 0; // less columns for mark in new line + colnr_T less_cols_off = 0; // columns to skip for mark adjust + pos_T old_cursor; // old cursor position + colnr_T newcol = 0; // new cursor column + int newindent = 0; // auto-indent of the new line + bool trunc_line = false; // truncate current line afterwards + bool retval = false; // return value, default is false + int extra_len = 0; // length of p_extra string + int lead_len; // length of comment leader + char_u *lead_flags; // position in 'comments' for comment leader + char_u *leader = NULL; // copy of comment leader + char_u *allocated = NULL; // allocated memory + char_u *p; + char_u saved_char = NUL; // init for GCC + pos_T *pos; + bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin); + bool no_si = false; // reset did_si afterwards + int first_char = NUL; // init for GCC int vreplace_mode; - int did_append; /* appended a new line */ - int saved_pi = curbuf->b_p_pi; /* copy of preserveindent setting */ + bool did_append; // appended a new line + int saved_pi = curbuf->b_p_pi; // copy of preserveindent setting - /* - * make a copy of the current line so we can mess with it - */ - saved_line = vim_strsave(get_cursor_line_ptr()); + // make a copy of the current line so we can mess with it + char_u *saved_line = vim_strsave(get_cursor_line_ptr()); if (State & VREPLACE_FLAG) { /* @@ -204,22 +197,19 @@ open_line ( char_u *ptr; char_u last_char; - old_cursor = curwin->w_cursor; + pos_T old_cursor = curwin->w_cursor; ptr = saved_line; if (flags & OPENLINE_DO_COM) lead_len = get_leader_len(ptr, NULL, FALSE, TRUE); else lead_len = 0; if (dir == FORWARD) { - /* - * Skip preprocessor directives, unless they are - * recognised as comments. - */ - if ( - lead_len == 0 && - ptr[0] == '#') { - while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) + // Skip preprocessor directives, unless they are + // recognised as comments. + if (lead_len == 0 && ptr[0] == '#') { + while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) { ptr = ml_get(--curwin->w_cursor.lnum); + } newindent = get_indent(); } if (flags & OPENLINE_DO_COM) @@ -303,28 +293,26 @@ open_line ( && cin_is_cinword(ptr)) did_si = TRUE; } - } else { /* dir == BACKWARD */ - /* - * Skip preprocessor directives, unless they are - * recognised as comments. - */ - if ( - lead_len == 0 && - ptr[0] == '#') { - int was_backslashed = FALSE; - - while ((ptr[0] == '#' || was_backslashed) && - curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') - was_backslashed = TRUE; - else - was_backslashed = FALSE; + } else { // dir == BACKWARD + // Skip preprocessor directives, unless they are + // recognised as comments. + if (lead_len == 0 && ptr[0] == '#') { + bool was_backslashed = false; + + while ((ptr[0] == '#' || was_backslashed) + && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { + if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') { + was_backslashed = true; + } else { + was_backslashed = false; + } ptr = ml_get(++curwin->w_cursor.lnum); } - if (was_backslashed) - newindent = 0; /* Got to end of file */ - else + if (was_backslashed) { + newindent = 0; // Got to end of file + } else { newindent = get_indent(); + } } p = skipwhite(ptr); if (*p == '}') /* if line starts with '}': do indent */ @@ -401,7 +389,7 @@ open_line ( end_comment_pending = -1; /* means we want to set it */ ++p; } - n = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); + size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); if (end_comment_pending == -1) /* we can set it now */ end_comment_pending = lead_end[n - 1]; @@ -498,10 +486,11 @@ open_line ( } } if (lead_len > 0) { - /* allocate buffer (may concatenate p_extra later) */ - leader = xmalloc(lead_len + lead_repl_len + extra_space + extra_len - + (second_line_indent > 0 ? second_line_indent : 0) + 1); - allocated = leader; /* remember to free it later */ + // allocate buffer (may concatenate p_extra later) + leader = xmalloc((size_t)(lead_len + lead_repl_len + extra_space + + extra_len + (second_line_indent > 0 + ? second_line_indent : 0) + 1)); + allocated = leader; // remember to free it later STRLCPY(leader, saved_line, lead_len + 1); @@ -598,9 +587,8 @@ open_line ( if (!ascii_iswhite(*p)) { /* Don't put a space before a TAB. */ if (p + 1 < leader + lead_len && p[1] == TAB) { - --lead_len; - memmove(p, p + 1, - (leader + lead_len) - p); + lead_len--; + memmove(p, p + 1, (size_t)(leader + lead_len - p)); } else { int l = (*mb_ptr2len)(p); @@ -611,8 +599,7 @@ open_line ( --l; *p++ = ' '; } - memmove(p + 1, p + l, - (leader + lead_len) - p); + memmove(p + 1, p + l, (size_t)(leader + lead_len - p)); lead_len -= l - 1; } *p = ' '; @@ -675,16 +662,12 @@ open_line ( did_si = can_si = FALSE; } else if (comment_end != NULL) { - /* - * We have finished a comment, so we don't use the leader. - * If this was a C-comment and 'ai' or 'si' is set do a normal - * indent to align with the line containing the start of the - * comment. - */ - if (comment_end[0] == '*' && comment_end[1] == '/' && - (curbuf->b_p_ai - || do_si - )) { + // We have finished a comment, so we don't use the leader. + // If this was a C-comment and 'ai' or 'si' is set do a normal + // indent to align with the line containing the start of the + // comment. + if (comment_end[0] == '*' && comment_end[1] == '/' + && (curbuf->b_p_ai || do_si)) { old_cursor = curwin->w_cursor; curwin->w_cursor.col = (colnr_T)(comment_end - saved_line); if ((pos = findmatch(NULL, NUL)) != NULL) { @@ -813,9 +796,11 @@ open_line ( * In REPLACE mode, for each character in the new indent, there must * be a NUL on the replace stack, for when it is deleted with BS */ - if (REPLACE_NORMAL(State)) - for (n = 0; n < (int)curwin->w_cursor.col; ++n) + if (REPLACE_NORMAL(State)) { + for (colnr_T n = 0; n < curwin->w_cursor.col; n++) { replace_push(NUL); + } + } newcol += curwin->w_cursor.col; if (no_si) did_si = FALSE; @@ -1281,11 +1266,13 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) * Add column offset for 'number', 'relativenumber' and 'foldcolumn'. */ width = wp->w_width - win_col_off(wp); - if (width <= 0) - return 32000; - if (col <= (unsigned int)width) + if (width <= 0) { + return 32000; // bigger than the number of lines of the screen + } + if (col <= (unsigned int)width) { return 1; - col -= width; + } + col -= (unsigned int)width; width += win_col_off2(wp); assert(col <= INT_MAX && (int)col < INT_MAX - (width -1)); return ((int)col + (width - 1)) / width + 1; @@ -1297,15 +1284,9 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) */ int plines_win_col(win_T *wp, linenr_T lnum, long column) { - long col; - char_u *s; - int lines = 0; - int width; - char_u *line; - - /* Check for filler lines above this buffer line. When folded the result - * is one line anyway. */ - lines = diff_check_fill(wp, lnum); + // Check for filler lines above this buffer line. When folded the result + // is one line anyway. + int lines = diff_check_fill(wp, lnum); if (!wp->w_p_wrap) return lines + 1; @@ -1313,30 +1294,29 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) if (wp->w_width == 0) return lines + 1; - line = s = ml_get_buf(wp->w_buffer, lnum, FALSE); + char_u *line = ml_get_buf(wp->w_buffer, lnum, false); + char_u *s = line; - col = 0; + colnr_T col = 0; while (*s != NUL && --column >= 0) { - col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL); + col += win_lbr_chartabsize(wp, line, s, col, NULL); mb_ptr_adv(s); } - /* - * If *s is a TAB, and the TAB is not displayed as ^I, and we're not in - * INSERT mode, then col must be adjusted so that it represents the last - * screen position of the TAB. This only fixes an error when the TAB wraps - * from one screen line to the next (when 'columns' is not a multiple of - * 'ts') -- webb. - */ - if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) - col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1; + // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in + // INSERT mode, then col must be adjusted so that it represents the last + // screen position of the TAB. This only fixes an error when the TAB wraps + // from one screen line to the next (when 'columns' is not a multiple of + // 'ts') -- webb. + if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) { + col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1; + } - /* - * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. - */ - width = wp->w_width - win_col_off(wp); - if (width <= 0) + // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. + int width = wp->w_width - win_col_off(wp); + if (width <= 0) { return 9999; + } lines += 1; if (col > width) @@ -1349,11 +1329,9 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) int count = 0; while (first <= last) { - int x; - - /* Check if there are any really folded lines, but also included lines - * that are maybe folded. */ - x = foldedCount(wp, first, NULL); + // Check if there are any really folded lines, but also included lines + // that are maybe folded. + linenr_T x = foldedCount(wp, first, NULL); if (x > 0) { ++count; /* count 1 for "+-- folded" line */ first += x; @@ -1374,117 +1352,99 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) */ void ins_bytes(char_u *p) { - ins_bytes_len(p, (int)STRLEN(p)); + ins_bytes_len(p, STRLEN(p)); } -/* - * Insert string "p" with length "len" at the cursor position. - * Handles Replace mode and multi-byte characters. - */ -void ins_bytes_len(char_u *p, int len) +/// Insert string "p" with length "len" at the cursor position. +/// Handles Replace mode and multi-byte characters. +void ins_bytes_len(char_u *p, size_t len) { - int i; - int n; - - if (has_mbyte) - for (i = 0; i < len; i += n) { - if (enc_utf8) - /* avoid reading past p[len] */ - n = utfc_ptr2len_len(p + i, len - i); - else - n = (*mb_ptr2len)(p + i); + if (has_mbyte) { + size_t n; + for (size_t i = 0; i < len; i += n) { + if (enc_utf8) { + // avoid reading past p[len] + n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i)); + } else { + n = (size_t)(*mb_ptr2len)(p + i); + } ins_char_bytes(p + i, n); } - else - for (i = 0; i < len; ++i) + } else { + for (size_t i = 0; i < len; i++) { ins_char(p[i]); + } + } } -/* - * Insert or replace a single character at the cursor position. - * When in REPLACE or VREPLACE mode, replace any existing character. - * Caller must have prepared for undo. - * For multi-byte characters we get the whole character, the caller must - * convert bytes to a character. - */ +/// Insert or replace a single character at the cursor position. +/// When in REPLACE or VREPLACE mode, replace any existing character. +/// Caller must have prepared for undo. +/// For multi-byte characters we get the whole character, the caller must +/// convert bytes to a character. void ins_char(int c) { char_u buf[MB_MAXBYTES + 1]; - int n; - - n = (*mb_char2bytes)(c, buf); + size_t n = (size_t)(*mb_char2bytes)(c, buf); - /* When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte. - * Happens for CTRL-Vu9900. */ - if (buf[0] == 0) + // When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte. + // Happens for CTRL-Vu9900. + if (buf[0] == 0) { buf[0] = '\n'; - + } ins_char_bytes(buf, n); } -void ins_char_bytes(char_u *buf, int charlen) +void ins_char_bytes(char_u *buf, size_t charlen) { - int c = buf[0]; - int newlen; /* nr of bytes inserted */ - int oldlen; /* nr of bytes deleted (0 when not replacing) */ - char_u *p; - char_u *newp; - char_u *oldp; - int linelen; /* length of old line including NUL */ - colnr_T col; - linenr_T lnum = curwin->w_cursor.lnum; - int i; - - /* Break tabs if needed. */ - if (virtual_active() && curwin->w_cursor.coladd > 0) + // Break tabs if needed. + if (virtual_active() && curwin->w_cursor.coladd > 0) { coladvance_force(getviscol()); + } - col = curwin->w_cursor.col; - oldp = ml_get(lnum); - linelen = (int)STRLEN(oldp) + 1; + int c = buf[0]; + size_t col = (size_t)curwin->w_cursor.col; + linenr_T lnum = curwin->w_cursor.lnum; + char_u *oldp = ml_get(lnum); + size_t linelen = STRLEN(oldp) + 1; // length of old line including NUL - /* The lengths default to the values for when not replacing. */ - oldlen = 0; - newlen = charlen; + // The lengths default to the values for when not replacing. + size_t oldlen = 0; // nr of bytes inserted + size_t newlen = charlen; // nr of bytes deleted (0 when not replacing) if (State & REPLACE_FLAG) { if (State & VREPLACE_FLAG) { - colnr_T new_vcol = 0; /* init for GCC */ + // Disable 'list' temporarily, unless 'cpo' contains the 'L' flag. + // Returns the old value of list, so when finished, + // curwin->w_p_list should be set back to this. + int old_list = curwin->w_p_list; + if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) { + curwin->w_p_list = false; + } + // In virtual replace mode each character may replace one or more + // characters (zero if it's a TAB). Count the number of bytes to + // be deleted to make room for the new character, counting screen + // cells. May result in adding spaces to fill a gap. colnr_T vcol; - int old_list; - - /* - * Disable 'list' temporarily, unless 'cpo' contains the 'L' flag. - * Returns the old value of list, so when finished, - * curwin->w_p_list should be set back to this. - */ - old_list = curwin->w_p_list; - if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) - curwin->w_p_list = FALSE; - - /* - * In virtual replace mode each character may replace one or more - * characters (zero if it's a TAB). Count the number of bytes to - * be deleted to make room for the new character, counting screen - * cells. May result in adding spaces to fill a gap. - */ getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL); - new_vcol = vcol + chartabsize(buf, vcol); + colnr_T new_vcol = vcol + chartabsize(buf, vcol); while (oldp[col + oldlen] != NUL && vcol < new_vcol) { vcol += chartabsize(oldp + col + oldlen, vcol); - /* Don't need to remove a TAB that takes us to the right - * position. */ - if (vcol > new_vcol && oldp[col + oldlen] == TAB) + // Don't need to remove a TAB that takes us to the right + // position. + if (vcol > new_vcol && oldp[col + oldlen] == TAB) { break; - oldlen += (*mb_ptr2len)(oldp + col + oldlen); - /* Deleted a bit too much, insert spaces. */ - if (vcol > new_vcol) - newlen += vcol - new_vcol; + } + oldlen += (size_t)(*mb_ptr2len)(oldp + col + oldlen); + // Deleted a bit too much, insert spaces. + if (vcol > new_vcol) { + newlen += (size_t)(vcol - new_vcol); + } } curwin->w_p_list = old_list; } else if (oldp[col] != NUL) { - /* normal replace */ - oldlen = (*mb_ptr2len)(oldp + col); + // normal replace + oldlen = (size_t)(*mb_ptr2len)(oldp + col); } @@ -1493,38 +1453,39 @@ void ins_char_bytes(char_u *buf, int charlen) * done the other way around, so that the first byte is popped off * first (it tells the byte length of the character). */ replace_push(NUL); - for (i = 0; i < oldlen; ++i) { - if (has_mbyte) - i += replace_push_mb(oldp + col + i) - 1; - else + for (size_t i = 0; i < oldlen; i++) { + if (has_mbyte) { + i += (size_t)replace_push_mb(oldp + col + i) - 1; + } else { replace_push(oldp[col + i]); + } } } - newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen)); + char_u *newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen)); - /* Copy bytes before the cursor. */ - if (col > 0) + // Copy bytes before the cursor. + if (col > 0) { memmove(newp, oldp, (size_t)col); + } - /* Copy bytes after the changed character(s). */ - p = newp + col; - memmove(p + newlen, oldp + col + oldlen, - (size_t)(linelen - col - oldlen)); + // Copy bytes after the changed character(s). + char_u *p = newp + col; + memmove(p + newlen, oldp + col + oldlen, (size_t)(linelen - col - oldlen)); - /* Insert or overwrite the new character. */ + // Insert or overwrite the new character. memmove(p, buf, charlen); - i = charlen; - /* Fill with spaces when necessary. */ - while (i < newlen) - p[i++] = ' '; + // Fill with spaces when necessary. + for (size_t i = charlen; i < newlen; i++) { + p[i] = ' '; + } /* Replace the line in the buffer. */ ml_replace(lnum, newp, FALSE); - /* mark the buffer as changed and prepare for displaying */ - changed_bytes(lnum, col); + // mark the buffer as changed and prepare for displaying + changed_bytes(lnum, (colnr_T)col); /* * If we're in Insert or Replace mode and 'showmatch' is set, then briefly @@ -1541,8 +1502,8 @@ void ins_char_bytes(char_u *buf, int charlen) } if (!p_ri || (State & REPLACE_FLAG)) { - /* Normal insert: move cursor right */ - curwin->w_cursor.col += charlen; + // Normal insert: move cursor right + curwin->w_cursor.col += (int)charlen; } /* * TODO: should try to update w_row here, to avoid recomputing it later. @@ -1595,7 +1556,7 @@ int del_char(int fixpos) return FAIL; return del_chars(1L, fixpos); } - return del_bytes(1L, fixpos, TRUE); + return del_bytes(1, fixpos, true); } /* @@ -1603,7 +1564,7 @@ int del_char(int fixpos) */ int del_chars(long count, int fixpos) { - long bytes = 0; + int bytes = 0; long i; char_u *p; int l; @@ -1617,30 +1578,22 @@ int del_chars(long count, int fixpos) return del_bytes(bytes, fixpos, TRUE); } -/* - * Delete "count" bytes under the cursor. - * If "fixpos" is TRUE, don't leave the cursor on the NUL after the line. - * Caller must have prepared for undo. - * - * return FAIL for failure, OK otherwise - */ -int -del_bytes ( - long count, - int fixpos_arg, - int use_delcombine /* 'delcombine' option applies */ -) +/// Delete "count" bytes under the cursor. +/// If "fixpos" is true, don't leave the cursor on the NUL after the line. +/// Caller must have prepared for undo. +/// +/// @param count number of bytes to be deleted +/// @param fixpos_arg leave the cursor on the NUL after the line +/// @param use_delcombine 'delcombine' option applies +/// +/// @return FAIL for failure, OK otherwise +int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) { - char_u *oldp, *newp; - colnr_T oldlen; linenr_T lnum = curwin->w_cursor.lnum; colnr_T col = curwin->w_cursor.col; - int was_alloced; - long movelen; - int fixpos = fixpos_arg; - - oldp = ml_get(lnum); - oldlen = (int)STRLEN(oldp); + bool fixpos = fixpos_arg; + char_u *oldp = ml_get(lnum); + colnr_T oldlen = (colnr_T)STRLEN(oldp); /* * Can't do anything when the cursor is on the NUL after the line. @@ -1664,14 +1617,12 @@ del_bytes ( count = utf_ptr2len(oldp + n); n += count; } while (UTF_COMPOSINGLIKE(oldp + col, oldp + n)); - fixpos = 0; + fixpos = false; } } - /* - * When count is too big, reduce it. - */ - movelen = (long)oldlen - (long)col - count + 1; /* includes trailing NUL */ + // When count is too big, reduce it. + int movelen = oldlen - col - count + 1; // includes trailing NUL if (movelen <= 1) { /* * If we just took off the last character of a non-blank line, and @@ -1691,15 +1642,14 @@ del_bytes ( movelen = 1; } - /* - * If the old line has been allocated the deletion can be done in the - * existing line. Otherwise a new line has to be allocated. - */ - was_alloced = ml_line_alloced(); /* check if oldp was allocated */ - if (was_alloced) - newp = oldp; /* use same allocated memory */ - else { /* need to allocate a new line */ - newp = xmalloc(oldlen + 1 - count); + // If the old line has been allocated the deletion can be done in the + // existing line. Otherwise a new line has to be allocated. + bool was_alloced = ml_line_alloced(); // check if oldp was allocated + char_u *newp; + if (was_alloced) { + newp = oldp; // use same allocated memory + } else { // need to allocate a new line + newp = xmalloc((size_t)(oldlen + 1 - count)); memmove(newp, oldp, (size_t)col); } memmove(newp + col, oldp + col + count, (size_t)movelen); @@ -1725,12 +1675,12 @@ truncate_line ( linenr_T lnum = curwin->w_cursor.lnum; colnr_T col = curwin->w_cursor.col; - if (col == 0) + if (col == 0) { newp = vim_strsave((char_u *)""); - else - newp = vim_strnsave(ml_get(lnum), col); - - ml_replace(lnum, newp, FALSE); + } else { + newp = vim_strnsave(ml_get(lnum), (size_t)col); + } + ml_replace(lnum, newp, false); /* mark the buffer as changed and prepare for displaying */ changed_bytes(lnum, curwin->w_cursor.col); @@ -1827,6 +1777,9 @@ void changed(void) if (curbuf->b_may_swap && !bt_dontwrite(curbuf) ) { + int save_need_wait_return = need_wait_return; + + need_wait_return = false; ml_open_file(curbuf); /* The ml_open_file() can cause an ATTENTION message. @@ -1838,6 +1791,8 @@ void changed(void) os_delay(2000L, true); wait_return(TRUE); msg_scroll = save_msg_scroll; + } else { + need_wait_return = save_need_wait_return; } } changed_int(); @@ -1985,13 +1940,13 @@ changed_lines ( changed_common(lnum, col, lnume, xtra); } -static void -changed_lines_buf ( - buf_T *buf, - linenr_T lnum, /* first line with change */ - linenr_T lnume, /* line below last changed line */ - long xtra /* number of extra lines (negative when deleting) */ -) +/// Mark line range in buffer as changed. +/// +/// @param buf the buffer where lines were changed +/// @param lnum first line with change +/// @param lnume line below last changed line +/// @param xtra number of extra lines (negative when deleting) +void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra) { if (buf->b_mod_set) { /* find the maximum area that must be redisplayed */ @@ -2250,7 +2205,7 @@ change_warning ( msg_col = col; msg_source(hl_attr(HLF_W)); MSG_PUTS_ATTR(_(w_readonly), hl_attr(HLF_W) | MSG_HIST); - set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_readonly), -1); + set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1); msg_clr_eos(); (void)msg_end(); if (msg_silent == 0 && !silent_mode) { @@ -2352,7 +2307,7 @@ int get_keystroke(void) int save_mapped_ctrl_c = mapped_ctrl_c; int waited = 0; - mapped_ctrl_c = FALSE; /* mappings are not used here */ + mapped_ctrl_c = 0; // mappings are not used here for (;; ) { // flush output before waiting ui_flush(); @@ -2360,13 +2315,13 @@ int get_keystroke(void) * 5 chars plus NUL). And fix_input_buffer() can triple the number of * bytes. */ maxlen = (buflen - 6 - len) / 3; - if (buf == NULL) - buf = xmalloc(buflen); - else if (maxlen < 10) { - /* Need some more space. This might happen when receiving a long - * escape sequence. */ + if (buf == NULL) { + buf = xmalloc((size_t)buflen); + } else if (maxlen < 10) { + // Need some more space. This might happen when receiving a long + // escape sequence. buflen += 100; - buf = xrealloc(buf, buflen); + buf = xrealloc(buf, (size_t)buflen); maxlen = (buflen - 6 - len) / 3; } @@ -2729,45 +2684,33 @@ void fast_breakcheck(void) } } -#ifndef SEEK_SET -# define SEEK_SET 0 -#endif -#ifndef SEEK_END -# define SEEK_END 2 -#endif - -/* - * Get the stdout of an external command. - * If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not - * NULL store the length there. - * Returns an allocated string, or NULL for error. - */ -char_u * -get_cmd_output ( - char_u *cmd, - char_u *infile, /* optional input file name */ - int flags, // can be kShellOptSilent - size_t *ret_len -) +/// Get the stdout of an external command. +/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not +/// NULL store the length there. +/// +/// @param cmd command to execute +/// @param infile optional input file name +/// @param flags can be kShellOptSilent or 0 +/// @param ret_len length of the stdout +/// +/// @return an allocated string, or NULL for error. +char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, + size_t *ret_len) { - char_u *tempname; - char_u *command; - char_u *buffer = NULL; - int len; - int i = 0; - FILE *fd; + char_u *buffer = NULL; if (check_restricted() || check_secure()) return NULL; - /* get a name for the temp file */ - if ((tempname = vim_tempname()) == NULL) { + // get a name for the temp file + char_u *tempname = vim_tempname(); + if (tempname == NULL) { EMSG(_(e_notmp)); return NULL; } - /* Add the redirection stuff */ - command = make_filter_cmd(cmd, infile, tempname); + // Add the redirection stuff + char_u *command = make_filter_cmd(cmd, infile, tempname); /* * Call the shell to execute the command (errors are ignored). @@ -2779,10 +2722,8 @@ get_cmd_output ( xfree(command); - /* - * read the names from the file into memory - */ - fd = mch_fopen((char *)tempname, READBIN); + // read the names from the file into memory + FILE *fd = mch_fopen((char *)tempname, READBIN); if (fd == NULL) { EMSG2(_(e_notopen), tempname); @@ -2790,11 +2731,11 @@ get_cmd_output ( } fseek(fd, 0L, SEEK_END); - len = ftell(fd); /* get size of temp file */ + size_t len = (size_t)ftell(fd); // get size of temp file fseek(fd, 0L, SEEK_SET); buffer = xmalloc(len + 1); - i = (int)fread((char *)buffer, (size_t)1, (size_t)len, fd); + size_t i = fread((char *)buffer, 1, len, fd); fclose(fd); os_remove((char *)tempname); if (i != len) { diff --git a/src/nvim/misc1.h b/src/nvim/misc1.h index 11891d6f7e..f0f66854d8 100644 --- a/src/nvim/misc1.h +++ b/src/nvim/misc1.h @@ -2,6 +2,7 @@ #define NVIM_MISC1_H #include "nvim/vim.h" +#include "nvim/os/shell.h" /* flags for open_line() */ #define OPENLINE_DELSPACES 1 /* delete spaces after cursor */ diff --git a/src/nvim/misc2.c b/src/nvim/misc2.c index 3c0a1414a6..4b64de1be0 100644 --- a/src/nvim/misc2.c +++ b/src/nvim/misc2.c @@ -327,9 +327,10 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) } } - set_vim_var_nr(VV_SHELL_ERROR, (long)retval); - if (do_profiling == PROF_YES) + set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T) retval); + if (do_profiling == PROF_YES) { prof_child_exit(&wait_time); + } return retval; } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 3ae1a6a890..2f499e477c 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -14,6 +14,8 @@ #include "nvim/misc1.h" #include "nvim/cursor.h" #include "nvim/buffer_defs.h" +#include "nvim/memline.h" +#include "nvim/charset.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mouse.c.generated.h" @@ -503,3 +505,95 @@ void set_mouse_topline(win_T *wp) orig_topfill = wp->w_topfill; } +/// +/// Return length of line "lnum" for horizontal scrolling. +/// +static colnr_T scroll_line_len(linenr_T lnum) +{ + colnr_T col = 0; + char_u *line = ml_get(lnum); + if (*line != NUL) { + for (;;) { + int numchar = chartabsize(line, col); + mb_ptr_adv(line); + if (*line == NUL) { // don't count the last character + break; + } + col += numchar; + } + } + return col; +} + +/// +/// Find longest visible line number. +/// +static linenr_T find_longest_lnum(void) +{ + linenr_T ret = 0; + + // Calculate maximum for horizontal scrollbar. Check for reasonable + // line numbers, topline and botline can be invalid when displaying is + // postponed. + if (curwin->w_topline <= curwin->w_cursor.lnum + && curwin->w_botline > curwin->w_cursor.lnum + && curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) { + long max = 0; + + // Use maximum of all visible lines. Remember the lnum of the + // longest line, closest to the cursor line. Used when scrolling + // below. + for (linenr_T lnum = curwin->w_topline; lnum < curwin->w_botline; lnum++) { + colnr_T len = scroll_line_len(lnum); + if (len > (colnr_T)max) { + max = len; + ret = lnum; + } else if (len == (colnr_T)max + && abs((int)(lnum - curwin->w_cursor.lnum)) + < abs((int)(ret - curwin->w_cursor.lnum))) { + ret = lnum; + } + } + } else { + // Use cursor line only. + ret = curwin->w_cursor.lnum; + } + + return ret; +} + +/// +/// Do a horizontal scroll. Return TRUE if the cursor moved, FALSE otherwise. +/// +bool mouse_scroll_horiz(int dir) +{ + if (curwin->w_p_wrap) { + return false; + } + + int step = 6; + if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { + step = curwin->w_width; + } + + int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step); + if (leftcol < 0) { + leftcol = 0; + } + + if (curwin->w_leftcol == leftcol) { + return false; + } + + curwin->w_leftcol = (colnr_T)leftcol; + + // When the line of the cursor is too short, move the cursor to the + // longest visible line. + if (!virtual_active() + && (colnr_T)leftcol > scroll_line_len(curwin->w_cursor.lnum)) { + curwin->w_cursor.lnum = find_longest_lnum(); + curwin->w_cursor.col = 0; + } + + return leftcol_changed(); +} diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h index c824bcc8f0..0149f7c7c0 100644 --- a/src/nvim/mouse.h +++ b/src/nvim/mouse.h @@ -34,6 +34,12 @@ #define MOUSE_X1 0x300 // Mouse-button X1 (6th) #define MOUSE_X2 0x400 // Mouse-button X2 +// Direction for nv_mousescroll() and ins_mousescroll() +#define MSCR_DOWN 0 // DOWN must be FALSE +#define MSCR_UP 1 +#define MSCR_LEFT -1 +#define MSCR_RIGHT -2 + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mouse.h.generated.h" diff --git a/src/nvim/move.c b/src/nvim/move.c index eb55397511..b129c5cb7a 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1010,12 +1010,9 @@ scrollup ( int byfold /* true: count a closed fold as one line */ ) { - if ( - (byfold && hasAnyFolding(curwin)) - || - curwin->w_p_diff - ) { - /* count each sequence of folded lines as one logical line */ + if ((byfold && hasAnyFolding(curwin)) + || curwin->w_p_diff) { + // count each sequence of folded lines as one logical line linenr_T lnum = curwin->w_topline; while (line_count--) { if (curwin->w_topfill > 0) @@ -1288,9 +1285,10 @@ void scroll_cursor_top(int min_scroll, int always) * - at least 'scrolloff' lines above and below the cursor */ validate_cheight(); - int used = curwin->w_cline_height; - if (curwin->w_cursor.lnum < curwin->w_topline) + int used = curwin->w_cline_height; // includes filler lines above + if (curwin->w_cursor.lnum < curwin->w_topline) { scrolled = used; + } if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) { --top; @@ -1301,9 +1299,10 @@ void scroll_cursor_top(int min_scroll, int always) } new_topline = top + 1; - /* count filler lines of the cursor window as context */ + // "used" already contains the number of filler lines above, don't add it + // again. + // Hide filler lines above cursor line by adding them to "extra". int extra = diff_check_fill(curwin, curwin->w_cursor.lnum); - used += extra; /* * Check if the lines from "top" to "bot" fit in the window. If they do, @@ -1312,7 +1311,7 @@ void scroll_cursor_top(int min_scroll, int always) while (top > 0) { int i = hasFolding(top, &top, NULL) ? 1 // count one logical line for a sequence of folded lines - : plines(top); + : plines_nofill(top); used += i; if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { if (hasFolding(bot, NULL, &bot)) diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h index d97cf28ca1..5611636d4f 100644 --- a/src/nvim/msgpack_rpc/defs.h +++ b/src/nvim/msgpack_rpc/defs.h @@ -1,8 +1,6 @@ #ifndef NVIM_MSGPACK_RPC_DEFS_H #define NVIM_MSGPACK_RPC_DEFS_H -#include <msgpack.h> - /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// functions of this type. @@ -24,22 +22,6 @@ void msgpack_rpc_add_method_handler(String method, void msgpack_rpc_init_function_metadata(Dictionary *metadata); -/// Dispatches to the actual API function after basic payload validation by -/// `msgpack_rpc_call`. It is responsible for validating/converting arguments -/// to C types, and converting the return value back to msgpack types. -/// The implementation is generated at compile time with metadata extracted -/// from the api/*.h headers, -/// -/// @param channel_id The channel id -/// @param method_id The method id -/// @param req The parsed request object -/// @param error Pointer to error structure -/// @return Some object -Object msgpack_rpc_dispatch(uint64_t channel_id, - msgpack_object *req, - Error *error) - FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); - MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len) FUNC_ATTR_NONNULL_ARG(1); diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index a119c4653a..0049ae6b95 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -94,10 +94,9 @@ bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) FUNC_ATTR_NONNULL_ALL { if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { - if (obj->via.bin.ptr == NULL) { - return false; - } - arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size); + arg->data = obj->via.bin.ptr != NULL + ? xmemdupz(obj->via.bin.ptr, obj->via.bin.size) + : NULL; arg->size = obj->via.bin.size; return true; } @@ -420,8 +419,8 @@ void msgpack_rpc_validate(uint64_t *response_id, return; } - if ((type == kMessageTypeRequest && req->via.array.size != 4) || - (type == kMessageTypeNotification && req->via.array.size != 3)) { + if ((type == kMessageTypeRequest && req->via.array.size != 4) + || (type == kMessageTypeNotification && req->via.array.size != 3)) { api_set_error(err, Validation, _("Request array size should be 4 (request) " "or 3 (notification)")); return; diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 474e25ffeb..6cc56ba3dd 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -14,7 +14,7 @@ #include "nvim/vim.h" #include "nvim/memory.h" #include "nvim/log.h" -#include "nvim/tempfile.h" +#include "nvim/fileio.h" #include "nvim/path.h" #include "nvim/strings.h" @@ -59,7 +59,7 @@ static void set_vservername(garray_T *srvs) char *default_server = (srvs->ga_len > 0) ? ((SocketWatcher **)srvs->ga_data)[0]->addr : NULL; - set_vim_var_string(VV_SEND_SERVER, (char_u *)default_server, -1); + set_vim_var_string(VV_SEND_SERVER, default_server, -1); } /// Teardown the server module diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ad53e9bf24..f5607f3676 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -569,36 +569,33 @@ static bool normal_need_aditional_char(NormalState *s) static bool normal_need_redraw_mode_message(NormalState *s) { return ( - ( // 'showmode' is set and messages can be printed - p_smd && msg_silent == 0 - // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual - // mode - && (restart_edit != 0 || (VIsual_active - && s->old_pos.lnum == curwin->w_cursor.lnum - && s->old_pos.col == curwin->w_cursor.col)) - // command-line must be cleared or redrawn - && (clear_cmdline || redraw_cmdline) - // some message was printed or scrolled - && (msg_didout || (msg_didany && msg_scroll)) - // it is fine to remove the current message - && !msg_nowait - // the command was the result of direct user input and not a mapping - && KeyTyped - ) - || - // must restart insert mode, not in visual mode and error message is - // being shown - (restart_edit != 0 && !VIsual_active && (msg_scroll && emsg_on_display)) - ) - // no register was used - && s->oa.regname == 0 - && !(s->ca.retval & CA_COMMAND_BUSY) - && stuff_empty() - && typebuf_typed() - && emsg_silent == 0 - && !did_wait_return - && s->oa.op_type == OP_NOP; + ((p_smd && msg_silent == 0 + // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual + // mode + && (restart_edit != 0 || (VIsual_active + && s->old_pos.lnum == curwin->w_cursor.lnum + && s->old_pos.col == curwin->w_cursor.col)) + // command-line must be cleared or redrawn + && (clear_cmdline || redraw_cmdline) + // some message was printed or scrolled + && (msg_didout || (msg_didany && msg_scroll)) + // it is fine to remove the current message + && !msg_nowait + // the command was the result of direct user input and not a mapping + && KeyTyped) + // must restart insert mode, not in visual mode and error message is + // being shown + || (restart_edit != 0 && !VIsual_active && msg_scroll + && emsg_on_display)) + // no register was used + && s->oa.regname == 0 + && !(s->ca.retval & CA_COMMAND_BUSY) + && stuff_empty() + && typebuf_typed() + && emsg_silent == 0 + && !did_wait_return + && s->oa.op_type == OP_NOP); } static void normal_redraw_mode_message(NormalState *s) @@ -1205,9 +1202,9 @@ static void normal_check_interrupt(NormalState *s) static void normal_check_cursor_moved(NormalState *s) { // Trigger CursorMoved if the cursor moved. - if (!finish_op && (has_cursormoved() || curwin->w_p_cole > 0) + if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0) && !equalpos(last_cursormoved, curwin->w_cursor)) { - if (has_cursormoved()) { + if (has_event(EVENT_CURSORMOVED)) { apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf); } @@ -1224,7 +1221,7 @@ static void normal_check_cursor_moved(NormalState *s) static void normal_check_text_changed(NormalState *s) { // Trigger TextChanged if b_changedtick differs. - if (!finish_op && has_textchanged() + if (!finish_op && has_event(EVENT_TEXTCHANGED) && last_changedtick != curbuf->b_changedtick) { if (last_changedtick_buf == curbuf) { apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf); @@ -1414,11 +1411,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) int lbr_saved = curwin->w_p_lbr; - /* The visual area is remembered for redo */ - static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */ - static linenr_T redo_VIsual_line_count; /* number of lines */ - static colnr_T redo_VIsual_vcol; /* number of cols or end column */ - static long redo_VIsual_count; /* count for Visual operator */ + // The visual area is remembered for redo + static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V + static linenr_T redo_VIsual_line_count; // number of lines + static colnr_T redo_VIsual_vcol; // number of cols or end column + static long redo_VIsual_count; // count for Visual operator + static int redo_VIsual_arg; // extra argument bool include_line_break = false; old_cursor = curwin->w_cursor; @@ -1430,21 +1428,25 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) || VIsual_active ) && oap->op_type != OP_NOP) { // Avoid a problem with unwanted linebreaks in block mode + if (curwin->w_p_lbr) { + curwin->w_valid &= ~VALID_VIRTCOL; + } curwin->w_p_lbr = false; oap->is_VIsual = VIsual_active; - if (oap->motion_force == 'V') - oap->motion_type = MLINE; - else if (oap->motion_force == 'v') { - /* If the motion was linewise, "inclusive" will not have been set. - * Use "exclusive" to be consistent. Makes "dvj" work nice. */ - if (oap->motion_type == MLINE) + if (oap->motion_force == 'V') { + oap->motion_type = kMTLineWise; + } else if (oap->motion_force == 'v') { + // If the motion was linewise, "inclusive" will not have been set. + // Use "exclusive" to be consistent. Makes "dvj" work nice. + if (oap->motion_type == kMTLineWise) { oap->inclusive = false; - /* If the motion already was characterwise, toggle "inclusive" */ - else if (oap->motion_type == MCHAR) + } else if (oap->motion_type == kMTCharWise) { + // If the motion already was characterwise, toggle "inclusive" oap->inclusive = !oap->inclusive; - oap->motion_type = MCHAR; + } + oap->motion_type = kMTCharWise; } else if (oap->motion_force == Ctrl_V) { - /* Change line- or characterwise motion into Visual block mode. */ + // Change line- or characterwise motion into Visual block mode. VIsual_active = true; VIsual = oap->start; VIsual_mode = Ctrl_V; @@ -1534,9 +1536,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) curbuf->b_visual_mode_eval = VIsual_mode; } - /* In Select mode, a linewise selection is operated upon like a - * characterwise selection. */ - if (VIsual_select && VIsual_mode == 'V') { + // In Select mode, a linewise selection is operated upon like a + // characterwise selection. + // Special case: gH<Del> deletes the last line. + if (VIsual_select && VIsual_mode == 'V' + && cap->oap->op_type != OP_DELETE) { if (lt(VIsual, curwin->w_cursor)) { VIsual.col = 0; curwin->w_cursor.col = @@ -1580,13 +1584,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * automatically. */ curwin->w_valid &= ~VALID_VIRTCOL; } else { - /* Include folded lines completely. */ - if (!VIsual_active && oap->motion_type == MLINE) { + // Include folded lines completely. + if (!VIsual_active && oap->motion_type == kMTLineWise) { if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, - NULL)) + NULL)) { curwin->w_cursor.col = 0; - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) + } + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } } oap->end = oap->start; oap->start = curwin->w_cursor; @@ -1598,55 +1604,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) virtual_op = virtual_active(); if (VIsual_active || redo_VIsual_busy) { - if (VIsual_mode == Ctrl_V) { /* block mode */ - colnr_T start, end; - - oap->block_mode = true; - - getvvcol(curwin, &(oap->start), - &oap->start_vcol, NULL, &oap->end_vcol); - if (!redo_VIsual_busy) { - getvvcol(curwin, &(oap->end), &start, NULL, &end); - - if (start < oap->start_vcol) - oap->start_vcol = start; - if (end > oap->end_vcol) { - if (*p_sel == 'e' && start >= 1 - && start - 1 >= oap->end_vcol) - oap->end_vcol = start - 1; - else - oap->end_vcol = end; - } - } - - /* if '$' was used, get oap->end_vcol from longest line */ - if (curwin->w_curswant == MAXCOL) { - curwin->w_cursor.col = MAXCOL; - oap->end_vcol = 0; - for (curwin->w_cursor.lnum = oap->start.lnum; - curwin->w_cursor.lnum <= oap->end.lnum; - ++curwin->w_cursor.lnum) { - getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); - if (end > oap->end_vcol) - oap->end_vcol = end; - } - } else if (redo_VIsual_busy) - oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; - /* - * Correct oap->end.col and oap->start.col to be the - * upper-left and lower-right corner of the block area. - * - * (Actually, this does convert column positions into character - * positions) - */ - curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); - oap->end = curwin->w_cursor; - - curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); - oap->start = curwin->w_cursor; - } + get_op_vcol(oap, redo_VIsual_vcol, true); if (!redo_VIsual_busy && !gui_yank) { /* @@ -1701,38 +1659,33 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) redo_VIsual_vcol = resel_VIsual_vcol; redo_VIsual_line_count = resel_VIsual_line_count; redo_VIsual_count = cap->count0; + redo_VIsual_arg = cap->arg; } } - /* - * oap->inclusive defaults to true. - * If oap->end is on a NUL (empty line) oap->inclusive becomes - * false. This makes "d}P" and "v}dP" work the same. - */ - if (oap->motion_force == NUL || oap->motion_type == MLINE) + // oap->inclusive defaults to true. + // If oap->end is on a NUL (empty line) oap->inclusive becomes + // false. This makes "d}P" and "v}dP" work the same. + if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { oap->inclusive = true; - if (VIsual_mode == 'V') - oap->motion_type = MLINE; - else { - oap->motion_type = MCHAR; - if (VIsual_mode != Ctrl_V && *ml_get_pos(&(oap->end)) == NUL + } + if (VIsual_mode == 'V') { + oap->motion_type = kMTLineWise; + } else if (VIsual_mode == 'v') { + oap->motion_type = kMTCharWise; + if (*ml_get_pos(&(oap->end)) == NUL && (include_line_break || !virtual_op) ) { oap->inclusive = false; - /* Try to include the newline, unless it's an operator - * that works on lines only. */ - if (*p_sel != 'o' && !op_on_lines(oap->op_type)) { - if (oap->end.lnum < curbuf->b_ml.ml_line_count) { - ++oap->end.lnum; - oap->end.col = 0; - oap->end.coladd = 0; - ++oap->line_count; - } else { - /* Cannot move below the last line, make the op - * inclusive to tell the operation to include the - * line break. */ - oap->inclusive = true; - } + // Try to include the newline, unless it's an operator + // that works on lines only. + if (*p_sel != 'o' + && !op_on_lines(oap->op_type) + && oap->end.lnum < curbuf->b_ml.ml_line_count) { + oap->end.lnum++; + oap->end.col = 0; + oap->end.coladd = 0; + oap->line_count++; } } } @@ -1750,10 +1703,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) VIsual_active = false; setmouse(); mouse_dragging = 0; - if (mode_displayed) - clear_cmdline = true; /* unshow visual mode later */ - else - clear_showcmd(); + may_clear_cmdline(); if ((oap->op_type == OP_YANK || oap->op_type == OP_COLON || oap->op_type == OP_FUNCTION @@ -1780,7 +1730,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * oap->empty is set when start and end are the same. The inclusive * flag affects this too, unless yanking and the end is on a NUL. */ - oap->empty = (oap->motion_type == MCHAR + oap->empty = (oap->motion_type != kMTLineWise && (!oap->inclusive || (oap->op_type == OP_YANK && gchar_pos(&oap->end) == NUL)) @@ -1805,24 +1755,23 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) /* * If the end of an operator is in column one while oap->motion_type - * is MCHAR and oap->inclusive is false, we put op_end after the last + * is kMTCharWise and oap->inclusive is false, we put op_end after the last * character in the previous line. If op_start is on or before the * first non-blank in the line, the operator becomes linewise * (strange, but that's the way vi does it). */ - if ( oap->motion_type == MCHAR - && oap->inclusive == false - && !(cap->retval & CA_NO_ADJ_OP_END) - && oap->end.col == 0 - && (!oap->is_VIsual || *p_sel == 'o') - && !oap->block_mode - && oap->line_count > 1) { - oap->end_adjusted = true; /* remember that we did this */ - --oap->line_count; - --oap->end.lnum; - if (inindent(0)) - oap->motion_type = MLINE; - else { + if (oap->motion_type == kMTCharWise + && oap->inclusive == false + && !(cap->retval & CA_NO_ADJ_OP_END) + && oap->end.col == 0 + && (!oap->is_VIsual || *p_sel == 'o') + && oap->line_count > 1) { + oap->end_adjusted = true; // remember that we did this + oap->line_count--; + oap->end.lnum--; + if (inindent(0)) { + oap->motion_type = kMTLineWise; + } else { oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); if (oap->end.col) { --oap->end.col; @@ -1861,8 +1810,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) CancelRedo(); } else { (void)op_delete(oap); - if (oap->motion_type == MLINE && has_format_option(FO_AUTO)) - u_save_cursor(); /* cursor line wasn't saved yet */ + if (oap->motion_type == kMTLineWise && has_format_option(FO_AUTO)) { + u_save_cursor(); // cursor line wasn't saved yet + } auto_format(false, true); } break; @@ -1895,9 +1845,14 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) else restart_edit_save = 0; restart_edit = 0; + // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - /* Reset finish_op now, don't want it set inside edit(). */ + if (curwin->w_p_lbr != lbr_saved) { + curwin->w_p_lbr = lbr_saved; + get_op_vcol(oap, redo_VIsual_mode, false); + } + + // Reset finish_op now, don't want it set inside edit(). finish_op = false; if (op_change(oap)) /* will call edit() */ cap->retval |= CA_COMMAND_BUSY; @@ -1975,7 +1930,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) restart_edit = 0; // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; + if (curwin->w_p_lbr != lbr_saved) { + curwin->w_p_lbr = lbr_saved; + get_op_vcol(oap, redo_VIsual_mode, false); + } op_insert(oap, cap->count1); @@ -1998,7 +1956,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) CancelRedo(); } else { // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; + if (curwin->w_p_lbr != lbr_saved) { + curwin->w_p_lbr = lbr_saved; + get_op_vcol(oap, redo_VIsual_mode, false); + } + op_replace(oap, cap->nchar); } break; @@ -2027,6 +1989,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) deleteFold(oap->start.lnum, oap->end.lnum, oap->op_type == OP_FOLDDELREC, oap->is_VIsual); break; + + case OP_NR_ADD: + case OP_NR_SUB: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + VIsual_active = true; + curwin->w_p_lbr = lbr_saved; + op_addsub(oap, cap->count1, redo_VIsual_arg); + VIsual_active = false; + } + check_cursor_col(); + break; default: clearopbeep(oap); } @@ -2035,7 +2011,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) /* * if 'sol' not set, go back to old column for some commands */ - if (!p_sol && oap->motion_type == MLINE && !oap->end_adjusted + if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT || oap->op_type == OP_DELETE)) { curwin->w_p_lbr = false; @@ -2044,7 +2020,6 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { curwin->w_cursor = old_cursor; } - oap->block_mode = false; clearop(oap); } curwin->w_p_lbr = lbr_saved; @@ -2111,16 +2086,18 @@ static void op_function(oparg_T *oap) /* Set '[ and '] marks to text to be operated on. */ curbuf->b_op_start = oap->start; curbuf->b_op_end = oap->end; - if (oap->motion_type != MLINE && !oap->inclusive) - /* Exclude the end position. */ + if (oap->motion_type != kMTLineWise && !oap->inclusive) { + // Exclude the end position. decl(&curbuf->b_op_end); + } - if (oap->block_mode) + if (oap->motion_type == kMTBlockWise) { argv[0] = (char_u *)"block"; - else if (oap->motion_type == MLINE) + } else if (oap->motion_type == kMTLineWise) { argv[0] = (char_u *)"line"; - else + } else { argv[0] = (char_u *)"char"; + } /* Reset virtual_op so that 'virtualedit' can be changed in the * function. */ @@ -2368,8 +2345,12 @@ do_mouse ( if (mouse_row == 0 && firstwin->w_winrow > 0) { if (is_drag) { if (in_tab_line) { - c1 = TabPageIdxs[mouse_col]; - tabpage_move(c1 <= 0 ? 9999 : c1 - 1); + if (tab_page_click_defs[mouse_col].type == kStlClickTabClose) { + tabpage_move(9999); + } else { + int tabnr = tab_page_click_defs[mouse_col].tabnr; + tabpage_move(tabnr < tabpage_index(curtab) ? tabnr - 1 : tabnr); + } } return false; } @@ -2379,41 +2360,114 @@ do_mouse ( && cmdwin_type == 0 && mouse_col < Columns) { in_tab_line = true; - c1 = TabPageIdxs[mouse_col]; - if (c1 >= 0) { - if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { - /* double click opens new page */ - end_visual_mode(); - tabpage_new(); - tabpage_move(c1 == 0 ? 9999 : c1 - 1); - } else { - /* Go to specified tab page, or next one if not clicking - * on a label. */ - goto_tabpage(c1); - - /* It's like clicking on the status line of a window. */ - if (curwin != old_curwin) + c1 = tab_page_click_defs[mouse_col].tabnr; + switch (tab_page_click_defs[mouse_col].type) { + case kStlClickDisabled: { + break; + } + case kStlClickTabClose: { + tabpage_T *tp; + + // Close the current or specified tab page. + if (c1 == 999) { + tp = curtab; + } else { + tp = find_tabpage(c1); + } + if (tp == curtab) { + if (first_tabpage->tp_next != NULL) { + tabpage_close(false); + } + } else if (tp != NULL) { + tabpage_close_other(tp, false); + } + break; + } + case kStlClickTabSwitch: { + if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { + // double click opens new page end_visual_mode(); + tabpage_new(); + tabpage_move(c1 == 0 ? 9999 : c1 - 1); + } else { + // Go to specified tab page, or next one if not clicking + // on a label. + goto_tabpage(c1); + + // It's like clicking on the status line of a window. + if (curwin != old_curwin) { + end_visual_mode(); + } + } + break; + } + case kStlClickFuncRun: { + typval_T argv[] = { + { + .v_lock = VAR_FIXED, + .v_type = VAR_NUMBER, + .vval = { + .v_number = (varnumber_T) tab_page_click_defs[mouse_col].tabnr + }, + }, + { + .v_lock = VAR_FIXED, + .v_type = VAR_NUMBER, + .vval = { + .v_number = (((mod_mask & MOD_MASK_MULTI_CLICK) + == MOD_MASK_4CLICK) + ? 4 + : ((mod_mask & MOD_MASK_MULTI_CLICK) + == MOD_MASK_3CLICK) + ? 3 + : ((mod_mask & MOD_MASK_MULTI_CLICK) + == MOD_MASK_2CLICK) + ? 2 + : 1) + }, + }, + { + .v_lock = VAR_FIXED, + .v_type = VAR_STRING, + .vval = { .v_string = (char_u *) (which_button == MOUSE_LEFT + ? "l" + : which_button == MOUSE_RIGHT + ? "r" + : which_button == MOUSE_MIDDLE + ? "m" + : "?") }, + }, + { + .v_lock = VAR_FIXED, + .v_type = VAR_STRING, + .vval = { + .v_string = (char_u[]) { + (char_u) (mod_mask & MOD_MASK_SHIFT ? 's' : ' '), + (char_u) (mod_mask & MOD_MASK_CTRL ? 'c' : ' '), + (char_u) (mod_mask & MOD_MASK_ALT ? 'a' : ' '), + (char_u) (mod_mask & MOD_MASK_META ? 'm' : ' '), + NUL + } + }, + } + }; + typval_T rettv; + int doesrange; + (void) call_func((char_u *) tab_page_click_defs[mouse_col].func, + (int) strlen(tab_page_click_defs[mouse_col].func), + &rettv, ARRAY_SIZE(argv), argv, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &doesrange, true, NULL); + clear_tv(&rettv); + break; } - } else if (c1 < 0) { - tabpage_T *tp; - - /* Close the current or specified tab page. */ - if (c1 == -999) - tp = curtab; - else - tp = find_tabpage(-c1); - if (tp == curtab) { - if (first_tabpage->tp_next != NULL) - tabpage_close(false); - } else if (tp != NULL) - tabpage_close_other(tp, false); } } return true; } else if (is_drag && in_tab_line) { - c1 = TabPageIdxs[mouse_col]; - tabpage_move(c1 <= 0 ? 9999 : c1 - 1); + tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose + ? 9999 + : tab_page_click_defs[mouse_col].tabnr - 1); in_tab_line = false; return false; } @@ -2477,7 +2531,7 @@ do_mouse ( */ if (!is_drag && oap != NULL && oap->op_type != OP_NOP) { got_click = false; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } /* When releasing the button let jump_to_mouse() know. */ @@ -2545,11 +2599,10 @@ do_mouse ( end_visual.col = leftcol; else end_visual.col = rightcol; - if (curwin->w_cursor.lnum < - (start_visual.lnum + end_visual.lnum) / 2) - end_visual.lnum = end_visual.lnum; - else + if (curwin->w_cursor.lnum >= + (start_visual.lnum + end_visual.lnum) / 2) { end_visual.lnum = start_visual.lnum; + } /* move VIsual to the right column */ start_visual = curwin->w_cursor; /* save the cursor pos */ @@ -2717,21 +2770,23 @@ do_mouse ( end_visual = curwin->w_cursor; while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) inc(&end_visual); - if (oap != NULL) - oap->motion_type = MCHAR; + if (oap != NULL) { + oap->motion_type = kMTCharWise; + } if (oap != NULL && VIsual_mode == 'v' && !vim_iswordc(gchar_pos(&end_visual)) && equalpos(curwin->w_cursor, VIsual) && (pos = findmatch(oap, NUL)) != NULL) { curwin->w_cursor = *pos; - if (oap->motion_type == MLINE) + if (oap->motion_type == kMTLineWise) { VIsual_mode = 'V'; - else if (*p_sel == 'e') { - if (lt(curwin->w_cursor, VIsual)) - ++VIsual.col; - else - ++curwin->w_cursor.col; + } else if (*p_sel == 'e') { + if (lt(curwin->w_cursor, VIsual)) { + VIsual.col++; + } else { + curwin->w_cursor.col++; + } } } } @@ -2886,10 +2941,7 @@ void end_visual_mode(void) if (!virtual_active()) curwin->w_cursor.coladd = 0; - if (mode_displayed) - clear_cmdline = true; /* unshow visual mode later */ - else - clear_showcmd(); + may_clear_cmdline(); adjust_cursor_eol(); } @@ -3155,10 +3207,19 @@ static void unshift_special(cmdarg_T *cap) cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask); } -/* - * Routines for displaying a partly typed command - */ +/// If the mode is currently displayed clear the command line or update the +/// command displayed. +static void may_clear_cmdline(void) +{ + if (mode_displayed) { + // unshow visual mode later + clear_cmdline = true; + } else { + clear_showcmd(); + } +} +// Routines for displaying a partly typed command # define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30 static char_u showcmd_buf[SHOWCMD_BUFLEN]; static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; /* For push_showcmd() */ @@ -3185,9 +3246,9 @@ void clear_showcmd(void) top = curwin->w_cursor.lnum; bot = VIsual.lnum; } - /* Include closed folds as a whole. */ - hasFolding(top, &top, NULL); - hasFolding(bot, NULL, &bot); + // Include closed folds as a whole. + (void)hasFolding(top, &top, NULL); + (void)hasFolding(bot, NULL, &bot); lines = bot - top + 1; if (VIsual_mode == Ctrl_V) { @@ -3537,9 +3598,16 @@ static void nv_help(cmdarg_T *cap) */ static void nv_addsub(cmdarg_T *cap) { - if (!checkclearopq(cap->oap) - && do_addsub(cap->cmdchar, cap->count1)) + if (!VIsual_active && cap->oap->op_type == OP_NOP) { prep_redo_cmd(cap); + cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB; + op_addsub(cap->oap, cap->count1, cap->arg); + cap->oap->op_type = OP_NOP; + } else if (VIsual_active) { + nv_operator(cap); + } else { + clearop(cap->oap); + } } /* @@ -3716,7 +3784,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) int width1; /* text width for first screen line */ int width2; /* test width for wrapped screen line */ - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = (curwin->w_curswant == MAXCOL); col_off1 = curwin_col_off(); @@ -3861,6 +3929,8 @@ static void nv_mousescroll(cmdarg_T *cap) cap->count0 = 3; nv_scroll_line(cap); } + } else { + mouse_scroll_horiz(cap->arg); } curwin->w_redr_status = true; @@ -3987,16 +4057,15 @@ static void nv_zet(cmdarg_T *cap) } dozet: - if ( - /* "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc" - * and "zC" only in Visual mode. "zj" and "zk" are motion - * commands. */ - cap->nchar != 'f' && cap->nchar != 'F' - && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar)) - && cap->nchar != 'j' && cap->nchar != 'k' - && - checkclearop(cap->oap)) + // "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc" + // and "zC" only in Visual mode. "zj" and "zk" are motion + // commands. */ + if (cap->nchar != 'f' && cap->nchar != 'F' + && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar)) + && cap->nchar != 'j' && cap->nchar != 'k' + && checkclearop(cap->oap)) { return; + } /* * For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb": @@ -4033,6 +4102,7 @@ dozet: case 't': scroll_cursor_top(0, true); redraw_later(VALID); + set_fraction(curwin); break; /* "z." and "zz": put cursor in middle of screen */ @@ -4041,6 +4111,7 @@ dozet: case 'z': scroll_cursor_halfway(true); redraw_later(VALID); + set_fraction(curwin); break; /* "z^", "z-" and "zb": put cursor at bottom of screen */ @@ -4061,6 +4132,7 @@ dozet: case 'b': scroll_cursor_bot(0, true); redraw_later(VALID); + set_fraction(curwin); break; /* "zH" - scroll screen right half-page */ @@ -4394,8 +4466,8 @@ static void nv_colon(cmdarg_T *cap) nv_operator(cap); else { if (cap->oap->op_type != OP_NOP) { - /* Using ":" as a movement is characterwise exclusive. */ - cap->oap->motion_type = MCHAR; + // Using ":" as a movement is characterwise exclusive. + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; } else if (cap->count0) { /* translate "count:" into ":.,.+(count - 1)" */ @@ -4794,7 +4866,7 @@ static void nv_scroll(cmdarg_T *cap) linenr_T lnum; int half; - cap->oap->motion_type = MLINE; + cap->oap->motion_type = kMTLineWise; setpcmark(); if (cap->cmdchar == 'L') { @@ -4874,7 +4946,7 @@ static void nv_right(cmdarg_T *cap) return; } - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; PAST_LINE = (VIsual_active && *p_sel != 'o'); @@ -4961,7 +5033,7 @@ static void nv_left(cmdarg_T *cap) return; } - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; for (n = cap->count1; n > 0; --n) { if (oneleft() == false) { @@ -5023,11 +5095,12 @@ static void nv_up(cmdarg_T *cap) cap->arg = BACKWARD; nv_page(cap); } else { - cap->oap->motion_type = MLINE; - if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) + cap->oap->motion_type = kMTLineWise; + if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else if (cap->arg) + } else if (cap->arg) { beginline(BL_WHITE | BL_FIX); + } } } @@ -5041,23 +5114,24 @@ static void nv_down(cmdarg_T *cap) /* <S-Down> is page down */ cap->arg = FORWARD; nv_page(cap); - } else - /* In a quickfix window a <CR> jumps to the error under the cursor. */ - if (bt_quickfix(curbuf) && cap->cmdchar == CAR) - if (curwin->w_llist_ref == NULL) - do_cmdline_cmd(".cc"); /* quickfix window */ - else - do_cmdline_cmd(".ll"); /* location list window */ - else { - /* In the cmdline window a <CR> executes the command. */ - if (cmdwin_type != 0 && cap->cmdchar == CAR) + } else if (bt_quickfix(curbuf) && cap->cmdchar == CAR) { + // In a quickfix window a <CR> jumps to the error under the cursor. + if (curwin->w_llist_ref == NULL) { + do_cmdline_cmd(".cc"); // quickfix window + } else { + do_cmdline_cmd(".ll"); // location list window + } + } else { + // In the cmdline window a <CR> executes the command. + if (cmdwin_type != 0 && cap->cmdchar == CAR) { cmdwin_result = CAR; - else { - cap->oap->motion_type = MLINE; - if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) + } else { + cap->oap->motion_type = kMTLineWise; + if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else if (cap->arg) + } else if (cap->arg) { beginline(BL_WHITE | BL_FIX); + } } } } @@ -5083,9 +5157,10 @@ static void nv_gotofile(cmdarg_T *cap) ptr = grab_file_name(cap->count1, &lnum); if (ptr != NULL) { - /* do autowrite if necessary */ - if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) - autowrite(curbuf, false); + // do autowrite if necessary + if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) { + (void)autowrite(curbuf, false); + } setpcmark(); (void)do_ecmd(0, ptr, NULL, NULL, ECMD_LAST, P_HID(curbuf) ? ECMD_HIDE : 0, curwin); @@ -5117,7 +5192,7 @@ static void nv_end(cmdarg_T *cap) */ static void nv_dollar(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = true; /* In virtual mode when off the edge of a line and an operator * is pending (whew!) keep the cursor where it is. @@ -5192,18 +5267,19 @@ static int normal_search( { int i; - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; cap->oap->use_reg_one = true; curwin->w_set_curswant = true; i = do_search(cap->oap, dir, pat, cap->count1, - opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL); - if (i == 0) + opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL); + if (i == 0) { clearop(cap->oap); - else { - if (i == 2) - cap->oap->motion_type = MLINE; + } else { + if (i == 2) { + cap->oap->motion_type = kMTLineWise; + } curwin->w_cursor.coladd = 0; if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) foldOpenCursor(); @@ -5230,10 +5306,10 @@ static void nv_csearch(cmdarg_T *cap) else t_cmd = false; - cap->oap->motion_type = MCHAR; - if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) + cap->oap->motion_type = kMTCharWise; + if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) { clearopbeep(cap->oap); - else { + } else { curwin->w_set_curswant = true; /* Include a Tab for "tx" and for "dfx". */ if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD @@ -5265,7 +5341,7 @@ static void nv_brackets(cmdarg_T *cap) int findc; int c; - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; old_pos = curwin->w_cursor; curwin->w_cursor.coladd = 0; /* TODO: don't do this for an error. */ @@ -5555,11 +5631,11 @@ static void nv_percent(cmdarg_T *cap) linenr_T lnum = curwin->w_cursor.lnum; cap->oap->inclusive = true; - if (cap->count0) { /* {cnt}% : goto {cnt} percentage in file */ - if (cap->count0 > 100) + if (cap->count0) { // {cnt}% : goto {cnt} percentage in file + if (cap->count0 > 100) { clearopbeep(cap->oap); - else { - cap->oap->motion_type = MLINE; + } else { + cap->oap->motion_type = kMTLineWise; setpcmark(); /* Round up, so CTRL-G will give same value. Watch out for a * large line count, the line number must not go negative! */ @@ -5573,8 +5649,8 @@ static void nv_percent(cmdarg_T *cap) curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; beginline(BL_SOL | BL_FIX); } - } else { /* "%" : go to matching paren */ - cap->oap->motion_type = MCHAR; + } else { // "%" : go to matching paren + cap->oap->motion_type = kMTCharWise; cap->oap->use_reg_one = true; if ((pos = findmatch(cap->oap, NUL)) == NULL) clearopbeep(cap->oap); @@ -5599,7 +5675,7 @@ static void nv_percent(cmdarg_T *cap) */ static void nv_brace(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->use_reg_one = true; /* The motion used to be inclusive for "(", but that is not what Vi does. */ cap->oap->inclusive = false; @@ -5633,7 +5709,7 @@ static void nv_mark(cmdarg_T *cap) */ static void nv_findpar(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; cap->oap->use_reg_one = true; curwin->w_set_curswant = true; @@ -5737,14 +5813,11 @@ static void nv_replace(cmdarg_T *cap) return; } - /* - * Replacing with a TAB is done by edit() when it is complicated because - * 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB. - * Other characters are done below to avoid problems with things like - * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC). - */ - if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && - (curbuf->b_p_et || p_sta)) { + // Replacing with a TAB is done by edit() when it is complicated because + // 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB. + // Other characters are done below to avoid problems with things like + // CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC). + if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta)) { stuffnumReadbuff(cap->count1); stuffcharReadbuff('R'); stuffcharReadbuff('\t'); @@ -6011,10 +6084,11 @@ static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos) else check_cursor(); } - cap->oap->motion_type = flag ? MLINE : MCHAR; - if (cap->cmdchar == '`') + cap->oap->motion_type = flag ? kMTLineWise : kMTCharWise; + if (cap->cmdchar == '`') { cap->oap->use_reg_one = true; - cap->oap->inclusive = false; /* ignored if not MCHAR */ + } + cap->oap->inclusive = false; // ignored if not kMTCharWise curwin->w_set_curswant = true; } @@ -6361,9 +6435,20 @@ static void nv_g_cmd(cmdarg_T *cap) bool flag = false; switch (cap->nchar) { - /* - * "gR": Enter virtual replace mode. - */ + // "g^A/g^X": Sequentially increment visually selected region. + case Ctrl_A: + case Ctrl_X: + if (VIsual_active) { + cap->arg = true; + cap->cmdchar = cap->nchar; + cap->nchar = NUL; + nv_addsub(cap); + } else { + clearopbeep(oap); + } + break; + + // "gR": Enter virtual replace mode. case 'R': cap->arg = true; nv_Replace(cap); @@ -6480,7 +6565,7 @@ static void nv_g_cmd(cmdarg_T *cap) if (!curwin->w_p_wrap || hasFolding(curwin->w_cursor.lnum, NULL, NULL) ) { - oap->motion_type = MLINE; + oap->motion_type = kMTLineWise; i = cursor_down(cap->count1, oap->op_type == OP_NOP); } else i = nv_screengo(oap, FORWARD, cap->count1); @@ -6495,7 +6580,7 @@ static void nv_g_cmd(cmdarg_T *cap) if (!curwin->w_p_wrap || hasFolding(curwin->w_cursor.lnum, NULL, NULL) ) { - oap->motion_type = MLINE; + oap->motion_type = kMTLineWise; i = cursor_up(cap->count1, oap->op_type == OP_NOP); } else i = nv_screengo(oap, BACKWARD, cap->count1); @@ -6522,7 +6607,7 @@ static void nv_g_cmd(cmdarg_T *cap) case 'm': case K_HOME: case K_KHOME: - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = false; if (curwin->w_p_wrap && curwin->w_width != 0 @@ -6555,7 +6640,7 @@ static void nv_g_cmd(cmdarg_T *cap) case '_': /* "g_": to the last non-blank character in the line or <count> lines * downward. */ - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = true; curwin->w_curswant = MAXCOL; if (cursor_down(cap->count1 - 1, @@ -6583,7 +6668,7 @@ static void nv_g_cmd(cmdarg_T *cap) { int col_off = curwin_col_off(); - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = true; if (curwin->w_p_wrap && curwin->w_width != 0 @@ -6645,23 +6730,19 @@ static void nv_g_cmd(cmdarg_T *cap) */ case 'e': case 'E': - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; curwin->w_set_curswant = true; oap->inclusive = true; if (bckend_word(cap->count1, cap->nchar == 'E', false) == false) clearopbeep(oap); break; - /* - * "g CTRL-G": display info about cursor position - */ + // "g CTRL-G": display info about cursor position case Ctrl_G: - cursor_pos_info(); + cursor_pos_info(NULL); break; - /* - * "gi": start Insert at the last position. - */ + // "gi": start Insert at the last position. case 'i': if (curbuf->b_last_insert.mark.lnum != 0) { curwin->w_cursor = curbuf->b_last_insert.mark; @@ -6878,10 +6959,16 @@ static void n_opencmd(cmdarg_T *cap) (cap->cmdchar == 'o' ? 1 : 0)) ) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, - has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : - 0, 0)) { - if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum) + has_format_option(FO_OPEN_COMS) + ? OPENLINE_DO_COM : 0, + 0)) { + if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum) { update_single_line(curwin, oldline); + } + if (curwin->w_p_cul) { + // force redraw of cursorline + curwin->w_valid &= ~VALID_CROW; + } invoke_edit(cap, false, cap->cmdchar, true); } } @@ -6971,18 +7058,17 @@ static void nv_operator(cmdarg_T *cap) */ static void set_op_var(int optype) { - char_u opchars[3]; - - if (optype == OP_NOP) + if (optype == OP_NOP) { set_vim_var_string(VV_OP, NULL, 0); - else { + } else { + char opchars[3]; int opchar0 = get_op_char(optype); assert(opchar0 >= 0 && opchar0 <= UCHAR_MAX); - opchars[0] = (char_u)opchar0; + opchars[0] = (char) opchar0; int opchar1 = get_extra_op_char(optype); assert(opchar1 >= 0 && opchar1 <= UCHAR_MAX); - opchars[1] = (char_u)opchar1; + opchars[1] = (char) opchar1; opchars[2] = NUL; set_vim_var_string(VV_OP, opchars, -1); @@ -7000,17 +7086,19 @@ static void set_op_var(int optype) */ static void nv_lineop(cmdarg_T *cap) { - cap->oap->motion_type = MLINE; - if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false) + cap->oap->motion_type = kMTLineWise; + if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else if ( (cap->oap->op_type == OP_DELETE /* only with linewise motions */ + } else if ((cap->oap->op_type == OP_DELETE + // only with linewise motions && cap->oap->motion_force != 'v' && cap->oap->motion_force != Ctrl_V) || cap->oap->op_type == OP_LSHIFT - || cap->oap->op_type == OP_RSHIFT) + || cap->oap->op_type == OP_RSHIFT) { beginline(BL_SOL | BL_FIX); - else if (cap->oap->op_type != OP_YANK) /* 'Y' does not move cursor */ + } else if (cap->oap->op_type != OP_YANK) { // 'Y' does not move cursor beginline(BL_WHITE | BL_FIX); + } } /* @@ -7034,7 +7122,7 @@ static void nv_home(cmdarg_T *cap) */ static void nv_pipe(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; beginline(0); if (cap->count0 > 0) { @@ -7053,7 +7141,7 @@ static void nv_pipe(cmdarg_T *cap) */ static void nv_bck_word(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; curwin->w_set_curswant = true; if (bck_word(cap->count1, cap->arg, false) == false) @@ -7102,7 +7190,7 @@ static void nv_wordcmd(cmdarg_T *cap) } } - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; curwin->w_set_curswant = true; if (word_end) n = end_word(cap->count1, cap->arg, flag, false); @@ -7153,7 +7241,7 @@ static void adjust_cursor(oparg_T *oap) */ static void nv_beginline(cmdarg_T *cap) { - cap->oap->motion_type = MCHAR; + cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; beginline(cap->arg); if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) @@ -7232,7 +7320,7 @@ static void nv_goto(cmdarg_T *cap) lnum = curbuf->b_ml.ml_line_count; else lnum = 1L; - cap->oap->motion_type = MLINE; + cap->oap->motion_type = kMTLineWise; setpcmark(); /* When a count is given, use it instead of the default lnum */ @@ -7466,6 +7554,13 @@ static void nv_object(cmdarg_T *cap) flag = current_block(cap->oap, cap->count1, include, '<', '>'); break; case 't': /* "at" = a tag block (xml and html) */ + // Do not adjust oap->end in do_pending_operator() + // otherwise there are different results for 'dit' + // (note leading whitespace in last line): + // 1) <b> 2) <b> + // foobar foobar + // </b> </b> + cap->retval |= CA_NO_ADJ_OP_END; flag = current_tagblock(cap->oap, cap->count1, include); break; case 'p': /* "ap" = a paragraph */ @@ -7658,6 +7753,10 @@ static void nv_put(cmdarg_T *cap) if (was_visual) { curbuf->b_visual.vi_start = curbuf->b_op_start; curbuf->b_visual.vi_end = curbuf->b_op_end; + // need to adjust cursor position + if (*p_sel == 'e') { + inc(&curbuf->b_visual.vi_end); + } } /* When all lines were selected and deleted do_put() leaves an empty @@ -7692,6 +7791,75 @@ static void nv_open(cmdarg_T *cap) n_opencmd(cap); } +// Calculate start/end virtual columns for operating in block mode. +static void get_op_vcol( + oparg_T *oap, + colnr_T redo_VIsual_vcol, + bool initial // when true: adjust position for 'selectmode' +) +{ + colnr_T start; + colnr_T end; + + if (VIsual_mode != Ctrl_V + || (!initial && oap->end.col < curwin->w_width)) { + return; + } + + oap->motion_type = kMTBlockWise; + + // prevent from moving onto a trail byte + if (has_mbyte) { + mb_adjustpos(curwin->w_buffer, &oap->end); + } + + getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); + if (!redo_VIsual_busy) { + getvvcol(curwin, &(oap->end), &start, NULL, &end); + + if (start < oap->start_vcol) { + oap->start_vcol = start; + } + if (end > oap->end_vcol) { + if (initial && *p_sel == 'e' + && start >= 1 + && start - 1 >= oap->end_vcol) { + oap->end_vcol = start - 1; + } else { + oap->end_vcol = end; + } + } + } + + // if '$' was used, get oap->end_vcol from longest line + if (curwin->w_curswant == MAXCOL) { + curwin->w_cursor.col = MAXCOL; + oap->end_vcol = 0; + for (curwin->w_cursor.lnum = oap->start.lnum; + curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); + if (end > oap->end_vcol) { + oap->end_vcol = end; + } + } + } else if (redo_VIsual_busy) { + oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; + } + + // Correct oap->end.col and oap->start.col to be the + // upper-left and lower-right corner of the block area. + // + // (Actually, this does convert column positions into character + // positions) + curwin->w_cursor.lnum = oap->end.lnum; + coladvance(oap->end_vcol); + oap->end = curwin->w_cursor; + + curwin->w_cursor = oap->start; + coladvance(oap->start_vcol); + oap->start = curwin->w_cursor; +} + // Handle an arbitrary event in normal mode static void nv_event(cmdarg_T *cap) { diff --git a/src/nvim/normal.h b/src/nvim/normal.h index 01259de6cd..51170105ed 100644 --- a/src/nvim/normal.h +++ b/src/nvim/normal.h @@ -10,32 +10,42 @@ #define FIND_STRING 2 /* find any string (WORD) */ #define FIND_EVAL 4 /* include "->", "[]" and "." */ +/// Motion types, used for operators and for yank/delete registers. +/// +/// The three valid numerical values must not be changed, as they +/// are used in external communication and serialization. +typedef enum { + kMTCharWise = 0, ///< character-wise movement/register + kMTLineWise = 1, ///< line-wise movement/register + kMTBlockWise = 2, ///< block-wise movement/register + kMTUnknown = -1 ///< Unknown or invalid motion type +} MotionType; + /* * Arguments for operators. */ typedef struct oparg_S { - int op_type; /* current pending operator type */ - int regname; /* register to use for the operator */ - int motion_type; /* type of the current cursor motion */ - int motion_force; /* force motion type: 'v', 'V' or CTRL-V */ - bool use_reg_one; /* true if delete uses reg 1 even when not - linewise */ - bool inclusive; /* true if char motion is inclusive (only - valid when motion_type is MCHAR */ - bool end_adjusted; /* backuped b_op_end one char (only used by - do_format()) */ - pos_T start; /* start of the operator */ - pos_T end; /* end of the operator */ - pos_T cursor_start; /* cursor position before motion for "gw" */ + int op_type; // current pending operator type + int regname; // register to use for the operator + MotionType motion_type; // type of the current cursor motion + int motion_force; // force motion type: 'v', 'V' or CTRL-V + bool use_reg_one; // true if delete uses reg 1 even when not + // linewise + bool inclusive; // true if char motion is inclusive (only + // valid when motion_type is kMTCharWise) + bool end_adjusted; // backuped b_op_end one char (only used by + // do_format()) + pos_T start; // start of the operator + pos_T end; // end of the operator + pos_T cursor_start; // cursor position before motion for "gw" - long line_count; /* number of lines from op_start to op_end - (inclusive) */ - bool empty; /* op_start and op_end the same (only used by - op_change()) */ - bool is_VIsual; /* operator on Visual area */ - bool block_mode; /* current operator is Visual block mode */ - colnr_T start_vcol; /* start col for block mode operator */ - colnr_T end_vcol; /* end col for block mode operator */ + long line_count; // number of lines from op_start to op_end + // (inclusive) + bool empty; // op_start and op_end the same (only used by + // op_change()) + bool is_VIsual; // operator on Visual area + colnr_T start_vcol; // start col for block mode operator + colnr_T end_vcol; // end col for block mode operator long prev_opcount; // ca.opcount saved for K_EVENT long prev_count0; // ca.count0 saved for K_EVENT } oparg_T; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 956b9c7758..eda963ff77 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -19,6 +19,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" +#include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/indent.h" @@ -87,34 +88,36 @@ struct block_def { */ static char opchars[][3] = { - {NUL, NUL, FALSE}, /* OP_NOP */ - {'d', NUL, FALSE}, /* OP_DELETE */ - {'y', NUL, FALSE}, /* OP_YANK */ - {'c', NUL, FALSE}, /* OP_CHANGE */ - {'<', NUL, TRUE}, /* OP_LSHIFT */ - {'>', NUL, TRUE}, /* OP_RSHIFT */ - {'!', NUL, TRUE}, /* OP_FILTER */ - {'g', '~', FALSE}, /* OP_TILDE */ - {'=', NUL, TRUE}, /* OP_INDENT */ - {'g', 'q', TRUE}, /* OP_FORMAT */ - {':', NUL, TRUE}, /* OP_COLON */ - {'g', 'U', FALSE}, /* OP_UPPER */ - {'g', 'u', FALSE}, /* OP_LOWER */ - {'J', NUL, TRUE}, /* DO_JOIN */ - {'g', 'J', TRUE}, /* DO_JOIN_NS */ - {'g', '?', FALSE}, /* OP_ROT13 */ - {'r', NUL, FALSE}, /* OP_REPLACE */ - {'I', NUL, FALSE}, /* OP_INSERT */ - {'A', NUL, FALSE}, /* OP_APPEND */ - {'z', 'f', TRUE}, /* OP_FOLD */ - {'z', 'o', TRUE}, /* OP_FOLDOPEN */ - {'z', 'O', TRUE}, /* OP_FOLDOPENREC */ - {'z', 'c', TRUE}, /* OP_FOLDCLOSE */ - {'z', 'C', TRUE}, /* OP_FOLDCLOSEREC */ - {'z', 'd', TRUE}, /* OP_FOLDDEL */ - {'z', 'D', TRUE}, /* OP_FOLDDELREC */ - {'g', 'w', TRUE}, /* OP_FORMAT2 */ - {'g', '@', FALSE}, /* OP_FUNCTION */ + { NUL, NUL, false }, // OP_NOP + { 'd', NUL, false }, // OP_DELETE + { 'y', NUL, false }, // OP_YANK + { 'c', NUL, false }, // OP_CHANGE + { '<', NUL, true }, // OP_LSHIFT + { '>', NUL, true }, // OP_RSHIFT + { '!', NUL, true }, // OP_FILTER + { 'g', '~', false }, // OP_TILDE + { '=', NUL, true }, // OP_INDENT + { 'g', 'q', true }, // OP_FORMAT + { ':', NUL, true }, // OP_COLON + { 'g', 'U', false }, // OP_UPPER + { 'g', 'u', false }, // OP_LOWER + { 'J', NUL, true }, // DO_JOIN + { 'g', 'J', true }, // DO_JOIN_NS + { 'g', '?', false }, // OP_ROT13 + { 'r', NUL, false }, // OP_REPLACE + { 'I', NUL, false }, // OP_INSERT + { 'A', NUL, false }, // OP_APPEND + { 'z', 'f', true }, // OP_FOLD + { 'z', 'o', true }, // OP_FOLDOPEN + { 'z', 'O', true }, // OP_FOLDOPENREC + { 'z', 'c', true }, // OP_FOLDCLOSE + { 'z', 'C', true }, // OP_FOLDCLOSEREC + { 'z', 'd', true }, // OP_FOLDDEL + { 'z', 'D', true }, // OP_FOLDDELREC + { 'g', 'w', true }, // OP_FORMAT2 + { 'g', '@', false }, // OP_FUNCTION + { Ctrl_A, NUL, false }, // OP_NR_ADD + { Ctrl_X, NUL, false }, // OP_NR_SUB }; /* @@ -125,13 +128,27 @@ int get_op_type(int char1, int char2) { int i; - if (char1 == 'r') /* ignore second character */ + if (char1 == 'r') { + // ignore second character return OP_REPLACE; - if (char1 == '~') /* when tilde is an operator */ + } + if (char1 == '~') { + // when tilde is an operator return OP_TILDE; - for (i = 0;; i++) - if (opchars[i][0] == char1 && opchars[i][1] == char2) + } + if (char1 == 'g' && char2 == Ctrl_A) { + // add + return OP_NR_ADD; + } + if (char1 == 'g' && char2 == Ctrl_X) { + // subtract + return OP_NR_SUB; + } + for (i = 0;; i++) { + if (opchars[i][0] == char1 && opchars[i][1] == char2) { break; + } + } return i; } @@ -174,20 +191,20 @@ void op_shift(oparg_T *oap, int curs_top, int amount) (linenr_T)(oap->end.lnum + 1)) == FAIL) return; - if (oap->block_mode) + if (oap->motion_type == kMTBlockWise) { block_col = curwin->w_cursor.col; + } for (i = oap->line_count - 1; i >= 0; i--) { first_char = *get_cursor_line_ptr(); - if (first_char == NUL) /* empty line */ + if (first_char == NUL) { // empty line curwin->w_cursor.col = 0; - else if (oap->block_mode) + } else if (oap->motion_type == kMTBlockWise) { shift_block(oap, amount); - else - /* Move the line right if it doesn't start with '#', 'smartindent' - * isn't set or 'cindent' isn't set or '#' isn't in 'cino'. */ - if (first_char != '#' || !preprocs_left()) { - shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, FALSE); + } else if (first_char != '#' || !preprocs_left()) { + // Move the line right if it doesn't start with '#', 'smartindent' + // isn't set or 'cindent' isn't set or '#' isn't in 'cino'. + shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, false); } ++curwin->w_cursor.lnum; } @@ -196,7 +213,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) /* The cursor line is not in a closed fold */ foldOpenCursor(); - if (oap->block_mode) { + if (oap->motion_type == kMTBlockWise) { curwin->w_cursor.lnum = oap->start.lnum; curwin->w_cursor.col = block_col; } else if (curs_top) { /* put cursor on first line, for ">>" */ @@ -554,7 +571,7 @@ void op_reindent(oparg_T *oap, Indenter how) { long i; char_u *l; - int count; + int amount; linenr_T first_changed = 0; linenr_T last_changed = 0; linenr_T start_lnum = curwin->w_cursor.lnum; @@ -582,11 +599,11 @@ void op_reindent(oparg_T *oap, Indenter how) || how != get_lisp_indent) { l = skipwhite(get_cursor_line_ptr()); if (*l == NUL) /* empty or blank line */ - count = 0; + amount = 0; else - count = how(); /* get the indent for this line */ + amount = how(); /* get the indent for this line */ - if (set_indent(count, SIN_UNDO)) { + if (amount >= 0 && set_indent(amount, SIN_UNDO)) { /* did change the indent, call changed_lines() later */ if (first_changed == 0) first_changed = curwin->w_cursor.lnum; @@ -703,17 +720,16 @@ char_u *get_expr_line_src(void) /// @param writing allow only writable registers bool valid_yank_reg(int regname, bool writing) { - if ( (regname > 0 && ASCII_ISALNUM(regname)) - || (!writing && vim_strchr((char_u *) - "/.%#:=" - , regname) != NULL) - || regname == '"' - || regname == '-' - || regname == '_' - || regname == '*' - || regname == '+' - ) + if ((regname > 0 && ASCII_ISALNUM(regname)) + || (!writing && vim_strchr((char_u *) "/.%:=" , regname) != NULL) + || regname == '#' + || regname == '"' + || regname == '-' + || regname == '_' + || regname == '*' + || regname == '+') { return true; + } return false; } @@ -795,17 +811,17 @@ yankreg_T *copy_register(int name) return copy; } -/* - * return TRUE if the current yank register has type MLINE - */ -int yank_register_mline(int regname) +/// check if the current yank register has kMTLineWise register type +bool yank_register_mline(int regname) { - if (regname != 0 && !valid_yank_reg(regname, false)) - return FALSE; - if (regname == '_') /* black hole is always empty */ - return FALSE; + if (regname != 0 && !valid_yank_reg(regname, false)) { + return false; + } + if (regname == '_') { // black hole is always empty + return false; + } yankreg_T *reg = get_yank_register(regname, YREG_PASTE); - return reg->y_type == MLINE; + return reg->y_type == kMTLineWise; } /* @@ -820,12 +836,13 @@ int do_record(int c) yankreg_T *old_y_previous; int retval; - if (Recording == FALSE) { /* start recording */ - /* registers 0-9, a-z and " are allowed */ - if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) + if (Recording == false) { + // start recording + // registers 0-9, a-z and " are allowed + if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) { retval = FAIL; - else { - Recording = TRUE; + } else { + Recording = c; showmode(); regname = c; retval = OK; @@ -902,7 +919,7 @@ static int stuff_yank(int regname, char_u *p) reg->y_array = (char_u **)xmalloc(sizeof(char_u *)); reg->y_array[0] = p; reg->y_size = 1; - reg->y_type = MCHAR; /* used to be MLINE, why? */ + reg->y_type = kMTCharWise; } reg->timestamp = os_time(); return OK; @@ -994,8 +1011,8 @@ do_execreg ( for (i = reg->y_size - 1; i >= 0; i--) { char_u *escaped; - /* insert NL between lines and after last line if type is MLINE */ - if (reg->y_type == MLINE || i < reg->y_size - 1 + // insert NL between lines and after last line if type is kMTLineWise + if (reg->y_type == kMTLineWise || i < reg->y_size - 1 || addcr) { if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL) return FAIL; @@ -1120,12 +1137,11 @@ insert_reg ( else { for (i = 0; i < reg->y_size; i++) { stuffescaped(reg->y_array[i], literally); - /* - * Insert a newline between lines and after last line if - * y_type is MLINE. - */ - if (reg->y_type == MLINE || i < reg->y_size - 1) + // Insert a newline between lines and after last line if + // y_type is kMTLineWise. + if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { stuffcharReadbuff('\n'); + } } } } @@ -1244,21 +1260,18 @@ get_spec_reg ( return FALSE; } -/* - * Paste a yank register into the command line. - * Only for non-special registers. - * Used by CTRL-R command in command-line mode - * insert_reg() can't be used here, because special characters from the - * register contents will be interpreted as commands. - * - * return FAIL for failure, OK otherwise - */ -int -cmdline_paste_reg ( - int regname, - int literally, /* Insert text literally instead of "as typed" */ - int remcr /* don't add trailing CR */ -) +/// Paste a yank register into the command line. +/// Only for non-special registers. +/// Used by CTRL-R command in command-line mode +/// insert_reg() can't be used here, because special characters from the +/// register contents will be interpreted as commands. +/// +/// @param regname Register name. +/// @param literally Insert text literally instead of "as typed". +/// @param remcr When true, don't add CR characters. +/// +/// @returns FAIL for failure, OK otherwise +bool cmdline_paste_reg(int regname, bool literally, bool remcr) { long i; @@ -1269,14 +1282,11 @@ cmdline_paste_reg ( for (i = 0; i < reg->y_size; i++) { cmdline_paste_str(reg->y_array[i], literally); - /* Insert ^M between lines and after last line if type is MLINE. - * Don't do this when "remcr" is TRUE and the next line is empty. */ - if (reg->y_type == MLINE - || (i < reg->y_size - 1 - && !(remcr - && i == reg->y_size - 2 - && *reg->y_array[i + 1] == NUL))) + // Insert ^M between lines and after last line if type is kMTLineWise. + // Don't do this when "remcr" is true. + if ((reg->y_type == kMTLineWise || i < reg->y_size - 1) && !remcr) { cmdline_paste_str((char_u *)"\r", literally); + } /* Check for CTRL-C, in case someone tries to paste a few thousand * lines and gets bored. */ @@ -1318,41 +1328,41 @@ int op_delete(oparg_T *oap) /* * Imitate the strange Vi behaviour: If the delete spans more than one - * line and motion_type == MCHAR and the result is a blank line, make the + * line and motion_type == kMTCharWise and the result is a blank line, make the * delete linewise. Don't do this for the change command or Visual mode. */ - if ( oap->motion_type == MCHAR - && !oap->is_VIsual - && !oap->block_mode - && oap->line_count > 1 - && oap->motion_force == NUL - && oap->op_type == OP_DELETE) { + if (oap->motion_type == kMTCharWise + && !oap->is_VIsual + && oap->line_count > 1 + && oap->motion_force == NUL + && oap->op_type == OP_DELETE) { ptr = ml_get(oap->end.lnum) + oap->end.col; if (*ptr != NUL) ptr += oap->inclusive; ptr = skipwhite(ptr); - if (*ptr == NUL && inindent(0)) - oap->motion_type = MLINE; + if (*ptr == NUL && inindent(0)) { + oap->motion_type = kMTLineWise; + } } /* * Check for trying to delete (e.g. "D") in an empty line. * Note: For the change operator it is ok. */ - if ( oap->motion_type == MCHAR - && oap->line_count == 1 - && oap->op_type == OP_DELETE - && *ml_get(oap->start.lnum) == NUL) { - /* - * It's an error to operate on an empty region, when 'E' included in - * 'cpoptions' (Vi compatible). - */ - if (virtual_op) - /* Virtual editing: Nothing gets deleted, but we set the '[ and '] - * marks as if it happened. */ + if (oap->motion_type != kMTLineWise + && oap->line_count == 1 + && oap->op_type == OP_DELETE + && *ml_get(oap->start.lnum) == NUL) { + // It's an error to operate on an empty region, when 'E' included in + // 'cpoptions' (Vi compatible). + if (virtual_op) { + // Virtual editing: Nothing gets deleted, but we set the '[ and '] + // marks as if it happened. goto setmarks; - if (vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL) + } + if (vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL) { beep_flush(); + } return OK; } @@ -1375,7 +1385,7 @@ int op_delete(oparg_T *oap) * Put deleted text into register 1 and shift number registers if the * delete contains a line break, or when a regname has been specified. */ - if (oap->regname != 0 || oap->motion_type == MLINE + if (oap->regname != 0 || oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { free_register(&y_regs[9]); /* free register "9 */ for (n = 9; n > 1; n--) @@ -1388,14 +1398,15 @@ int op_delete(oparg_T *oap) /* Yank into small delete register when no named register specified * and the delete is within one line. */ - if (oap->regname == 0 && oap->motion_type != MLINE + if (oap->regname == 0 && oap->motion_type != kMTLineWise && oap->line_count == 1) { reg = get_yank_register('-', YREG_YANK); op_yank_reg(oap, false, reg, false); } - if(oap->regname == 0) { + if (oap->regname == 0) { set_clipboard(0, reg); + yank_do_autocmd(oap, reg); } } @@ -1403,10 +1414,11 @@ int op_delete(oparg_T *oap) /* * block mode delete */ - if (oap->block_mode) { + if (oap->motion_type == kMTBlockWise) { if (u_save((linenr_T)(oap->start.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) + (linenr_T)(oap->end.lnum + 1)) == FAIL) { return FAIL; + } for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; ++lnum) { block_prep(oap, &bd, lnum, TRUE); @@ -1440,9 +1452,9 @@ int op_delete(oparg_T *oap) check_cursor_col(); changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col, - oap->end.lnum + 1, 0L); - oap->line_count = 0; /* no lines deleted */ - } else if (oap->motion_type == MLINE) { + oap->end.lnum + 1, 0L); + oap->line_count = 0; // no lines deleted + } else if (oap->motion_type == kMTLineWise) { if (oap->op_type == OP_CHANGE) { /* Delete the lines except the first one. Temporarily move the * cursor to the next line. Save the current line number, if the @@ -1539,62 +1551,38 @@ int op_delete(oparg_T *oap) if (gchar_cursor() != NUL) curwin->w_cursor.coladd = 0; } - if (oap->op_type == OP_DELETE - && oap->inclusive - && oap->end.lnum == curbuf->b_ml.ml_line_count - && n > (int)STRLEN(ml_get(oap->end.lnum))) { - /* Special case: gH<Del> deletes the last line. */ - del_lines(1L, FALSE); - } else { - (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE - && !oap->is_VIsual - ); - } - } else { /* delete characters between lines */ + + (void)del_bytes((long)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); + } else { + // delete characters between lines pos_T curpos; - int delete_last_line; /* save deleted and changed lines for undo */ if (u_save((linenr_T)(curwin->w_cursor.lnum - 1), (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL) return FAIL; - delete_last_line = (oap->end.lnum == curbuf->b_ml.ml_line_count); - truncate_line(TRUE); /* delete from cursor to end of line */ - - curpos = curwin->w_cursor; /* remember curwin->w_cursor */ - ++curwin->w_cursor.lnum; - del_lines(oap->line_count - 2, FALSE); + truncate_line(true); // delete from cursor to end of line - if (delete_last_line) - oap->end.lnum = curbuf->b_ml.ml_line_count; + curpos = curwin->w_cursor; // remember curwin->w_cursor + curwin->w_cursor.lnum++; + del_lines(oap->line_count - 2, false); + // delete from start of line until op_end n = (oap->end.col + 1 - !oap->inclusive); - if (oap->inclusive && delete_last_line - && n > (int)STRLEN(ml_get(oap->end.lnum))) { - /* Special case: gH<Del> deletes the last line. */ - del_lines(1L, FALSE); - curwin->w_cursor = curpos; /* restore curwin->w_cursor */ - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } else { - /* delete from start of line until op_end */ - curwin->w_cursor.col = 0; - (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE - && !oap->is_VIsual - ); - curwin->w_cursor = curpos; /* restore curwin->w_cursor */ - } - if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - do_join(2, FALSE, FALSE, FALSE, false); - } + curwin->w_cursor.col = 0; + (void)del_bytes((long)n, !virtual_op, + oap->op_type == OP_DELETE && !oap->is_VIsual); + curwin->w_cursor = curpos; // restore curwin->w_cursor + (void)do_join(2, false, false, false, false); } } msgmore(curbuf->b_ml.ml_line_count - old_lcount); setmarks: - if (oap->block_mode) { + if (oap->motion_type == kMTBlockWise) { curbuf->b_op_end.lnum = oap->end.lnum; curbuf->b_op_end.col = oap->start.col; } else @@ -1655,7 +1643,7 @@ int op_replace(oparg_T *oap, int c) /* * block mode replace */ - if (oap->block_mode) { + if (oap->motion_type == kMTBlockWise) { bd.is_MAX = (curwin->w_curswant == MAXCOL); for (; curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { curwin->w_cursor.col = 0; /* make sure cursor position is valid */ @@ -1744,10 +1732,8 @@ int op_replace(oparg_T *oap, int c) } } } else { - /* - * MCHAR and MLINE motion replace. - */ - if (oap->motion_type == MLINE) { + // Characterwise or linewise motion replace. + if (oap->motion_type == kMTLineWise) { oap->start.col = 0; curwin->w_cursor.col = 0; oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); @@ -1837,8 +1823,8 @@ void op_tilde(oparg_T *oap) return; pos = oap->start; - if (oap->block_mode) { /* Visual block mode */ - for (; pos.lnum <= oap->end.lnum; ++pos.lnum) { + if (oap->motion_type == kMTBlockWise) { // Visual block mode + for (; pos.lnum <= oap->end.lnum; pos.lnum++) { int one_change; block_prep(oap, &bd, pos.lnum, FALSE); @@ -1849,8 +1835,8 @@ void op_tilde(oparg_T *oap) } if (did_change) changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); - } else { /* not block mode */ - if (oap->motion_type == MLINE) { + } else { // not block mode + if (oap->motion_type == kMTLineWise) { oap->start.col = 0; pos.col = 0; oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); @@ -1991,6 +1977,7 @@ void op_insert(oparg_T *oap, long count1) char_u *firstline, *ins_text; struct block_def bd; int i; + pos_T t1; /* edit() changes this - record it for OP_APPEND */ bd.is_MAX = (curwin->w_curswant == MAXCOL); @@ -1999,11 +1986,11 @@ void op_insert(oparg_T *oap, long count1) curwin->w_cursor.lnum = oap->start.lnum; update_screen(INVERTED); - if (oap->block_mode) { - /* When 'virtualedit' is used, need to insert the extra spaces before - * doing block_prep(). When only "block" is used, virtual edit is - * already disabled, but still need it when calling - * coladvance_force(). */ + if (oap->motion_type == kMTBlockWise) { + // When 'virtualedit' is used, need to insert the extra spaces before + // doing block_prep(). When only "block" is used, virtual edit is + // already disabled, but still need it when calling + // coladvance_force(). if (curwin->w_cursor.coladd > 0) { int old_ve_flags = ve_flags; @@ -2025,7 +2012,7 @@ void op_insert(oparg_T *oap, long count1) } if (oap->op_type == OP_APPEND) { - if (oap->block_mode + if (oap->motion_type == kMTBlockWise && curwin->w_cursor.coladd == 0 ) { /* Move the cursor to the character right of the block. */ @@ -2053,7 +2040,16 @@ void op_insert(oparg_T *oap, long count1) } } - edit(NUL, FALSE, (linenr_T)count1); + t1 = oap->start; + edit(NUL, false, (linenr_T)count1); + + // When a tab was inserted, and the characters in front of the tab + // have been converted to a tab as well, the column of the cursor + // might have actually been reduced, so need to adjust here. */ + if (t1.lnum == curbuf->b_op_start_orig.lnum + && lt(curbuf->b_op_start_orig, t1)) { + oap->start = curbuf->b_op_start_orig; + } /* If user has moved off this line, we don't know what to do, so do * nothing. @@ -2061,7 +2057,7 @@ void op_insert(oparg_T *oap, long count1) if (curwin->w_cursor.lnum != oap->start.lnum || got_int) return; - if (oap->block_mode) { + if (oap->motion_type == kMTBlockWise) { struct block_def bd2; /* The user may have moved the cursor before inserting something, try @@ -2069,21 +2065,23 @@ void op_insert(oparg_T *oap, long count1) if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX) { if (oap->op_type == OP_INSERT && oap->start.col + oap->start.coladd - != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { + != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { + size_t t = getviscol2(curbuf->b_op_start_orig.col, + curbuf->b_op_start_orig.coladd); oap->start.col = curbuf->b_op_start_orig.col; - pre_textlen -= getviscol2(oap->start.col, oap->start.coladd) - - oap->start_vcol; - oap->start_vcol = getviscol2(oap->start.col, oap->start.coladd); + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; } else if (oap->op_type == OP_APPEND && oap->end.col + oap->end.coladd - >= curbuf->b_op_start_orig.col - + curbuf->b_op_start_orig.coladd) { + >= curbuf->b_op_start_orig.col + + curbuf->b_op_start_orig.coladd) { + size_t t = getviscol2(curbuf->b_op_start_orig.col, + curbuf->b_op_start_orig.coladd); oap->start.col = curbuf->b_op_start_orig.col; /* reset pre_textlen to the value of OP_INSERT */ pre_textlen += bd.textlen; - pre_textlen -= getviscol2(oap->start.col, oap->start.coladd) - - oap->start_vcol; - oap->start_vcol = getviscol2(oap->start.col, oap->start.coladd); + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; oap->op_type = OP_INSERT; } } @@ -2146,7 +2144,7 @@ int op_change(oparg_T *oap) struct block_def bd; l = oap->start.col; - if (oap->motion_type == MLINE) { + if (oap->motion_type == kMTLineWise) { l = 0; if (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin @@ -2166,21 +2164,23 @@ int op_change(oparg_T *oap) && !virtual_op) inc_cursor(); - /* check for still on same line (<CR> in inserted text meaningless) */ - /* skip blank lines too */ - if (oap->block_mode) { - /* Add spaces before getting the current line length. */ + // check for still on same line (<CR> in inserted text meaningless) + // skip blank lines too + if (oap->motion_type == kMTBlockWise) { + // Add spaces before getting the current line length. if (virtual_op && (curwin->w_cursor.coladd > 0 - || gchar_cursor() == NUL)) + || gchar_cursor() == NUL)) { coladvance_force(getviscol()); + } firstline = ml_get(oap->start.lnum); pre_textlen = (long)STRLEN(firstline); pre_indent = (long)(skipwhite(firstline) - firstline); bd.textcol = curwin->w_cursor.col; } - if (oap->motion_type == MLINE) + if (oap->motion_type == kMTLineWise) { fix_indent(); + } retval = edit(NUL, FALSE, (linenr_T)1); @@ -2189,9 +2189,10 @@ int op_change(oparg_T *oap) * block. * Don't repeat the insert when Insert mode ended with CTRL-C. */ - if (oap->block_mode && oap->start.lnum != oap->end.lnum && !got_int) { - /* Auto-indenting may have changed the indent. If the cursor was past - * the indent, exclude that indent change from the inserted text. */ + if (oap->motion_type == kMTBlockWise + && oap->start.lnum != oap->end.lnum && !got_int) { + // Auto-indenting may have changed the indent. If the cursor was past + // the indent, exclude that indent change from the inserted text. firstline = ml_get(oap->start.lnum); if (bd.textcol > (colnr_T)pre_indent) { long new_indent = (long)(skipwhite(firstline) - firstline); @@ -2303,6 +2304,8 @@ bool op_yank(oparg_T *oap, bool message) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); set_clipboard(oap->regname, reg); + yank_do_autocmd(oap, reg); + return true; } @@ -2314,7 +2317,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) char_u **new_ptr; linenr_T lnum; /* current line number */ long j; - int yanktype = oap->motion_type; + MotionType yank_type = oap->motion_type; long yanklines = oap->line_count; linenr_T yankendlnum = oap->end.lnum; char_u *p; @@ -2332,20 +2335,19 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) * If the cursor was in column 1 before and after the movement, and the * operator is not inclusive, the yank is always linewise. */ - if ( oap->motion_type == MCHAR - && oap->start.col == 0 - && !oap->inclusive - && (!oap->is_VIsual || *p_sel == 'o') - && !oap->block_mode - && oap->end.col == 0 - && yanklines > 1) { - yanktype = MLINE; - --yankendlnum; - --yanklines; + if (oap->motion_type == kMTCharWise + && oap->start.col == 0 + && !oap->inclusive + && (!oap->is_VIsual || *p_sel == 'o') + && oap->end.col == 0 + && yanklines > 1) { + yank_type = kMTLineWise; + yankendlnum--; + yanklines--; } reg->y_size = yanklines; - reg->y_type = yanktype; /* set the yank register type */ + reg->y_type = yank_type; // set the yank register type reg->y_width = 0; reg->y_array = xcalloc(yanklines, sizeof(char_u *)); reg->additional_data = NULL; @@ -2354,9 +2356,8 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) y_idx = 0; lnum = oap->start.lnum; - if (oap->block_mode) { - /* Visual block mode */ - reg->y_type = MBLOCK; /* set the yank register type */ + if (yank_type == kMTBlockWise) { + // Visual block mode reg->y_width = oap->end_vcol - oap->start_vcol; if (curwin->w_curswant == MAXCOL && reg->y_width > 0) @@ -2365,16 +2366,16 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) for (; lnum <= yankendlnum; lnum++, y_idx++) { switch (reg->y_type) { - case MBLOCK: - block_prep(oap, &bd, lnum, FALSE); + case kMTBlockWise: + block_prep(oap, &bd, lnum, false); yank_copy_line(reg, &bd, y_idx); break; - case MLINE: + case kMTLineWise: reg->y_array[y_idx] = vim_strsave(ml_get(lnum)); break; - case MCHAR: + case kMTCharWise: { colnr_T startcol = 0, endcol = MAXCOL; int is_oneChar = FALSE; @@ -2435,7 +2436,9 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) yank_copy_line(reg, &bd, y_idx); break; } - /* NOTREACHED */ + // NOTREACHED + case kMTUnknown: + assert(false); } } @@ -2446,12 +2449,15 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) xfree(curr->y_array); curr->y_array = new_ptr; - if (yanktype == MLINE) /* MLINE overrides MCHAR and MBLOCK */ - curr->y_type = MLINE; + if (yank_type == kMTLineWise) { + // kMTLineWise overrides kMTCharWise and kMTBlockWise + curr->y_type = kMTLineWise; + } - /* Concatenate the last line of the old block with the first line of - * the new block, unless being Vi compatible. */ - if (curr->y_type == MCHAR && vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) { + // Concatenate the last line of the old block with the first line of + // the new block, unless being Vi compatible. + if (curr->y_type == kMTCharWise + && vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) { pnew = xmalloc(STRLEN(curr->y_array[curr->y_size - 1]) + STRLEN(reg->y_array[0]) + 1); STRCPY(pnew, curr->y_array[--j]); @@ -2470,25 +2476,26 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) if (curwin->w_p_rnu) { redraw_later(SOME_VALID); // cursor moved to start } - if (message) { /* Display message about yank? */ - if (yanktype == MCHAR - && !oap->block_mode - && yanklines == 1) + if (message) { // Display message about yank? + if (yank_type == kMTCharWise && yanklines == 1) { yanklines = 0; - /* Some versions of Vi use ">=" here, some don't... */ + } + // Some versions of Vi use ">=" here, some don't... if (yanklines > p_report) { - /* redisplay now, so message is not deleted */ + // redisplay now, so message is not deleted update_topline_redraw(); if (yanklines == 1) { - if (oap->block_mode) + if (yank_type == kMTBlockWise) { MSG(_("block of 1 line yanked")); - else + } else { MSG(_("1 line yanked")); - } else if (oap->block_mode) + } + } else if (yank_type == kMTBlockWise) { smsg(_("block of %" PRId64 " lines yanked"), (int64_t)yanklines); - else + } else { smsg(_("%" PRId64 " lines yanked"), (int64_t)yanklines); + } } } @@ -2497,9 +2504,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) */ curbuf->b_op_start = oap->start; curbuf->b_op_end = oap->end; - if (yanktype == MLINE - && !oap->block_mode - ) { + if (yank_type == kMTLineWise) { curbuf->b_op_start.col = 0; curbuf->b_op_end.col = MAXCOL; } @@ -2521,6 +2526,58 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx) *pnew = NUL; } +/// Execute autocommands for TextYankPost. +/// +/// @param oap Operator arguments. +/// @param reg The yank register used. +static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + + if (recursive || !has_event(EVENT_TEXTYANKPOST)) { + // No autocommand was defined + // or we yanked from this autocommand. + return; + } + + recursive = true; + + // set v:event to a dictionary with information about the yank + dict_T *dict = get_vim_var_dict(VV_EVENT); + + // the yanked text + list_T *list = list_alloc(); + for (linenr_T i = 0; i < reg->y_size; i++) { + list_append_string(list, reg->y_array[i], -1); + } + list->lv_lock = VAR_FIXED; + dict_add_list(dict, "regcontents", list); + + // the register type + char buf[NUMBUFLEN+2]; + format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); + dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + + // name of requested register or the empty string for an unnamed operation. + buf[0] = (char)oap->regname; + buf[1] = NUL; + dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + + // kind of operation (yank/delete/change) + buf[0] = get_op_char(oap->op_type); + buf[1] = NUL; + dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + + dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); + textlock--; + dict_clear(dict); + + recursive = false; +} + /* * Put contents of register "regname" into the text. @@ -2537,8 +2594,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) int totlen = 0; /* init for gcc */ linenr_T lnum; colnr_T col; - long i; /* index in y_array[] */ - int y_type; + long i; // index in y_array[] + MotionType y_type; long y_size; int oldlen; long y_width = 0; @@ -2599,7 +2656,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } if (insert_string != NULL) { - y_type = MCHAR; + y_type = kMTCharWise; if (regname == '=') { /* For the = register we need to split the string at NL * characters. @@ -2618,7 +2675,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) ++ptr; /* A trailing '\n' makes the register linewise. */ if (*ptr == NUL) { - y_type = MLINE; + y_type = kMTLineWise; break; } } @@ -2659,19 +2716,29 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) return; } - if (y_type == MLINE) { + if (y_type == kMTLineWise) { if (flags & PUT_LINE_SPLIT) { - /* "p" or "P" in Visual mode: split the lines to put the text in - * between. */ - if (u_save_cursor() == FAIL) + // "p" or "P" in Visual mode: split the lines to put the text in + // between. + if (u_save_cursor() == FAIL) { goto end; - ptr = vim_strsave(get_cursor_pos_ptr()); - ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, FALSE); + } + char_u *p = get_cursor_pos_ptr(); + if (dir == FORWARD && *p != NUL) { + mb_ptr_adv(p); + } + ptr = vim_strsave(p); + ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, false); xfree(ptr); - ptr = vim_strnsave(get_cursor_line_ptr(), curwin->w_cursor.col); - ml_replace(curwin->w_cursor.lnum, ptr, FALSE); - ++nr_lines; + oldp = get_cursor_line_ptr(); + p = oldp + curwin->w_cursor.col; + if (dir == FORWARD && *p != NUL) { + mb_ptr_adv(p); + } + ptr = vim_strnsave(oldp, p - oldp); + ml_replace(curwin->w_cursor.lnum, ptr, false); + nr_lines++; dir = FORWARD; } if (flags & PUT_LINE_FORWARD) { @@ -2683,8 +2750,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) curbuf->b_op_end = curwin->w_cursor; /* default for '] mark */ } - if (flags & PUT_LINE) /* :put command or "p" in Visual line mode. */ - y_type = MLINE; + if (flags & PUT_LINE) { // :put command or "p" in Visual line mode. + y_type = kMTLineWise; + } if (y_size == 0 || y_array == NULL) { EMSG2(_("E353: Nothing in register %s"), @@ -2692,13 +2760,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) goto end; } - if (y_type == MBLOCK) { + if (y_type == kMTBlockWise) { lnum = curwin->w_cursor.lnum + y_size + 1; if (lnum > curbuf->b_ml.ml_line_count) lnum = curbuf->b_ml.ml_line_count + 1; if (u_save(curwin->w_cursor.lnum - 1, lnum) == FAIL) goto end; - } else if (y_type == MLINE) { + } else if (y_type == kMTLineWise) { lnum = curwin->w_cursor.lnum; /* Correct line number for closed fold. Don't move the cursor yet, * u_save() uses it. */ @@ -2722,7 +2790,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) yanklen = (int)STRLEN(y_array[0]); - if (ve_flags == VE_ALL && y_type == MCHAR) { + if (ve_flags == VE_ALL && y_type == kMTCharWise) { if (gchar_cursor() == TAB) { /* Don't need to insert spaces when "p" on the last position of a * tab or "P" on the first position. */ @@ -2742,7 +2810,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* * Block mode */ - if (y_type == MBLOCK) { + if (y_type == kMTBlockWise) { char c = gchar_cursor(); colnr_T endcol2 = 0; @@ -2890,12 +2958,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } else curwin->w_cursor.lnum = lnum; } else { - /* - * Character or Line mode - */ - if (y_type == MCHAR) { - /* if type is MCHAR, FORWARD is the same as BACKWARD on the next - * char */ + // Character or Line mode + if (y_type == kMTCharWise) { + // if type is kMTCharWise, FORWARD is the same as BACKWARD on the next + // char if (dir == FORWARD && gchar_cursor() != NUL) { if (has_mbyte) { int bytelen = (*mb_ptr2len)(get_cursor_pos_ptr()); @@ -2926,7 +2992,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* * simple case: insert into current line */ - if (y_type == MCHAR && y_size == 1) { + if (y_type == kMTCharWise && y_size == 1) { do { totlen = count * yanklen; if (totlen > 0) { @@ -2961,18 +3027,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) ++curwin->w_cursor.col; changed_bytes(lnum, col); } else { - /* - * Insert at least one line. When y_type is MCHAR, break the first - * line in two. - */ - for (cnt = 1; cnt <= count; ++cnt) { + // Insert at least one line. When y_type is kMTCharWise, break the first + // line in two. + for (cnt = 1; cnt <= count; cnt++) { i = 0; - if (y_type == MCHAR) { - /* - * Split the current line in two at the insert position. - * First insert y_array[size - 1] in front of second line. - * Then append y_array[0] to first line. - */ + if (y_type == kMTCharWise) { + // Split the current line in two at the insert position. + // First insert y_array[size - 1] in front of second line. + // Then append y_array[0] to first line. lnum = new_cursor.lnum; ptr = ml_get(lnum) + col; totlen = (int)STRLEN(y_array[y_size - 1]); @@ -2996,10 +3058,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } for (; i < y_size; i++) { - if ((y_type != MCHAR || i < y_size - 1) - && ml_append(lnum, y_array[i], (colnr_T)0, FALSE) - == FAIL) + if ((y_type != kMTCharWise || i < y_size - 1) + && ml_append(lnum, y_array[i], (colnr_T)0, false) + == FAIL) { goto error; + } lnum++; ++nr_lines; if (flags & PUT_FIXINDENT) { @@ -3028,22 +3091,23 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } error: - /* Adjust marks. */ - if (y_type == MLINE) { + // Adjust marks. + if (y_type == kMTLineWise) { curbuf->b_op_start.col = 0; if (dir == FORWARD) curbuf->b_op_start.lnum++; } - mark_adjust(curbuf->b_op_start.lnum + (y_type == MCHAR), - (linenr_T)MAXLNUM, nr_lines, 0L); + mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise), + (linenr_T)MAXLNUM, nr_lines, 0L); - /* note changed text for displaying and folding */ - if (y_type == MCHAR) + // note changed text for displaying and folding + if (y_type == kMTCharWise) { changed_lines(curwin->w_cursor.lnum, col, - curwin->w_cursor.lnum + 1, nr_lines); - else + curwin->w_cursor.lnum + 1, nr_lines); + } else { changed_lines(curbuf->b_op_start.lnum, 0, - curbuf->b_op_start.lnum, nr_lines); + curbuf->b_op_start.lnum, nr_lines); + } /* put '] mark at last inserted character */ curbuf->b_op_end.lnum = lnum; @@ -3059,19 +3123,20 @@ error: curwin->w_cursor.lnum = lnum; beginline(BL_WHITE | BL_FIX); } else if (flags & PUT_CURSEND) { - /* put cursor after inserted text */ - if (y_type == MLINE) { - if (lnum >= curbuf->b_ml.ml_line_count) + // put cursor after inserted text + if (y_type == kMTLineWise) { + if (lnum >= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - else + } else { curwin->w_cursor.lnum = lnum + 1; + } curwin->w_cursor.col = 0; } else { curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = col; } - } else if (y_type == MLINE) { - /* put cursor on first non-blank in first inserted line */ + } else if (y_type == kMTLineWise) { + // put cursor on first non-blank in first inserted line curwin->w_cursor.col = 0; if (dir == FORWARD) ++curwin->w_cursor.lnum; @@ -3124,11 +3189,9 @@ void adjust_cursor_eol(void) */ int preprocs_left(void) { - return - (curbuf->b_p_si && !curbuf->b_p_cin) || - (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE) - && curbuf->b_ind_hash_comment == 0) - ; + return ((curbuf->b_p_si && !curbuf->b_p_cin) + || (curbuf->b_p_cin && in_cinkeys('#', ' ', true) + && curbuf->b_ind_hash_comment == 0)); } /* Return the character name of the register with the given number */ @@ -3210,9 +3273,10 @@ void ex_display(exarg_T *eap) p += clen - 1; } } - if (n > 1 && yb->y_type == MLINE) + if (n > 1 && yb->y_type == kMTLineWise) { MSG_PUTS_ATTR("^J", attr); - ui_flush(); /* show one line at a time */ + } + ui_flush(); // show one line at a time } os_breakcheck(); } @@ -3926,7 +3990,7 @@ format_lines ( if (line_count < 0 && u_save_cursor() == FAIL) break; if (next_leader_len > 0) { - (void)del_bytes((long)next_leader_len, FALSE, FALSE); + (void)del_bytes(next_leader_len, false, false); mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L, (long)-next_leader_len); } else if (second_indent > 0) { /* the "leader" for FO_Q_SECOND */ @@ -4170,285 +4234,513 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int i bdp->textstart = pstart; } - -static void reverse_line(char_u *s) +/// Handle the add/subtract operator. +/// +/// @param[in] oap Arguments of operator. +/// @param[in] Prenum1 Amount of addition or subtraction. +/// @param[in] g_cmd Prefixed with `g`. +void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) { - int i, j; - char_u c; + pos_T pos; + struct block_def bd; + ssize_t change_cnt = 0; + linenr_T amount = Prenum1; - if ((i = (int)STRLEN(s) - 1) <= 0) - return; + if (!VIsual_active) { + pos = curwin->w_cursor; + if (u_save_cursor() == FAIL) { + return; + } + change_cnt = do_addsub(oap->op_type, &pos, 0, amount); + if (change_cnt) { + changed_lines(pos.lnum, 0, pos.lnum + 1, 0L); + } + } else { + int one_change; + int length; + pos_T startpos; + + if (u_save((linenr_T)(oap->start.lnum - 1), + (linenr_T)(oap->end.lnum + 1)) == FAIL) { + return; + } + + pos = oap->start; + for (; pos.lnum <= oap->end.lnum; pos.lnum++) { + if (oap->motion_type == kMTBlockWise) { + // Visual block mode + block_prep(oap, &bd, pos.lnum, false); + pos.col = bd.textcol; + length = bd.textlen; + } else if (oap->motion_type == kMTLineWise) { + curwin->w_cursor.col = 0; + pos.col = 0; + length = (colnr_T)STRLEN(ml_get(pos.lnum)); + } else { + // oap->motion_type == kMTCharWise + if (!oap->inclusive) { + dec(&(oap->end)); + } + length = (colnr_T)STRLEN(ml_get(pos.lnum)); + pos.col = 0; + if (pos.lnum == oap->start.lnum) { + pos.col += oap->start.col; + length -= oap->start.col; + } + if (pos.lnum == oap->end.lnum) { + length = (int)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col >= length) { + oap->end.col = length - 1; + } + length = oap->end.col - pos.col + 1; + } + } + one_change = do_addsub(oap->op_type, &pos, length, amount); + if (one_change) { + // Remember the start position of the first change. + if (change_cnt == 0) { + startpos = curbuf->b_op_start; + } + change_cnt++; + } - curwin->w_cursor.col = i - curwin->w_cursor.col; - for (j = 0; j < i; j++, i--) { - c = s[i]; s[i] = s[j]; s[j] = c; + if (g_cmd && one_change) { + amount += Prenum1; + } + } + if (change_cnt) { + changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); + } + + if (!change_cnt && oap->is_VIsual) { + // No change: need to remove the Visual selection + redraw_curbuf_later(INVERTED); + } + + // Set '[ mark if something changed. Keep the last end + // position from do_addsub(). + if (change_cnt > 0) { + curbuf->b_op_start = startpos; + } + + if (change_cnt > p_report) { + if (change_cnt == 1) { + MSG(_("1 line changed")); + } else { + smsg((char *)_("%" PRId64 " lines changed"), (int64_t)change_cnt); + } + } } } -# define RLADDSUBFIX(ptr) if (curwin->w_p_rl) reverse_line(ptr); - -/* - * add or subtract 'Prenum1' from a number in a line - * 'command' is CTRL-A for add, CTRL-X for subtract - * - * return FAIL for failure, OK otherwise - */ -int do_addsub(int command, linenr_T Prenum1) +/// Add or subtract from a number in a line. +/// +/// @param op_type OP_NR_ADD or OP_NR_SUB. +/// @param pos Cursor position. +/// @param length Target number length. +/// @param Prenum1 Amount of addition or subtraction. +/// +/// @return true if some character was changed. +int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) { int col; char_u *buf1; char_u buf2[NUMBUFLEN]; - int hex; /* 'X' or 'x': hex; '0': octal */ - static int hexupper = FALSE; /* 0xABC */ - unsigned long n, oldn; + int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin + static bool hexupper = false; // 0xABC + unsigned long n; + unsigned long oldn; char_u *ptr; int c; - int length = 0; /* character length of the number */ int todel; - int dohex; - int dooct; - int doalp; + bool dohex; + bool dooct; + bool dobin; + bool doalp; int firstdigit; - int negative; - int subtract; + bool subtract; + bool negative = false; + bool was_positive = true; + bool visual = VIsual_active; + bool did_change = false; + pos_T save_cursor = curwin->w_cursor; + int maxlen = 0; + pos_T startpos; + pos_T endpos; + + dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); // "heX" + dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); // "Octal" + dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin" + doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); // "alPha" + + curwin->w_cursor = *pos; + ptr = ml_get(pos->lnum); + col = pos->col; + + if (*ptr == NUL) { + goto theend; + } + + // First check if we are on a hexadecimal number, after the "0x". + if (!VIsual_active) { + if (dobin) { + while (col > 0 && ascii_isbdigit(ptr[col])) { + col--; + } + } - dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */ - dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); /* "Octal" */ - doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); /* "alPha" */ + if (dohex) { + while (col > 0 && ascii_isxdigit(ptr[col])) { + col--; + } + } + if (dobin + && dohex + && !((col > 0 + && (ptr[col] == 'X' || ptr[col] == 'x') + && ptr[col - 1] == '0' + && ascii_isxdigit(ptr[col + 1])))) { + // In case of binary/hexadecimal pattern overlap match, rescan + + col = curwin->w_cursor.col; + + while (col > 0 && ascii_isdigit(ptr[col])) { + col--; + } + } - ptr = get_cursor_line_ptr(); - RLADDSUBFIX(ptr); + if ((dohex + && col > 0 + && (ptr[col] == 'X' || ptr[col] == 'x') + && ptr[col - 1] == '0' + && ascii_isxdigit(ptr[col + 1])) + || (dobin + && col > 0 + && (ptr[col] == 'B' || ptr[col] == 'b') + && ptr[col - 1] == '0' + && ascii_isbdigit(ptr[col + 1]))) { + // Found hexadecimal or binary number, move to its start. + col--; + } else { + // Search forward and then backward to find the start of number. + col = pos->col; - /* - * First check if we are on a hexadecimal number, after the "0x". - */ - col = curwin->w_cursor.col; - if (dohex) - while (col > 0 && ascii_isxdigit(ptr[col])) - --col; - if ( dohex - && col > 0 - && (ptr[col] == 'X' - || ptr[col] == 'x') - && ptr[col - 1] == '0' - && ascii_isxdigit(ptr[col + 1])) { - /* - * Found hexadecimal number, move to its start. - */ - --col; - } else { - /* - * Search forward and then backward to find the start of number. - */ - col = curwin->w_cursor.col; + while (ptr[col] != NUL + && !ascii_isdigit(ptr[col]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) { + col++; + } - while (ptr[col] != NUL - && !ascii_isdigit(ptr[col]) - && !(doalp && ASCII_ISALPHA(ptr[col]))) - ++col; + while (col > 0 + && ascii_isdigit(ptr[col - 1]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) { + col--; + } + } + } - while (col > 0 - && ascii_isdigit(ptr[col - 1]) - && !(doalp && ASCII_ISALPHA(ptr[col]))) - --col; + if (visual) { + while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) + && !(doalp && ASCII_ISALPHA(ptr[col]))) { + col++; + length--; + } + + if (length == 0) { + goto theend; + } + + if (col > pos->col && ptr[col - 1] == '-') { + negative = true; + was_positive = false; + } } - /* - * If a number was found, and saving for undo works, replace the number. - */ + // If a number was found, and saving for undo works, replace the number. firstdigit = ptr[col]; - RLADDSUBFIX(ptr); - if ((!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) - || u_save_cursor() != OK) { + if (!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) { beep_flush(); - return FAIL; + goto theend; } - /* get ptr again, because u_save() may have changed it */ - ptr = get_cursor_line_ptr(); - RLADDSUBFIX(ptr); - if (doalp && ASCII_ISALPHA(firstdigit)) { - /* decrement or increment alphabetic character */ - if (command == Ctrl_X) { + // decrement or increment alphabetic character + if (op_type == OP_NR_SUB) { if (CharOrd(firstdigit) < Prenum1) { - if (isupper(firstdigit)) + if (isupper(firstdigit)) { firstdigit = 'A'; - else + } else { firstdigit = 'a'; - } else + } + } else { firstdigit -= Prenum1; + } } else { if (26 - CharOrd(firstdigit) - 1 < Prenum1) { - if (isupper(firstdigit)) + if (isupper(firstdigit)) { firstdigit = 'Z'; - else + } else { firstdigit = 'z'; - } else + } + } else { firstdigit += Prenum1; + } } curwin->w_cursor.col = col; - (void)del_char(FALSE); + if (!did_change) { + startpos = curwin->w_cursor; + } + did_change = true; + (void)del_char(false); ins_char(firstdigit); + endpos = curwin->w_cursor; + curwin->w_cursor.col = col; } else { - negative = FALSE; - if (col > 0 && ptr[col - 1] == '-') { /* negative number */ - --col; - negative = TRUE; + if (col > 0 && ptr[col - 1] == '-' && !visual) { + // negative number + col--; + negative = true; } - /* get the number value (unsigned) */ - vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n); + // get the number value (unsigned) + if (visual && VIsual_mode != 'V') { + maxlen = (curbuf->b_visual.vi_curswant == MAXCOL + ? (int)STRLEN(ptr) - col + : length); + } - /* ignore leading '-' for hex and octal numbers */ - if (hex && negative) { - ++col; - --length; - negative = FALSE; + vim_str2nr(ptr + col, &pre, &length, + 0 + (dobin ? STR2NR_BIN : 0) + + (dooct ? STR2NR_OCT : 0) + + (dohex ? STR2NR_HEX : 0), + NULL, &n, maxlen); + + // ignore leading '-' for hex, octal and bin numbers + if (pre && negative) { + col++; + length--; + negative = false; } - /* add or subtract */ - subtract = FALSE; - if (command == Ctrl_X) - subtract ^= TRUE; - if (negative) - subtract ^= TRUE; + // add or subtract + subtract = false; + if (op_type == OP_NR_SUB) { + subtract ^= true; + } + if (negative) { + subtract ^= true; + } oldn = n; - if (subtract) - n -= (unsigned long)Prenum1; - else - n += (unsigned long)Prenum1; - /* handle wraparound for decimal numbers */ - if (!hex) { + n = subtract ? n - (unsigned long) Prenum1 + : n + (unsigned long) Prenum1; + + // handle wraparound for decimal numbers + if (!pre) { if (subtract) { if (n > oldn) { n = 1 + (n ^ (unsigned long)-1); - negative ^= TRUE; + negative ^= true; } - } else { /* add */ + } else { + // add if (n < oldn) { n = (n ^ (unsigned long)-1); - negative ^= TRUE; + negative ^= true; } } - if (n == 0) - negative = FALSE; + if (n == 0) { + negative = false; + } } - /* - * Delete the old number. - */ + if (visual && !was_positive && !negative && col > 0) { + // need to remove the '-' + col--; + length++; + } + + // Delete the old number. curwin->w_cursor.col = col; + if (!did_change) { + startpos = curwin->w_cursor; + } + did_change = true; todel = length; c = gchar_cursor(); - /* - * Don't include the '-' in the length, only the length of the part - * after it is kept the same. - */ - if (c == '-') - --length; + + // Don't include the '-' in the length, only the length of the part + // after it is kept the same. + if (c == '-') { + length--; + } while (todel-- > 0) { if (c < 0x100 && isalpha(c)) { - if (isupper(c)) - hexupper = TRUE; - else - hexupper = FALSE; + if (isupper(c)) { + hexupper = true; + } else { + hexupper = false; + } } - /* del_char() will mark line needing displaying */ - (void)del_char(FALSE); + // del_char() will mark line needing displaying + (void)del_char(false); c = gchar_cursor(); } - /* - * Prepare the leading characters in buf1[]. - * When there are many leading zeros it could be very long. Allocate - * a bit too much. - */ + // Prepare the leading characters in buf1[]. + // When there are many leading zeros it could be very long. + // Allocate a bit too much. buf1 = xmalloc(length + NUMBUFLEN); + if (buf1 == NULL) { + goto theend; + } ptr = buf1; - if (negative) { + if (negative && (!visual || (visual && was_positive))) { *ptr++ = '-'; } - if (hex) { + if (pre) { *ptr++ = '0'; - --length; + length--; } - if (hex == 'x' || hex == 'X') { - *ptr++ = hex; - --length; + if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') { + *ptr++ = pre; + length--; } - /* - * Put the number characters in buf2[]. - */ - if (hex == 0) - sprintf((char *)buf2, "%" PRIu64, (uint64_t)n); - else if (hex == '0') - sprintf((char *)buf2, "%" PRIo64, (uint64_t)n); - else if (hex && hexupper) - sprintf((char *)buf2, "%" PRIX64, (uint64_t)n); - else - sprintf((char *)buf2, "%" PRIx64, (uint64_t)n); + // Put the number characters in buf2[]. + if (pre == 'b' || pre == 'B') { + size_t bits = 0; + size_t i = 0; + + // leading zeros + for (bits = 8 * sizeof(n); bits > 0; bits--) { + if ((n >> (bits - 1)) & 0x1) { + break; + } + } + + while (bits > 0) { + buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0'; + } + + buf2[i] = '\0'; + + } else if (pre == 0) { + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n); + } else if (pre == '0') { + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n); + } else if (pre && hexupper) { + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n); + } else { + vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n); + } length -= (int)STRLEN(buf2); - /* - * Adjust number of zeros to the new number of digits, so the - * total length of the number remains the same. - * Don't do this when - * the result may look like an octal number. - */ - if (firstdigit == '0' && !(dooct && hex == 0)) - while (length-- > 0) + // Adjust number of zeros to the new number of digits, so the + // total length of the number remains the same. + // Don't do this when + // the result may look like an octal number. + if (firstdigit == '0' && !(dooct && pre == 0)) { + while (length-- > 0) { *ptr++ = '0'; + } + } *ptr = NUL; STRCAT(buf1, buf2); - ins_str(buf1); /* insert the new number */ + ins_str(buf1); // insert the new number xfree(buf1); + endpos = curwin->w_cursor; + if (did_change && curwin->w_cursor.col) { + curwin->w_cursor.col--; + } } - --curwin->w_cursor.col; - curwin->w_set_curswant = TRUE; - ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE); - RLADDSUBFIX(ptr); - return OK; + + if (did_change) { + // set the '[ and '] marks + curbuf->b_op_start = startpos; + curbuf->b_op_end = endpos; + if (curbuf->b_op_end.col > 0) { + curbuf->b_op_end.col--; + } + } + +theend: + if (visual) { + curwin->w_cursor = save_cursor; + } + + return did_change; } /* * Return the type of a register. * Used for getregtype() - * Returns MAUTO for error. + * Returns kMTUnknown for error. */ -char_u get_reg_type(int regname, long *reglen) +MotionType get_reg_type(int regname, colnr_T *reg_width) { switch (regname) { - case '%': /* file name */ - case '#': /* alternate file name */ - case '=': /* expression */ - case ':': /* last command line */ - case '/': /* last search-pattern */ - case '.': /* last inserted text */ - case Ctrl_F: /* Filename under cursor */ - case Ctrl_P: /* Path under cursor, expand via "path" */ - case Ctrl_W: /* word under cursor */ - case Ctrl_A: /* WORD (mnemonic All) under cursor */ - case '_': /* black hole: always empty */ - return MCHAR; + case '%': // file name + case '#': // alternate file name + case '=': // expression + case ':': // last command line + case '/': // last search-pattern + case '.': // last inserted text + case Ctrl_F: // Filename under cursor + case Ctrl_P: // Path under cursor, expand via "path" + case Ctrl_W: // word under cursor + case Ctrl_A: // WORD (mnemonic All) under cursor + case '_': // black hole: always empty + return kMTCharWise; } - if (regname != NUL && !valid_yank_reg(regname, false)) - return MAUTO; + if (regname != NUL && !valid_yank_reg(regname, false)) { + return kMTUnknown; + } yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array != NULL) { - if (reglen != NULL && reg->y_type == MBLOCK) - *reglen = reg->y_width; + if (reg_width != NULL && reg->y_type == kMTBlockWise) { + *reg_width = reg->y_width; + } return reg->y_type; } - return MAUTO; + return kMTUnknown; } +/// Format the register type as a string. +/// +/// @param reg_type The register type. +/// @param reg_width The width, only used if "reg_type" is kMTBlockWise. +/// @param[out] buf Buffer to store formatted string. The allocated size should +/// be at least NUMBUFLEN+2 to always fit the value. +/// @param buf_len The allocated size of the buffer. +void format_reg_type(MotionType reg_type, colnr_T reg_width, + char *buf, size_t buf_len) + FUNC_ATTR_NONNULL_ALL +{ + assert(buf_len > 1); + switch (reg_type) { + case kMTLineWise: + buf[0] = 'V'; + buf[1] = NUL; + break; + case kMTCharWise: + buf[0] = 'v'; + buf[1] = NUL; + break; + case kMTBlockWise: + snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1); + break; + case kMTUnknown: + buf[0] = NUL; + break; + } +} + + /// When `flags` has `kGRegList` return a list with text `s`. /// Otherwise just return `s`. /// @@ -4527,10 +4819,11 @@ void *get_reg_contents(int regname, int flags) len += STRLEN(reg->y_array[i]); /* * Insert a newline between lines and after last line if - * y_type is MLINE. + * y_type is kMTLineWise. */ - if (reg->y_type == MLINE || i < reg->y_size - 1) - ++len; + if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { + len++; + } } retval = xmalloc(len + 1); @@ -4545,10 +4838,11 @@ void *get_reg_contents(int regname, int flags) /* * Insert a NL between lines and after the last line if y_type is - * MLINE. + * kMTLineWise. */ - if (reg->y_type == MLINE || i < reg->y_size - 1) + if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { retval[len++] = '\n'; + } } retval[len] = NUL; @@ -4589,11 +4883,12 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous void write_reg_contents(int name, const char_u *str, ssize_t len, int must_append) { - write_reg_contents_ex(name, str, len, must_append, MAUTO, 0L); + write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } void write_reg_contents_lst(int name, char_u **strings, int maxlen, - bool must_append, int yank_type, long block_len) + bool must_append, MotionType yank_type, + long block_len) { if (name == '/' || name == '=') { char_u *s = strings[0]; @@ -4639,13 +4934,13 @@ void write_reg_contents_lst(int name, char_u **strings, int maxlen, /// contents of the register. Note that regardless of /// `must_append`, this function will append when `name` /// is an uppercase letter. -/// @param yank_type MCHAR, MLINE, MBLOCK or MAUTO +/// @param yank_type The motion type (kMTUnknown to auto detect) /// @param block_len width of visual block void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_append, - int yank_type, + MotionType yank_type, long block_len) { if (len < 0) { @@ -4658,6 +4953,27 @@ void write_reg_contents_ex(int name, return; } + if (name == '#') { + buf_T *buf; + + if (ascii_isdigit(*str)) { + int num = atoi((char *)str); + + buf = buflist_findnr(num); + if (buf == NULL) { + EMSGN(_(e_nobufnr), (long)num); + } + } else { + buf = buflist_findnr(buflist_findpat(str, str + STRLEN(str), + true, false, false)); + } + if (buf == NULL) { + return; + } + curwin->w_alt_fnum = buf->b_fnum; + return; + } + if (name == '=') { size_t offset = 0; size_t totlen = (size_t) len; @@ -4698,24 +5014,24 @@ void write_reg_contents_ex(int name, /// When the register is not empty, the string is appended. /// /// @param y_ptr pointer to yank register -/// @param yank_type MCHAR, MLINE, MBLOCK or MAUTO +/// @param yank_type The motion type (kMTUnknown to auto detect) /// @param str string or list of strings to put in register /// @param len length of the string (Ignored when str_list=true.) /// @param blocklen width of visual block, or -1 for "I don't know." /// @param str_list True if str is `char_u **`. -static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str, - size_t len, colnr_T blocklen, bool str_list) +static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, + const char_u *str, size_t len, colnr_T blocklen, + bool str_list) FUNC_ATTR_NONNULL_ALL { if (y_ptr->y_array == NULL) { // NULL means empty register y_ptr->y_size = 0; } - int type = yank_type; // MCHAR, MLINE or MBLOCK - if (yank_type == MAUTO) { - type = ((str_list || - (len > 0 && (str[len - 1] == NL || str[len - 1] == CAR))) - ? MLINE : MCHAR); + if (yank_type == kMTUnknown) { + yank_type = ((str_list + || (len > 0 && (str[len - 1] == NL || str[len - 1] == CAR))) + ? kMTLineWise : kMTCharWise); } size_t newlines = 0; @@ -4729,11 +5045,11 @@ static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str, } } else { newlines = memcnt(str, '\n', len); - if (type == MCHAR || len == 0 || str[len - 1] != '\n') { + if (yank_type == kMTCharWise || len == 0 || str[len - 1] != '\n') { extraline = 1; ++newlines; // count extra newline at the end } - if (y_ptr->y_size > 0 && y_ptr->y_type == MCHAR) { + if (y_ptr->y_size > 0 && y_ptr->y_type == kMTCharWise) { append = true; --newlines; // uncount newline when appending first line } @@ -4786,11 +5102,11 @@ static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str, memchrsub(pp[lnum], NUL, '\n', s_len); } } - y_ptr->y_type = type; + y_ptr->y_type = yank_type; y_ptr->y_size = lnum; set_yreg_additional_data(y_ptr, NULL); y_ptr->timestamp = os_time(); - if (type == MBLOCK) { + if (yank_type == kMTBlockWise) { y_ptr->y_width = (blocklen == -1 ? (colnr_T) maxlen - 1 : blocklen); } else { y_ptr->y_width = 0; @@ -4849,18 +5165,18 @@ static long line_count_info(char_u *line, long *wc, long *cc, long limit, int eo return i; } -/* - * Give some info about the position of the cursor (for "g CTRL-G"). - * In Visual mode, give some info about the selected region. (In this case, - * the *_count_cursor variables store running totals for the selection.) - */ -void cursor_pos_info(void) +/// Give some info about the position of the cursor (for "g CTRL-G"). +/// In Visual mode, give some info about the selected region. (In this case, +/// the *_count_cursor variables store running totals for the selection.) +/// When "dict" is not NULL store the info there instead of showing it. +void cursor_pos_info(dict_T *dict) { char_u *p; char_u buf1[50]; char_u buf2[40]; linenr_T lnum; long byte_count = 0; + long bom_count = 0; long byte_count_cursor = 0; long char_count = 0; long char_count_cursor = 0; @@ -4875,11 +5191,12 @@ void cursor_pos_info(void) const int l_VIsual_active = VIsual_active; const int l_VIsual_mode = VIsual_mode; - /* - * Compute the length of the file in characters. - */ + // Compute the length of the file in characters. if (curbuf->b_ml.ml_flags & ML_EMPTY) { - MSG(_(no_lines_msg)); + if (dict == NULL) { + MSG(_(no_lines_msg)); + return; + } } else { if (get_fileformat(curbuf) == EOL_DOS) eol_size = 2; @@ -4903,7 +5220,7 @@ void cursor_pos_info(void) /* Make 'sbr' empty for a moment to get the correct size. */ p_sbr = empty_option; oparg.is_VIsual = true; - oparg.block_mode = true; + oparg.motion_type = kMTBlockWise; oparg.op_type = OP_NOP; getvcols(curwin, &min_pos, &max_pos, &oparg.start_vcol, &oparg.end_vcol); @@ -4964,7 +5281,7 @@ void cursor_pos_info(void) &char_count_cursor, len, eol_size); if (lnum == curbuf->b_ml.ml_line_count && !curbuf->b_p_eol - && curbuf->b_p_bin + && (curbuf->b_p_bin || !curbuf->b_p_fixeol) && (long)STRLEN(s) < len) byte_count_cursor -= eol_size; } @@ -4984,78 +5301,105 @@ void cursor_pos_info(void) &char_count, (long)MAXCOL, eol_size); } - /* Correction for when last line doesn't have an EOL. */ - if (!curbuf->b_p_eol && curbuf->b_p_bin) + // Correction for when last line doesn't have an EOL. + if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol)) { byte_count -= eol_size; + } + + if (dict == NULL) { + if (l_VIsual_active) { + if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) { + getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col); + vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "), + (int64_t)(oparg.end_vcol - oparg.start_vcol + 1)); + } else { + buf1[0] = NUL; + } + + if (char_count_cursor == byte_count_cursor + && char_count == byte_count) { + vim_snprintf((char *)IObuff, IOSIZE, + _("Selected %s%" PRId64 " of %" PRId64 " Lines;" + " %" PRId64 " of %" PRId64 " Words;" + " %" PRId64 " of %" PRId64 " Bytes"), + buf1, (int64_t)line_count_selected, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } else { + vim_snprintf((char *)IObuff, IOSIZE, + _("Selected %s%" PRId64 " of %" PRId64 " Lines;" + " %" PRId64 " of %" PRId64 " Words;" + " %" PRId64 " of %" PRId64 " Chars;" + " %" PRId64 " of %" PRId64 " Bytes"), + buf1, (int64_t)line_count_selected, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)char_count_cursor, (int64_t)char_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } + } else { + p = get_cursor_line_ptr(); + validate_virtcol(); + col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, + (int)curwin->w_virtcol + 1); + col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p)); + + if (char_count_cursor == byte_count_cursor + && char_count == byte_count) { + vim_snprintf((char *)IObuff, IOSIZE, + _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";" + " Word %" PRId64 " of %" PRId64 ";" + " Byte %" PRId64 " of %" PRId64 ""), + (char *)buf1, (char *)buf2, + (int64_t)curwin->w_cursor.lnum, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } else { + vim_snprintf((char *)IObuff, IOSIZE, + _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";" + " Word %" PRId64 " of %" PRId64 ";" + " Char %" PRId64 " of %" PRId64 ";" + " Byte %" PRId64 " of %" PRId64 ""), + (char *)buf1, (char *)buf2, + (int64_t)curwin->w_cursor.lnum, + (int64_t)curbuf->b_ml.ml_line_count, + (int64_t)word_count_cursor, (int64_t)word_count, + (int64_t)char_count_cursor, (int64_t)char_count, + (int64_t)byte_count_cursor, (int64_t)byte_count); + } + } + } + + // Don't shorten this message, the user asked for it. + bom_count = bomb_size(); + if (bom_count > 0) { + vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), + _("(+%" PRId64 " for BOM)"), (int64_t)byte_count); + } + if (dict == NULL) { + p = p_shm; + p_shm = (char_u *)""; + msg(IObuff); + p_shm = p; + } + } + + if (dict != NULL) { + dict_add_nr_str(dict, "words", word_count, NULL); + dict_add_nr_str(dict, "chars", char_count, NULL); + dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL); if (l_VIsual_active) { - if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) { - getvcols(curwin, &min_pos, &max_pos, &min_pos.col, - &max_pos.col); - vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "), - (int64_t)(oparg.end_vcol - oparg.start_vcol + 1)); - } else - buf1[0] = NUL; - - if (char_count_cursor == byte_count_cursor - && char_count == byte_count) - vim_snprintf((char *)IObuff, IOSIZE, - _("Selected %s%" PRId64 " of %" PRId64 " Lines; %" PRId64 - " of %" PRId64 " Words; %" PRId64 " of %" PRId64 " Bytes"), - buf1, (int64_t)line_count_selected, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); - else - vim_snprintf((char *)IObuff, IOSIZE, - _("Selected %s%" PRId64 " of %" PRId64 " Lines; %" PRId64 - " of %" PRId64 " Words; %" PRId64 " of %" PRId64 - " Chars; %" PRId64 " of %" PRId64 " Bytes"), - buf1, (int64_t)line_count_selected, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)char_count_cursor, (int64_t)char_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); + dict_add_nr_str(dict, "visual_bytes", byte_count_cursor, NULL); + dict_add_nr_str(dict, "visual_chars", char_count_cursor, NULL); + dict_add_nr_str(dict, "visual_words", word_count_cursor, NULL); } else { - p = get_cursor_line_ptr(); - validate_virtcol(); - col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, - (int)curwin->w_virtcol + 1); - col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p)); - - if (char_count_cursor == byte_count_cursor - && char_count == byte_count) - vim_snprintf((char *)IObuff, IOSIZE, - _("Col %s of %s; Line %" PRId64 " of %" PRId64 "; Word %" PRId64 - " of %" PRId64 "; Byte %" PRId64 " of %" PRId64 ""), - (char *)buf1, (char *)buf2, - (int64_t)curwin->w_cursor.lnum, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); - else - vim_snprintf((char *)IObuff, IOSIZE, - _( - "Col %s of %s; Line %" PRId64 " of %" PRId64 "; Word %" PRId64 - " of %" PRId64 "; Char %" PRId64 " of %" PRId64 - "; Byte %" PRId64 " of %" PRId64 ""), - (char *)buf1, (char *)buf2, - (int64_t)curwin->w_cursor.lnum, - (int64_t)curbuf->b_ml.ml_line_count, - (int64_t)word_count_cursor, (int64_t)word_count, - (int64_t)char_count_cursor, (int64_t)char_count, - (int64_t)byte_count_cursor, (int64_t)byte_count); - } - - byte_count = bomb_size(); - if (byte_count > 0) - sprintf((char *)IObuff + STRLEN(IObuff), _("(+%" PRId64 " for BOM)"), - (int64_t)byte_count); - /* Don't shorten this message, the user asked for it. */ - p = p_shm; - p_shm = (char_u *)""; - msg(IObuff); - p_shm = p; + dict_add_nr_str(dict, "cursor_bytes", byte_count_cursor, NULL); + dict_add_nr_str(dict, "cursor_chars", char_count_cursor, NULL); + dict_add_nr_str(dict, "cursor_words", word_count_cursor, NULL); + } } } @@ -5158,16 +5502,16 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } switch (regtype[0]) { case 0: - reg->y_type = MAUTO; + reg->y_type = kMTUnknown; break; case 'v': case 'c': - reg->y_type = MCHAR; + reg->y_type = kMTCharWise; break; case 'V': case 'l': - reg->y_type = MLINE; + reg->y_type = kMTLineWise; break; case 'b': case Ctrl_V: - reg->y_type = MBLOCK; + reg->y_type = kMTBlockWise; break; default: goto err; @@ -5175,7 +5519,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } else { lines = res; // provider did not specify regtype, calculate it below - reg->y_type = MAUTO; + reg->y_type = kMTUnknown; } reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *)); @@ -5196,20 +5540,20 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { // a known-to-be charwise yank might have a final linebreak // but otherwise there is no line after the final newline - if (reg->y_type != MCHAR) { + if (reg->y_type != kMTCharWise) { xfree(reg->y_array[reg->y_size-1]); reg->y_size--; - if (reg->y_type == MAUTO) { - reg->y_type = MLINE; + if (reg->y_type == kMTUnknown) { + reg->y_type = kMTLineWise; } } } else { - if (reg->y_type == MAUTO) { - reg->y_type = MCHAR; + if (reg->y_type == kMTUnknown) { + reg->y_type = kMTCharWise; } } - if (reg->y_type == MBLOCK) { + if (reg->y_type == kMTBlockWise) { int maxlen = 0; for (int i = 0; i < reg->y_size; i++) { int rowlen = STRLEN(reg->y_array[i]); @@ -5258,17 +5602,19 @@ static void set_clipboard(int name, yankreg_T *reg) char_u regtype; switch (reg->y_type) { - case MLINE: + case kMTLineWise: regtype = 'V'; list_append_string(lines, (char_u*)"", 0); break; - case MCHAR: + case kMTCharWise: regtype = 'v'; break; - case MBLOCK: + case kMTBlockWise: regtype = 'b'; list_append_string(lines, (char_u*)"", 0); break; + case kMTUnknown: + assert(false); } list_append_string(args, ®type, 1); @@ -5309,7 +5655,7 @@ static inline bool reg_empty(const yankreg_T *const reg) return (reg->y_array == NULL || reg->y_size == 0 || (reg->y_size == 1 - && reg->y_type == MCHAR + && reg->y_type == kMTCharWise && *(reg->y_array[0]) == NUL)); } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 507f933acf..8c8a586957 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -35,37 +35,39 @@ typedef int (*Indenter)(void); #define PLUS_REGISTER 38 #define NUM_REGISTERS 39 -/* - * Operator IDs; The order must correspond to opchars[] in ops.c! - */ -#define OP_NOP 0 /* no pending operation */ -#define OP_DELETE 1 /* "d" delete operator */ -#define OP_YANK 2 /* "y" yank operator */ -#define OP_CHANGE 3 /* "c" change operator */ -#define OP_LSHIFT 4 /* "<" left shift operator */ -#define OP_RSHIFT 5 /* ">" right shift operator */ -#define OP_FILTER 6 /* "!" filter operator */ -#define OP_TILDE 7 /* "g~" switch case operator */ -#define OP_INDENT 8 /* "=" indent operator */ -#define OP_FORMAT 9 /* "gq" format operator */ -#define OP_COLON 10 /* ":" colon operator */ -#define OP_UPPER 11 /* "gU" make upper case operator */ -#define OP_LOWER 12 /* "gu" make lower case operator */ -#define OP_JOIN 13 /* "J" join operator, only for Visual mode */ -#define OP_JOIN_NS 14 /* "gJ" join operator, only for Visual mode */ -#define OP_ROT13 15 /* "g?" rot-13 encoding */ -#define OP_REPLACE 16 /* "r" replace chars, only for Visual mode */ -#define OP_INSERT 17 /* "I" Insert column, only for Visual mode */ -#define OP_APPEND 18 /* "A" Append column, only for Visual mode */ -#define OP_FOLD 19 /* "zf" define a fold */ -#define OP_FOLDOPEN 20 /* "zo" open folds */ -#define OP_FOLDOPENREC 21 /* "zO" open folds recursively */ -#define OP_FOLDCLOSE 22 /* "zc" close folds */ -#define OP_FOLDCLOSEREC 23 /* "zC" close folds recursively */ -#define OP_FOLDDEL 24 /* "zd" delete folds */ -#define OP_FOLDDELREC 25 /* "zD" delete folds recursively */ -#define OP_FORMAT2 26 /* "gw" format operator, keeps cursor pos */ -#define OP_FUNCTION 27 /* "g@" call 'operatorfunc' */ +// Operator IDs; The order must correspond to opchars[] in ops.c! +#define OP_NOP 0 // no pending operation +#define OP_DELETE 1 // "d" delete operator +#define OP_YANK 2 // "y" yank operator +#define OP_CHANGE 3 // "c" change operator +#define OP_LSHIFT 4 // "<" left shift operator +#define OP_RSHIFT 5 // ">" right shift operator +#define OP_FILTER 6 // "!" filter operator +#define OP_TILDE 7 // "g~" switch case operator +#define OP_INDENT 8 // "=" indent operator +#define OP_FORMAT 9 // "gq" format operator +#define OP_COLON 10 // ":" colon operator +#define OP_UPPER 11 // "gU" make upper case operator +#define OP_LOWER 12 // "gu" make lower case operator +#define OP_JOIN 13 // "J" join operator, only for Visual mode +#define OP_JOIN_NS 14 // "gJ" join operator, only for Visual mode +#define OP_ROT13 15 // "g?" rot-13 encoding +#define OP_REPLACE 16 // "r" replace chars, only for Visual mode +#define OP_INSERT 17 // "I" Insert column, only for Visual mode +#define OP_APPEND 18 // "A" Append column, only for Visual mode +#define OP_FOLD 19 // "zf" define a fold +#define OP_FOLDOPEN 20 // "zo" open folds +#define OP_FOLDOPENREC 21 // "zO" open folds recursively +#define OP_FOLDCLOSE 22 // "zc" close folds +#define OP_FOLDCLOSEREC 23 // "zC" close folds recursively +#define OP_FOLDDEL 24 // "zd" delete folds +#define OP_FOLDDELREC 25 // "zD" delete folds recursively +#define OP_FORMAT2 26 // "gw" format operator, keeps cursor pos +#define OP_FUNCTION 27 // "g@" call 'operatorfunc' +#define OP_NR_ADD 28 // "<C-A>" Add to the number or alphabetic + // character (OP_ADD conflicts with Perl) +#define OP_NR_SUB 29 // "<C-X>" Subtract from the number or + // alphabetic character /// Flags for get_reg_contents(). enum GRegFlags { @@ -76,10 +78,10 @@ enum GRegFlags { /// Definition of one register typedef struct yankreg { - char_u **y_array; ///< Pointer to an array of line pointers. - linenr_T y_size; ///< Number of lines in y_array. - char_u y_type; ///< Register type: MLINE, MCHAR or MBLOCK. - colnr_T y_width; ///< Register width (only valid for y_type == MBLOCK). + char_u **y_array; ///< Pointer to an array of line pointers. + linenr_T y_size; ///< Number of lines in y_array. + MotionType y_type; ///< Register type + colnr_T y_width; ///< Register width (only valid for y_type == kBlockWise). Timestamp timestamp; ///< Time when register was last modified. dict_T *additional_data; ///< Additional data from ShaDa file. } yankreg_T; diff --git a/src/nvim/option.c b/src/nvim/option.c index dbcd230186..2f22c245dd 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -32,6 +32,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/edit.h" #include "nvim/option.h" #include "nvim/buffer.h" #include "nvim/charset.h" @@ -122,6 +123,7 @@ static char_u *p_cpt; static char_u *p_cfu; static char_u *p_ofu; static int p_eol; +static int p_fixeol; static int p_et; static char_u *p_fenc; static char_u *p_ff; @@ -167,11 +169,12 @@ static int p_ml_nobin; static long p_tw_nobin; static long p_wm_nobin; -/* Saved values for when 'paste' is set */ +// Saved values for when 'paste' is set. +static int p_ai_nopaste; +static int p_et_nopaste; +static long p_sts_nopaste; static long p_tw_nopaste; static long p_wm_nopaste; -static long p_sts_nopaste; -static int p_ai_nopaste; typedef struct vimoption { char *fullname; /* full option name */ @@ -217,26 +220,22 @@ typedef struct vimoption { #define P_RALL 0x6000U /* redraw all windows */ #define P_RCLR 0x7000U /* clear and redraw all */ -#define P_COMMA 0x8000U /* comma separated list */ -#define P_NODUP 0x10000U /* don't allow duplicate strings */ -#define P_FLAGLIST 0x20000U /* list of single-char flags */ - -#define P_SECURE 0x40000U /* cannot change in modeline or secure mode */ -#define P_GETTEXT 0x80000U /* expand default value with _() */ -#define P_NOGLOB 0x100000U /* do not use local value for global vimrc */ -#define P_NFNAME 0x200000U /* only normal file name chars allowed */ -#define P_INSECURE 0x400000U /* option was set from a modeline */ -#define P_PRI_MKRC 0x800000U /* priority for :mkvimrc (setting option has - side effects) */ -#define P_NO_ML 0x1000000U /* not allowed in modeline */ -#define P_CURSWANT 0x2000000U /* update curswant required; not needed when - * there is a redraw flag */ - -#define ISK_LATIN1 (char_u *)"@,48-57,_,192-255" - -/* 'isprint' for latin1 is also used for MS-Windows cp1252, where 0x80 is used - * for the currency sign. */ -# define ISP_LATIN1 (char_u *)"@,161-255" +#define P_COMMA 0x8000U ///< comma separated list +#define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive + ///< commas +#define P_NODUP 0x20000U ///< don't allow duplicate strings +#define P_FLAGLIST 0x40000U ///< list of single-char flags + +#define P_SECURE 0x80000U ///< cannot change in modeline or secure mode +#define P_GETTEXT 0x100000U ///< expand default value with _() +#define P_NOGLOB 0x200000U ///< do not use local value for global vimrc +#define P_NFNAME 0x400000U ///< only normal file name chars allowed +#define P_INSECURE 0x800000U ///< option was set from a modeline +#define P_PRI_MKRC 0x1000000U ///< priority for :mkvimrc (setting option + ///< has side effects) +#define P_NO_ML 0x2000000U ///< not allowed in modeline +#define P_CURSWANT 0x4000000U ///< update curswant required; not needed + ///< when there is a redraw flag #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \ @@ -263,30 +262,31 @@ typedef struct vimoption { #define PARAM_COUNT ARRAY_SIZE(options) -static char *(p_ambw_values[]) = {"single", "double", NULL}; -static char *(p_bg_values[]) = {"light", "dark", NULL}; -static char *(p_nf_values[]) = {"octal", "hex", "alpha", NULL}; -static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL}; -static char *(p_wop_values[]) = {"tagfile", NULL}; -static char *(p_wak_values[]) = {"yes", "menu", "no", NULL}; -static char *(p_mousem_values[]) = -{"extend", "popup", "popup_setpos", "mac", NULL}; -static char *(p_sel_values[]) = {"inclusive", "exclusive", "old", NULL}; -static char *(p_slm_values[]) = {"mouse", "key", "cmd", NULL}; -static char *(p_km_values[]) = {"startsel", "stopsel", NULL}; -static char *(p_scbopt_values[]) = {"ver", "hor", "jump", NULL}; -static char *(p_debug_values[]) = {"msg", "throw", "beep", NULL}; -static char *(p_ead_values[]) = {"both", "ver", "hor", NULL}; -static char *(p_buftype_values[]) = -{"nofile", "nowrite", "quickfix", "help", "acwrite", "terminal", NULL}; -static char *(p_bufhidden_values[]) = {"hide", "unload", "delete", "wipe", NULL}; -static char *(p_bs_values[]) = {"indent", "eol", "start", NULL}; -static char *(p_fdm_values[]) = {"manual", "expr", "marker", "indent", "syntax", - "diff", - NULL}; -static char *(p_fcl_values[]) = {"all", NULL}; -static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", - "noinsert", "noselect", NULL}; +static char *(p_ambw_values[]) = { "single", "double", NULL }; +static char *(p_bg_values[]) = { "light", "dark", NULL }; +static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", NULL }; +static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL }; +static char *(p_wop_values[]) = { "tagfile", NULL }; +static char *(p_wak_values[]) = { "yes", "menu", "no", NULL }; +static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", + "mac", NULL }; +static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL }; +static char *(p_slm_values[]) = { "mouse", "key", "cmd", NULL }; +static char *(p_km_values[]) = { "startsel", "stopsel", NULL }; +static char *(p_scbopt_values[]) = { "ver", "hor", "jump", NULL }; +static char *(p_debug_values[]) = { "msg", "throw", "beep", NULL }; +static char *(p_ead_values[]) = { "both", "ver", "hor", NULL }; +static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix", + "help", "acwrite", "terminal", NULL }; + +static char *(p_bufhidden_values[]) = { "hide", "unload", "delete", + "wipe", NULL }; +static char *(p_bs_values[]) = { "indent", "eol", "start", NULL }; +static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", + "syntax", "diff", NULL }; +static char *(p_fcl_values[]) = { "all", NULL }; +static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", + "noinsert", "noselect", NULL }; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.c.generated.h" @@ -712,15 +712,11 @@ void set_init_1(void) /* Must be before option_expand(), because that one needs vim_isIDc() */ didset_options(); - /* Use the current chartab for the generic chartab. */ + // Use the current chartab for the generic chartab. This is not in + // didset_options() because it only depends on 'encoding'. init_spell_chartab(); /* - * initialize the table for 'breakat'. - */ - fill_breakat_flags(); - - /* * Expand environment variables and things like "~" for the defaults. * If option_expand() returns non-NULL the variable is expanded. This can * only happen for non-indirect options. @@ -752,14 +748,8 @@ void set_init_1(void) } } - /* Initialize the highlight_attr[] table. */ - highlight_changed(); - save_file_ff(curbuf); /* Buffer is unchanged */ - /* Parse default for 'wildmode' */ - check_opt_wim(); - /* Detect use of mlterm. * Mlterm is a terminal emulator akin to xterm that has some special * abilities (bidi namely). @@ -769,65 +759,20 @@ void set_init_1(void) if (os_env_exists("MLTERM")) set_option_value((char_u *)"tbidi", 1L, NULL, 0); - /* Parse default for 'fillchars'. */ - (void)set_chars_option(&p_fcs); - - /* Parse default for 'listchars'. */ - (void)set_chars_option(&p_lcs); + didset_options2(); - /* enc_locale() will try to find the encoding of the current locale. */ + // enc_locale() will try to find the encoding of the current locale. + // This will be used when 'default' is used as encoding specifier + // in 'fileencodings' char_u *p = enc_locale(); - if (p != NULL) { - char_u *save_enc; - - /* Try setting 'encoding' and check if the value is valid. - * If not, go back to the default "utf-8". */ - save_enc = p_enc; - p_enc = (char_u *) p; - if (STRCMP(p_enc, "gb18030") == 0) { - /* We don't support "gb18030", but "cp936" is a good substitute - * for practical purposes, thus use that. It's not an alias to - * still support conversion between gb18030 and utf-8. */ - p_enc = vim_strsave((char_u *)"cp936"); - xfree(p); - } - if (mb_init() == NULL) { - opt_idx = findoption((char_u *)"encoding"); - if (opt_idx >= 0) { - options[opt_idx].def_val[VI_DEFAULT] = p_enc; - options[opt_idx].flags |= P_DEF_ALLOCED; - } - -#if defined(MSWIN) || defined(MACOS) - if (STRCMP(p_enc, "latin1") == 0 - || enc_utf8 - ) { - /* Adjust the default for 'isprint' and 'iskeyword' to match - * latin1. */ - set_string_option_direct((char_u *)"isp", -1, - ISP_LATIN1, OPT_FREE, SID_NONE); - set_string_option_direct((char_u *)"isk", -1, - ISK_LATIN1, OPT_FREE, SID_NONE); - opt_idx = findoption((char_u *)"isp"); - if (opt_idx >= 0) - options[opt_idx].def_val[VIM_DEFAULT] = ISP_LATIN1; - opt_idx = findoption((char_u *)"isk"); - if (opt_idx >= 0) - options[opt_idx].def_val[VIM_DEFAULT] = ISK_LATIN1; - (void)init_chartab(); - } -#endif - - } else { - xfree(p_enc); - // mb_init() failed; fallback to utf8 and try again. - p_enc = save_enc; - mb_init(); - } - } else { - // enc_locale() failed; initialize the default (utf8). - mb_init(); + if (p == NULL) { + // use utf-8 as 'default' if locale encoding can't be detected. + p = vim_strsave((char_u *)"utf-8"); } + fenc_default = p; + + // Initialize multibyte (utf-8) handling + mb_init(); // Don't change &encoding when resetting to defaults with ":set all&". opt_idx = findoption((char_u *)"encoding"); @@ -1018,12 +963,9 @@ void set_init_2(void) */ void set_init_3(void) { -#if defined(UNIX) || defined(WIN3264) - /* - * Set 'shellpipe' and 'shellredir', depending on the 'shell' option. - * This is done after other initializations, where 'shell' might have been - * set, but only if they have not been set before. - */ + // Set 'shellpipe' and 'shellredir', depending on the 'shell' option. + // This is done after other initializations, where 'shell' might have been + // set, but only if they have not been set before. int idx_srr; int do_srr; int idx_sp; @@ -1080,8 +1022,6 @@ void set_init_3(void) } xfree(p); } -#endif - set_title_defaults(); } @@ -1198,9 +1138,12 @@ do_set ( */ arg += 3; if (*arg == '&') { - ++arg; - /* Only for :set command set global value of local options. */ + arg++; + // Only for :set command set global value of local options. set_options_default(OPT_FREE | opt_flags); + didset_options(); + didset_options2(); + redraw_all_later(CLEAR); } else { showoptions(1, opt_flags); did_show = TRUE; @@ -1235,28 +1178,27 @@ do_set ( errmsg = e_invarg; goto skip; } - arg[len] = NUL; /* put NUL after name */ - if (arg[1] == 't' && arg[2] == '_') /* could be term code */ - opt_idx = findoption(arg + 1); - arg[len++] = '>'; /* restore '>' */ - if (opt_idx == -1) + if (arg[1] == 't' && arg[2] == '_') { // could be term code + opt_idx = findoption_len(arg + 1, (size_t) (len - 1)); + } + len++; + if (opt_idx == -1) { key = find_key_option(arg + 1); + } } else { len = 0; - /* - * The two characters after "t_" may not be alphanumeric. - */ - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) + // The two characters after "t_" may not be alphanumeric. + if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) { len = 4; - else - while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') - ++len; - nextchar = arg[len]; - arg[len] = NUL; /* put NUL after name */ - opt_idx = findoption(arg); - arg[len] = nextchar; /* restore nextchar */ - if (opt_idx == -1) + } else { + while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') { + len++; + } + } + opt_idx = findoption_len(arg, (size_t) len); + if (opt_idx == -1) { key = find_key_option(arg); + } } /* remember character after option name */ @@ -1482,7 +1424,7 @@ do_set ( } else if (*arg == '-' || ascii_isdigit(*arg)) { // Allow negative (for 'undolevels'), octal and // hex numbers. - vim_str2nr(arg, NULL, &i, true, true, &value, NULL); + vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0); if (arg[i] != NUL && !ascii_iswhite(arg[i])) { errmsg = e_invarg; goto skip; @@ -1506,7 +1448,7 @@ do_set ( char_u *oldval = NULL; // previous value if *varp char_u *newval; char_u *origval = NULL; - char_u *saved_origval = NULL; + char *saved_origval = NULL; unsigned newlen; int comma; int bs; @@ -1723,6 +1665,13 @@ do_set ( && *newval != NUL); if (adding) { i = (int)STRLEN(origval); + // Strip a trailing comma, would get 2. + if (comma && i > 1 + && (flags & P_ONECOMMA) == P_ONECOMMA + && origval[i - 1] == ',' + && origval[i - 2] != '\\') { + i--; + } memmove(newval + i + comma, newval, STRLEN(newval) + 1); memmove(newval, origval, (size_t)i); @@ -1776,7 +1725,7 @@ do_set ( if (!starting && origval != NULL) { // origval may be freed by // did_set_string_option(), make a copy. - saved_origval = vim_strsave(origval); + saved_origval = xstrdup((char *) origval); } /* Handle side effects, and set the global value for @@ -1791,11 +1740,10 @@ do_set ( } if (saved_origval != NULL) { - char_u buf_type[7]; - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + char buf_type[7]; + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); - set_vim_var_string(VV_OPTION_NEW, - *(char_u **)varp, -1); + set_vim_var_string(VV_OPTION_NEW, *(char **) varp, -1); set_vim_var_string(VV_OPTION_OLD, saved_origval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); apply_autocmds(EVENT_OPTIONSET, @@ -2109,13 +2057,36 @@ static void didset_options(void) (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true); (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true); (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); + (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); (void)spell_check_msm(); (void)spell_check_sps(); (void)compile_cap_prog(curwin->w_s); - /* set cedit_key */ + (void)did_set_spell_option(true); + // set cedit_key (void)check_cedit(); briopt_check(curwin); + // initialize the table for 'breakat'. + fill_breakat_flags(); +} + +// More side effects of setting options. +static void didset_options2(void) +{ + // Initialize the highlight_attr[] table. + (void)highlight_changed(); + + // Parse default for 'clipboard'. + (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); + + // Parse default for 'fillchars'. + (void)set_chars_option(&p_fcs); + + // Parse default for 'listchars'. + (void)set_chars_option(&p_lcs); + + // Parse default for 'wildmode'. + check_opt_wim(); } /* @@ -2174,6 +2145,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_ep); check_string_option(&buf->b_p_path); check_string_option(&buf->b_p_tags); + check_string_option(&buf->b_p_tc); check_string_option(&buf->b_p_dict); check_string_option(&buf->b_p_tsr); check_string_option(&buf->b_p_lw); @@ -2353,7 +2325,7 @@ set_string_option ( char_u *s; char_u **varp; char_u *oldval; - char_u *saved_oldval = NULL; + char *saved_oldval = NULL; char_u *r = NULL; if (options[opt_idx].var == NULL) /* don't set hidden option */ @@ -2369,7 +2341,7 @@ set_string_option ( *varp = s; if (!starting) { - saved_oldval = vim_strsave(oldval); + saved_oldval = xstrdup((char *) oldval); } if ((r = did_set_string_option(opt_idx, varp, (int)true, oldval, NULL, @@ -2378,10 +2350,10 @@ set_string_option ( // call autocommand after handling side effects if (saved_oldval != NULL) { - char_u buf_type[7]; - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + char buf_type[7]; + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); - set_vim_var_string(VV_OPTION_NEW, *varp, -1); + set_vim_var_string(VV_OPTION_NEW, (char *) (*varp), -1); set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); apply_autocmds(EVENT_OPTIONSET, @@ -2894,22 +2866,7 @@ did_set_string_option ( || varp == &(curwin->w_s->b_p_spf)) { // When 'spelllang' or 'spellfile' is set and there is a window for this // buffer in which 'spell' is set load the wordlists. - if (varp == &(curwin->w_s->b_p_spf)) { - int l = (int)STRLEN(curwin->w_s->b_p_spf); - if (l > 0 - && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) { - errmsg = e_invarg; - } - } - - if (errmsg == NULL) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == curbuf && wp->w_p_spell) { - errmsg = did_set_spelllang(wp); - break; - } - } - } + errmsg = did_set_spell_option(varp == &(curwin->w_s->b_p_spf)); } /* When 'spellcapcheck' is set compile the regexp program. */ else if (varp == &(curwin->w_s->b_p_spc)) { @@ -3002,13 +2959,17 @@ did_set_string_option ( } /* 'completeopt' */ else if (varp == &p_cot) { - if (check_opt_strings(p_cot, p_cot_values, TRUE) != OK) + if (check_opt_strings(p_cot, p_cot_values, true) != OK) { errmsg = e_invarg; + } else { + completeopt_was_set(); + } } /* 'pastetoggle': translate key codes like in a mapping */ else if (varp == &p_pt) { if (*p_pt) { - (void)replace_termcodes(p_pt, &p, TRUE, TRUE, FALSE); + (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, false, + CPO_TO_CPO_FLAGS); if (p != NULL) { if (new_value_alloced) free_string_option(p_pt); @@ -3028,6 +2989,24 @@ did_set_string_option ( if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) { errmsg = e_invarg; } + } else if (gvarp == &p_tc) { // 'tagcase' + unsigned int *flags; + + if (opt_flags & OPT_LOCAL) { + p = curbuf->b_p_tc; + flags = &curbuf->b_tc_flags; + } else { + p = p_tc; + flags = &tc_flags; + } + + if ((opt_flags & OPT_LOCAL) && *p == NUL) { + // make the local value empty: use the global value + *flags = 0; + } else if (*p == NUL + || opt_strings_flags(p, p_tc_values, flags, false) != OK) { + errmsg = e_invarg; + } } else if (varp == &p_cmp) { // 'casemap' if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) errmsg = e_invarg; @@ -3465,6 +3444,30 @@ char_u *check_stl_option(char_u *s) return NULL; } +static char_u *did_set_spell_option(bool is_spellfile) +{ + char_u *errmsg = NULL; + + if (is_spellfile) { + int l = (int)STRLEN(curwin->w_s->b_p_spf); + if (l > 0 + && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) { + errmsg = e_invarg; + } + } + + if (errmsg == NULL) { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer == curbuf && wp->w_p_spell) { + errmsg = did_set_spelllang(wp); + break; + } + } + } + + return errmsg; +} + /* * Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'. * Return error message when failed, NULL when OK. @@ -3592,6 +3595,9 @@ set_bool_option ( /* when 'endofline' is changed, redraw the window title */ else if ((int *)varp == &curbuf->b_p_eol) { redraw_titles(); + } else if ((int *)varp == &curbuf->b_p_fixeol) { + // when 'fixeol' is changed, redraw the window title + redraw_titles(); } /* when 'bomb' is changed, redraw the window title and tab page text */ else if ((int *)varp == &curbuf->b_p_bomb) { @@ -3817,7 +3823,7 @@ set_bool_option ( msg_source(hl_attr(HLF_W)); MSG_ATTR(_(w_arabic), hl_attr(HLF_W)); - set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_arabic), -1); + set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1); } /* set 'delcombine' */ @@ -3864,14 +3870,14 @@ set_bool_option ( options[opt_idx].flags |= P_WAS_SET; if (!starting) { - char_u buf_old[2]; - char_u buf_new[2]; - char_u buf_type[7]; - vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%d", + char buf_old[2]; + char buf_new[2]; + char buf_type[7]; + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true: false); - vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%d", + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true: false); - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); @@ -4254,12 +4260,12 @@ set_num_option ( options[opt_idx].flags |= P_WAS_SET; if (!starting && errmsg == NULL) { - char_u buf_old[NUMBUFLEN]; - char_u buf_new[NUMBUFLEN]; - char_u buf_type[7]; - vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); - vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%ld", value); - vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s", + char buf_old[NUMBUFLEN]; + char buf_new[NUMBUFLEN]; + char buf_type[7]; + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); @@ -4301,14 +4307,16 @@ static void check_redraw(uint32_t flags) redraw_all_later(NOT_VALID); } -/* - * Find index for option 'arg'. - * Return -1 if not found. - */ -static int findoption(char_u *arg) +/// Find index for named option +/// +/// @param[in] arg Option to find index for. +/// @param[in] len Length of the option. +/// +/// @return Index of the option or -1 if option was not found. +int findoption_len(const char_u *const arg, const size_t len) { - char *s, *p; - static short quick_tab[27] = {0, 0}; /* quick access table */ + char *s, *p; + static int quick_tab[27] = { 0, 0 }; // quick access table int is_term_opt; /* @@ -4332,25 +4340,31 @@ static int findoption(char_u *arg) /* * Check for name starting with an illegal character. */ - if (arg[0] < 'a' || arg[0] > 'z') + if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { return -1; + } int opt_idx; - is_term_opt = (arg[0] == 't' && arg[1] == '_'); - if (is_term_opt) + is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); + if (is_term_opt) { opt_idx = quick_tab[26]; - else + } else { opt_idx = quick_tab[CharOrdLow(arg[0])]; + } + // Match full name for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (STRCMP(arg, s) == 0) /* match full name */ + if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) { break; + } } if (s == NULL && !is_term_opt) { opt_idx = quick_tab[CharOrdLow(arg[0])]; + // Match short name for (; options[opt_idx].fullname != NULL; opt_idx++) { s = options[opt_idx].shortname; - if (s != NULL && STRCMP(arg, s) == 0) /* match short name */ + if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) { break; + } s = NULL; } } @@ -4418,6 +4432,15 @@ bool set_tty_option(char *name, char *value) } /* + * Find index for option 'arg'. + * Return -1 if not found. + */ +static int findoption(char_u *arg) +{ + return findoption_len(arg, STRLEN(arg)); +} + +/* * Get the value for an option. * * Returns: @@ -4670,40 +4693,35 @@ char_u *get_highlight_default(void) return (char_u *)NULL; } -char_u *get_encoding_default(void) -{ - int i; - - i = findoption((char_u *)"enc"); - if (i >= 0) - return options[i].def_val[VI_DEFAULT]; - return (char_u *)NULL; -} - /* * Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. */ -static int find_key_option(char_u *arg) +int find_key_option_len(const char_u *arg, size_t len) { int key; int modifiers; - /* - * Don't use get_special_key_code() for t_xx, we don't want it to call - * add_termcap_entry(). - */ - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) + // Don't use get_special_key_code() for t_xx, we don't want it to call + // add_termcap_entry(). + if (len >= 4 && arg[0] == 't' && arg[1] == '_') { key = TERMCAP2KEY(arg[2], arg[3]); - else { - --arg; /* put arg at the '<' */ + } else { + arg--; // put arg at the '<' modifiers = 0; - key = find_special_key(&arg, &modifiers, TRUE, TRUE); - if (modifiers) /* can't handle modifiers here */ + key = find_special_key(&arg, len + 1, &modifiers, true, true); + if (modifiers) { // can't handle modifiers here key = 0; + } } return key; } +static int find_key_option(const char_u *arg) +{ + return find_key_option_len(arg, STRLEN(arg)); +} + + /* * if 'all' == 0: show changed options * if 'all' == 1: show all normal options @@ -4764,9 +4782,10 @@ showoptions ( option_value2string(p, opt_flags); len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1; } - if ((len <= INC - GAP && run == 1) || - (len > INC - GAP && run == 2)) + if ((len <= INC - GAP && run == 1) + || (len > INC - GAP && run == 2)) { items[item_count++] = p; + } } } @@ -4955,18 +4974,15 @@ int makeset(FILE *fd, int opt_flags, int local_only) } else { /* P_STRING */ int do_endif = FALSE; - /* Don't set 'syntax' and 'filetype' again if the value is - * already right, avoids reloading the syntax file. */ - if ( - p->indir == PV_SYN - || - p->indir == PV_FT - ) { + // Don't set 'syntax' and 'filetype' again if the value is + // already right, avoids reloading the syntax file. + if (p->indir == PV_SYN || p->indir == PV_FT) { if (fprintf(fd, "if &%s != '%s'", p->fullname, - *(char_u **)(varp)) < 0 - || put_eol(fd) < 0) + *(char_u **)(varp)) < 0 + || put_eol(fd) < 0) { return FAIL; - do_endif = TRUE; + } + do_endif = true; } if (put_setstring(fd, cmd, p->fullname, (char_u **)varp, (p->flags & P_EXPAND) != 0) == FAIL) @@ -5139,6 +5155,10 @@ void unset_global_local_option(char *name, void *from) case PV_TAGS: clear_string_option(&buf->b_p_tags); break; + case PV_TC: + clear_string_option(&buf->b_p_tc); + buf->b_tc_flags = 0; + break; case PV_DEF: clear_string_option(&buf->b_p_def); break; @@ -5192,6 +5212,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) case PV_PATH: return (char_u *)&(curbuf->b_p_path); case PV_AR: return (char_u *)&(curbuf->b_p_ar); case PV_TAGS: return (char_u *)&(curbuf->b_p_tags); + case PV_TC: return (char_u *)&(curbuf->b_p_tc); case PV_DEF: return (char_u *)&(curbuf->b_p_def); case PV_INC: return (char_u *)&(curbuf->b_p_inc); case PV_DICT: return (char_u *)&(curbuf->b_p_dict); @@ -5229,6 +5250,8 @@ static char_u *get_varp(vimoption_T *p) ? (char_u *)&(curbuf->b_p_ar) : p->var; case PV_TAGS: return *curbuf->b_p_tags != NUL ? (char_u *)&(curbuf->b_p_tags) : p->var; + case PV_TC: return *curbuf->b_p_tc != NUL + ? (char_u *)&(curbuf->b_p_tc) : p->var; case PV_BKC: return *curbuf->b_p_bkc != NUL ? (char_u *)&(curbuf->b_p_bkc) : p->var; case PV_DEF: return *curbuf->b_p_def != NUL @@ -5304,6 +5327,7 @@ static char_u *get_varp(vimoption_T *p) case PV_CFU: return (char_u *)&(curbuf->b_p_cfu); case PV_OFU: return (char_u *)&(curbuf->b_p_ofu); case PV_EOL: return (char_u *)&(curbuf->b_p_eol); + case PV_FIXEOL: return (char_u *)&(curbuf->b_p_fixeol); case PV_ET: return (char_u *)&(curbuf->b_p_et); case PV_FENC: return (char_u *)&(curbuf->b_p_fenc); case PV_FF: return (char_u *)&(curbuf->b_p_ff); @@ -5548,7 +5572,9 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_bin = p_bin; buf->b_p_bomb = p_bomb; buf->b_p_et = p_et; + buf->b_p_fixeol = p_fixeol; buf->b_p_et_nobin = p_et_nobin; + buf->b_p_et_nopaste = p_et_nopaste; buf->b_p_ml = p_ml; buf->b_p_ml_nobin = p_ml_nobin; buf->b_p_inf = p_inf; @@ -5605,6 +5631,8 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_kp = empty_option; buf->b_p_path = empty_option; buf->b_p_tags = empty_option; + buf->b_p_tc = empty_option; + buf->b_tc_flags = 0; buf->b_p_def = empty_option; buf->b_p_inc = empty_option; buf->b_p_inex = vim_strsave(p_inex); @@ -5971,13 +5999,17 @@ option_value2string ( if (opp->flags & P_NUM) { long wc = 0; - if (wc_use_keyname(varp, &wc)) - STRCPY(NameBuff, get_special_key_name((int)wc, 0)); - else if (wc != 0) - STRCPY(NameBuff, transchar((int)wc)); - else - sprintf((char *)NameBuff, "%" PRId64, (int64_t)*(long *)varp); - } else { /* P_STRING */ + if (wc_use_keyname(varp, &wc)) { + STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff)); + } else if (wc != 0) { + STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff)); + } else { + snprintf((char *)NameBuff, + sizeof(NameBuff), + "%" PRId64, + (int64_t)*(long *)varp); + } + } else { // P_STRING varp = *(char_u **)(varp); if (varp == NULL) /* just in case */ NameBuff[0] = NUL; @@ -6187,16 +6219,14 @@ int has_format_option(int x) return vim_strchr(curbuf->b_p_fo, x) != NULL; } -/* - * Return TRUE if "x" is present in 'shortmess' option, or - * 'shortmess' contains 'a' and "x" is present in SHM_A. - */ -int shortmess(int x) +/// @returns true if "x" is present in 'shortmess' option, or +/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS. +bool shortmess(int x) { - return p_shm != NULL && - ( vim_strchr(p_shm, x) != NULL - || (vim_strchr(p_shm, 'a') != NULL - && vim_strchr((char_u *)SHM_A, x) != NULL)); + return (p_shm != NULL + && (vim_strchr(p_shm, x) != NULL + || (vim_strchr(p_shm, 'a') != NULL + && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL))); } /* @@ -6206,6 +6236,7 @@ static void paste_option_changed(void) { static int old_p_paste = FALSE; static int save_sm = 0; + static int save_sta = 0; static int save_ru = 0; static int save_ri = 0; static int save_hkmap = 0; @@ -6222,40 +6253,44 @@ static void paste_option_changed(void) buf->b_p_wm_nopaste = buf->b_p_wm; buf->b_p_sts_nopaste = buf->b_p_sts; buf->b_p_ai_nopaste = buf->b_p_ai; + buf->b_p_et_nopaste = buf->b_p_et; } - /* save global options */ + // save global options save_sm = p_sm; + save_sta = p_sta; save_ru = p_ru; save_ri = p_ri; save_hkmap = p_hkmap; - /* save global values for local buffer options */ + // save global values for local buffer options + p_ai_nopaste = p_ai; + p_et_nopaste = p_et; + p_sts_nopaste = p_sts; p_tw_nopaste = p_tw; p_wm_nopaste = p_wm; - p_sts_nopaste = p_sts; - p_ai_nopaste = p_ai; } - /* - * Always set the option values, also when 'paste' is set when it is - * already on. - */ - /* set options for each buffer */ + // Always set the option values, also when 'paste' is set when it is + // already on. + // set options for each buffer FOR_ALL_BUFFERS(buf) { - buf->b_p_tw = 0; /* textwidth is 0 */ - buf->b_p_wm = 0; /* wrapmargin is 0 */ - buf->b_p_sts = 0; /* softtabstop is 0 */ - buf->b_p_ai = 0; /* no auto-indent */ - } - - /* set global options */ - p_sm = 0; /* no showmatch */ - if (p_ru) - status_redraw_all(); /* redraw to remove the ruler */ - p_ru = 0; /* no ruler */ - p_ri = 0; /* no reverse insert */ - p_hkmap = 0; /* no Hebrew keyboard */ - /* set global values for local buffer options */ + buf->b_p_tw = 0; // textwidth is 0 + buf->b_p_wm = 0; // wrapmargin is 0 + buf->b_p_sts = 0; // softtabstop is 0 + buf->b_p_ai = 0; // no auto-indent + buf->b_p_et = 0; // no expandtab + } + + // set global options + p_sm = 0; // no showmatch + p_sta = 0; // no smarttab + if (p_ru) { + status_redraw_all(); // redraw to remove the ruler + } + p_ru = 0; // no ruler + p_ri = 0; // no reverse insert + p_hkmap = 0; // no Hebrew keyboard + // set global values for local buffer options p_tw = 0; p_wm = 0; p_sts = 0; @@ -6271,20 +6306,24 @@ static void paste_option_changed(void) buf->b_p_wm = buf->b_p_wm_nopaste; buf->b_p_sts = buf->b_p_sts_nopaste; buf->b_p_ai = buf->b_p_ai_nopaste; + buf->b_p_et = buf->b_p_et_nopaste; } /* restore global options */ p_sm = save_sm; - if (p_ru != save_ru) - status_redraw_all(); /* redraw to draw the ruler */ + p_sta = save_sta; + if (p_ru != save_ru) { + status_redraw_all(); // redraw to draw the ruler + } p_ru = save_ru; p_ri = save_ri; p_hkmap = save_hkmap; - /* set global values for local buffer options */ + // set global values for local buffer options + p_ai = p_ai_nopaste; + p_et = p_et_nopaste; + p_sts = p_sts_nopaste; p_tw = p_tw_nopaste; p_wm = p_wm_nopaste; - p_sts = p_sts_nopaste; - p_ai = p_ai_nopaste; } old_p_paste = p_paste; @@ -6483,6 +6522,7 @@ void save_file_ff(buf_T *buf) * from when editing started (save_file_ff() called). * Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was * changed and 'binary' is not set. + * Also when 'endofline' was changed and 'fixeol' is not set. * When "ignore_empty" is true don't consider a new, empty buffer to be * changed. */ @@ -6497,9 +6537,9 @@ bool file_ff_differs(buf_T *buf, bool ignore_empty) && *ml_get_buf(buf, (linenr_T)1, FALSE) == NUL) return FALSE; if (buf->b_start_ffc != *buf->b_p_ff) - return TRUE; - if (buf->b_p_bin && buf->b_start_eol != buf->b_p_eol) - return TRUE; + return true; + if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) + return true; if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) return TRUE; if (buf->b_start_fenc == NULL) diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index d4d3410d5c..904e97f8ca 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include "nvim/types.h" +#include "nvim/macros.h" // For EXTERN // option_defs.h: definition of global variables for settable options @@ -151,26 +152,42 @@ #define COCU_ALL "nvic" /* flags for 'concealcursor' */ -/* characters for p_shm option: */ -#define SHM_RO 'r' /* readonly */ -#define SHM_MOD 'm' /* modified */ -#define SHM_FILE 'f' /* (file 1 of 2) */ -#define SHM_LAST 'i' /* last line incomplete */ -#define SHM_TEXT 'x' /* tx instead of textmode */ -#define SHM_LINES 'l' /* "L" instead of "lines" */ -#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */ -#define SHM_WRI 'w' /* "[w]" instead of "written" */ -#define SHM_A "rmfixlnw" /* represented by 'a' flag */ -#define SHM_WRITE 'W' /* don't use "written" at all */ -#define SHM_TRUNC 't' /* trunctate file messages */ -#define SHM_TRUNCALL 'T' /* trunctate all messages */ -#define SHM_OVER 'o' /* overwrite file messages */ -#define SHM_OVERALL 'O' /* overwrite more messages */ -#define SHM_SEARCH 's' /* no search hit bottom messages */ -#define SHM_ATTENTION 'A' /* no ATTENTION messages */ -#define SHM_INTRO 'I' /* intro messages */ -#define SHM_COMPLETIONMENU 'c' // completion menu messages -#define SHM_ALL "rmfixlnwaWtToOsAIc" /* all possible flags for 'shm' */ +/// characters for p_shm option: +enum { + SHM_RO = 'r', ///< Readonly. + SHM_MOD = 'm', ///< Modified. + SHM_FILE = 'f', ///< (file 1 of 2) + SHM_LAST = 'i', ///< Last line incomplete. + SHM_TEXT = 'x', ///< Tx instead of textmode. + SHM_LINES = 'l', ///< "L" instead of "lines". + SHM_NEW = 'n', ///< "[New]" instead of "[New file]". + SHM_WRI = 'w', ///< "[w]" instead of "written". + SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS. + SHM_WRITE = 'W', ///< Don't use "written" at all. + SHM_TRUNC = 't', ///< Trunctate file messages. + SHM_TRUNCALL = 'T', ///< Trunctate all messages. + SHM_OVER = 'o', ///< Overwrite file messages. + SHM_OVERALL = 'O', ///< Overwrite more messages. + SHM_SEARCH = 's', ///< No search hit bottom messages. + SHM_ATTENTION = 'A', ///< No ATTENTION messages. + SHM_INTRO = 'I', ///< Intro messages. + SHM_COMPLETIONMENU = 'c', ///< Completion menu messages. + SHM_RECORDING = 'q', ///< Short recording message. + SHM_FILEINFO = 'F', ///< No file info messages. +}; +/// Represented by 'a' flag. +#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \ + SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ + 0, \ +}) +/// All possible flags for 'shm'. +#define SHM_ALL ((char_u[]) { \ + SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ + SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \ + SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \ + SHM_RECORDING, SHM_FILEINFO, \ + 0, \ +}) /* characters for p_go: */ #define GO_ASEL 'a' /* autoselect */ @@ -209,44 +226,58 @@ #define COM_ALL "nbsmexflrO" /* all flags for 'comments' option */ #define COM_MAX_LEN 50 /* maximum length of a part */ -/* flags for 'statusline' option */ -#define STL_FILEPATH 'f' /* path of file in buffer */ -#define STL_FULLPATH 'F' /* full path of file in buffer */ -#define STL_FILENAME 't' /* last part (tail) of file path */ -#define STL_COLUMN 'c' /* column og cursor*/ -#define STL_VIRTCOL 'v' /* virtual column */ -#define STL_VIRTCOL_ALT 'V' /* - with 'if different' display */ -#define STL_LINE 'l' /* line number of cursor */ -#define STL_NUMLINES 'L' /* number of lines in buffer */ -#define STL_BUFNO 'n' /* current buffer number */ -#define STL_KEYMAP 'k' /* 'keymap' when active */ -#define STL_OFFSET 'o' /* offset of character under cursor*/ -#define STL_OFFSET_X 'O' /* - in hexadecimal */ -#define STL_BYTEVAL 'b' /* byte value of character */ -#define STL_BYTEVAL_X 'B' /* - in hexadecimal */ -#define STL_ROFLAG 'r' /* readonly flag */ -#define STL_ROFLAG_ALT 'R' /* - other display */ -#define STL_HELPFLAG 'h' /* window is showing a help file */ -#define STL_HELPFLAG_ALT 'H' /* - other display */ -#define STL_FILETYPE 'y' /* 'filetype' */ -#define STL_FILETYPE_ALT 'Y' /* - other display */ -#define STL_PREVIEWFLAG 'w' /* window is showing the preview buf */ -#define STL_PREVIEWFLAG_ALT 'W' /* - other display */ -#define STL_MODIFIED 'm' /* modified flag */ -#define STL_MODIFIED_ALT 'M' /* - other display */ -#define STL_QUICKFIX 'q' /* quickfix window description */ -#define STL_PERCENTAGE 'p' /* percentage through file */ -#define STL_ALTPERCENT 'P' /* percentage as TOP BOT ALL or NN% */ -#define STL_ARGLISTSTAT 'a' /* argument list status as (x of y) */ -#define STL_PAGENUM 'N' /* page number (when printing)*/ -#define STL_VIM_EXPR '{' /* start of expression to substitute */ -#define STL_MIDDLEMARK '=' /* separation between left and right */ -#define STL_TRUNCMARK '<' /* truncation mark if line is too long*/ -#define STL_USER_HL '*' /* highlight from (User)1..9 or 0 */ -#define STL_HIGHLIGHT '#' /* highlight name */ -#define STL_TABPAGENR 'T' /* tab page label nr */ -#define STL_TABCLOSENR 'X' /* tab page close nr */ -#define STL_ALL ((char_u *) "fFtcvVlLknoObBrRhHmYyWwMqpPaN{#") +/// 'statusline' option flags +enum { + STL_FILEPATH = 'f', ///< Path of file in buffer. + STL_FULLPATH = 'F', ///< Full path of file in buffer. + STL_FILENAME = 't', ///< Last part (tail) of file path. + STL_COLUMN = 'c', ///< Column og cursor. + STL_VIRTCOL = 'v', ///< Virtual column. + STL_VIRTCOL_ALT = 'V', ///< - with 'if different' display. + STL_LINE = 'l', ///< Line number of cursor. + STL_NUMLINES = 'L', ///< Number of lines in buffer. + STL_BUFNO = 'n', ///< Current buffer number. + STL_KEYMAP = 'k', ///< 'keymap' when active. + STL_OFFSET = 'o', ///< Offset of character under cursor. + STL_OFFSET_X = 'O', ///< - in hexadecimal. + STL_BYTEVAL = 'b', ///< Byte value of character. + STL_BYTEVAL_X = 'B', ///< - in hexadecimal. + STL_ROFLAG = 'r', ///< Readonly flag. + STL_ROFLAG_ALT = 'R', ///< - other display. + STL_HELPFLAG = 'h', ///< Window is showing a help file. + STL_HELPFLAG_ALT = 'H', ///< - other display. + STL_FILETYPE = 'y', ///< 'filetype'. + STL_FILETYPE_ALT = 'Y', ///< - other display. + STL_PREVIEWFLAG = 'w', ///< Window is showing the preview buf. + STL_PREVIEWFLAG_ALT = 'W', ///< - other display. + STL_MODIFIED = 'm', ///< Modified flag. + STL_MODIFIED_ALT = 'M', ///< - other display. + STL_QUICKFIX = 'q', ///< Quickfix window description. + STL_PERCENTAGE = 'p', ///< Percentage through file. + STL_ALTPERCENT = 'P', ///< Percentage as TOP BOT ALL or NN%. + STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y). + STL_PAGENUM = 'N', ///< Page number (when printing). + STL_VIM_EXPR = '{', ///< Start of expression to substitute. + STL_MIDDLEMARK = '=', ///< Separation between left and right. + STL_TRUNCMARK = '<', ///< Truncation mark if line is too long. + STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0. + STL_HIGHLIGHT = '#', ///< Highlight name. + STL_TABPAGENR = 'T', ///< Tab page label nr. + STL_TABCLOSENR = 'X', ///< Tab page close nr. + STL_CLICK_FUNC = '@', ///< Click region start. +}; +/// C string containing all 'statusline' option flags +#define STL_ALL ((char_u[]) { \ + STL_FILEPATH, STL_FULLPATH, STL_FILENAME, STL_COLUMN, STL_VIRTCOL, \ + STL_VIRTCOL_ALT, STL_LINE, STL_NUMLINES, STL_BUFNO, STL_KEYMAP, STL_OFFSET, \ + STL_OFFSET_X, STL_BYTEVAL, STL_BYTEVAL_X, STL_ROFLAG, STL_ROFLAG_ALT, \ + STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \ + STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \ + STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \ + STL_VIM_EXPR, STL_MIDDLEMARK, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \ + STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \ + 0, \ +}) /* flags used for parsed 'wildmode' */ #define WIM_FULL 1 @@ -405,27 +436,25 @@ static char *(p_fdo_values[]) = {"all", "block", "hor", "mark", "percent", # define FDO_INSERT 0x100 # define FDO_UNDO 0x200 # define FDO_JUMP 0x400 -EXTERN char_u *p_fp; /* 'formatprg' */ -#ifdef HAVE_FSYNC -EXTERN int p_fs; /* 'fsync' */ -#endif -EXTERN int p_gd; /* 'gdefault' */ -EXTERN char_u *p_pdev; /* 'printdevice' */ -EXTERN char_u *p_penc; /* 'printencoding' */ -EXTERN char_u *p_pexpr; /* 'printexpr' */ -EXTERN char_u *p_pmfn; /* 'printmbfont' */ -EXTERN char_u *p_pmcs; /* 'printmbcharset' */ -EXTERN char_u *p_pfn; /* 'printfont' */ -EXTERN char_u *p_popt; /* 'printoptions' */ -EXTERN char_u *p_header; /* 'printheader' */ -EXTERN int p_prompt; /* 'prompt' */ -EXTERN char_u *p_guicursor; /* 'guicursor' */ -EXTERN char_u *p_hf; /* 'helpfile' */ -EXTERN long p_hh; /* 'helpheight' */ -EXTERN char_u *p_hlg; /* 'helplang' */ -EXTERN int p_hid; /* 'hidden' */ -/* Use P_HID to check if a buffer is to be hidden when it is no longer - * visible in a window. */ +EXTERN char_u *p_fp; // 'formatprg' +EXTERN int p_fs; // 'fsync' +EXTERN int p_gd; // 'gdefault' +EXTERN char_u *p_pdev; // 'printdevice' +EXTERN char_u *p_penc; // 'printencoding' +EXTERN char_u *p_pexpr; // 'printexpr' +EXTERN char_u *p_pmfn; // 'printmbfont' +EXTERN char_u *p_pmcs; // 'printmbcharset' +EXTERN char_u *p_pfn; // 'printfont' +EXTERN char_u *p_popt; // 'printoptions' +EXTERN char_u *p_header; // 'printheader' +EXTERN int p_prompt; // 'prompt' +EXTERN char_u *p_guicursor; // 'guicursor' +EXTERN char_u *p_hf; // 'helpfile' +EXTERN long p_hh; // 'helpheight' +EXTERN char_u *p_hlg; // 'helplang' +EXTERN int p_hid; // 'hidden' +// Use P_HID to check if a buffer is to be hidden when it is no longer +// visible in a window. # define P_HID(buf) (buf_hide(buf)) EXTERN char_u *p_hl; /* 'highlight' */ EXTERN int p_hls; /* 'hlsearch' */ @@ -537,64 +566,74 @@ EXTERN int p_stmp; /* 'shelltemp' */ #ifdef BACKSLASH_IN_FILENAME EXTERN int p_ssl; /* 'shellslash' */ #endif -EXTERN char_u *p_stl; /* 'statusline' */ -EXTERN int p_sr; /* 'shiftround' */ -EXTERN char_u *p_shm; /* 'shortmess' */ -EXTERN char_u *p_sbr; /* 'showbreak' */ -EXTERN int p_sc; /* 'showcmd' */ -EXTERN int p_sft; /* 'showfulltag' */ -EXTERN int p_sm; /* 'showmatch' */ -EXTERN int p_smd; /* 'showmode' */ -EXTERN long p_ss; /* 'sidescroll' */ -EXTERN long p_siso; /* 'sidescrolloff' */ -EXTERN int p_scs; /* 'smartcase' */ -EXTERN int p_sta; /* 'smarttab' */ -EXTERN int p_sb; /* 'splitbelow' */ -EXTERN long p_tpm; /* 'tabpagemax' */ -EXTERN char_u *p_tal; /* 'tabline' */ -EXTERN char_u *p_sps; /* 'spellsuggest' */ -EXTERN int p_spr; /* 'splitright' */ -EXTERN int p_sol; /* 'startofline' */ -EXTERN char_u *p_su; /* 'suffixes' */ -EXTERN char_u *p_sws; /* 'swapsync' */ -EXTERN char_u *p_swb; /* 'switchbuf' */ +EXTERN char_u *p_stl; // 'statusline' +EXTERN int p_sr; // 'shiftround' +EXTERN char_u *p_shm; // 'shortmess' +EXTERN char_u *p_sbr; // 'showbreak' +EXTERN int p_sc; // 'showcmd' +EXTERN int p_sft; // 'showfulltag' +EXTERN int p_sm; // 'showmatch' +EXTERN int p_smd; // 'showmode' +EXTERN long p_ss; // 'sidescroll' +EXTERN long p_siso; // 'sidescrolloff' +EXTERN int p_scs; // 'smartcase' +EXTERN int p_sta; // 'smarttab' +EXTERN int p_sb; // 'splitbelow' +EXTERN long p_tpm; // 'tabpagemax' +EXTERN char_u *p_tal; // 'tabline' +EXTERN char_u *p_sps; // 'spellsuggest' +EXTERN int p_spr; // 'splitright' +EXTERN int p_sol; // 'startofline' +EXTERN char_u *p_su; // 'suffixes' +EXTERN char_u *p_swb; // 'switchbuf' EXTERN unsigned swb_flags; #ifdef IN_OPTION_C -static char *(p_swb_values[]) = {"useopen", "usetab", "split", "newtab", NULL}; +static char *(p_swb_values[]) = + { "useopen", "usetab", "split", "newtab", "vsplit", NULL }; #endif #define SWB_USEOPEN 0x001 #define SWB_USETAB 0x002 #define SWB_SPLIT 0x004 #define SWB_NEWTAB 0x008 -EXTERN int p_tbs; /* 'tagbsearch' */ -EXTERN long p_tl; /* 'taglength' */ -EXTERN int p_tr; /* 'tagrelative' */ -EXTERN char_u *p_tags; /* 'tags' */ -EXTERN int p_tgst; /* 'tagstack' */ -EXTERN int p_tbidi; /* 'termbidi' */ -EXTERN int p_terse; /* 'terse' */ -EXTERN int p_to; /* 'tildeop' */ -EXTERN int p_timeout; /* 'timeout' */ -EXTERN long p_tm; /* 'timeoutlen' */ -EXTERN int p_title; /* 'title' */ -EXTERN long p_titlelen; /* 'titlelen' */ -EXTERN char_u *p_titleold; /* 'titleold' */ -EXTERN char_u *p_titlestring; /* 'titlestring' */ -EXTERN char_u *p_tsr; /* 'thesaurus' */ -EXTERN int p_ttimeout; /* 'ttimeout' */ -EXTERN long p_ttm; /* 'ttimeoutlen' */ -EXTERN char_u *p_udir; /* 'undodir' */ -EXTERN long p_ul; /* 'undolevels' */ -EXTERN long p_ur; /* 'undoreload' */ -EXTERN long p_uc; /* 'updatecount' */ -EXTERN long p_ut; /* 'updatetime' */ -EXTERN char_u *p_fcs; /* 'fillchar' */ -EXTERN char_u *p_shada; /* 'shada' */ -EXTERN char_u *p_vdir; /* 'viewdir' */ -EXTERN char_u *p_vop; /* 'viewoptions' */ -EXTERN unsigned vop_flags; /* uses SSOP_ flags */ -EXTERN int p_vb; /* 'visualbell' */ -EXTERN char_u *p_ve; /* 'virtualedit' */ +#define SWB_VSPLIT 0x010 +EXTERN int p_tbs; ///< 'tagbsearch' +EXTERN char_u *p_tc; ///< 'tagcase' +EXTERN unsigned tc_flags; ///< flags from 'tagcase' +#ifdef IN_OPTION_C +static char *(p_tc_values[]) = { "followic", "ignore", "match", NULL }; +#endif +#define TC_FOLLOWIC 0x01 +#define TC_IGNORE 0x02 +#define TC_MATCH 0x04 +EXTERN long p_tl; ///< 'taglength' +EXTERN int p_tr; ///< 'tagrelative' +EXTERN char_u *p_tags; ///< 'tags' +EXTERN int p_tgst; ///< 'tagstack' +EXTERN int p_tbidi; ///< 'termbidi' +EXTERN int p_terse; ///< 'terse' +EXTERN int p_to; ///< 'tildeop' +EXTERN int p_timeout; ///< 'timeout' +EXTERN long p_tm; ///< 'timeoutlen' +EXTERN int p_title; ///< 'title' +EXTERN long p_titlelen; ///< 'titlelen' +EXTERN char_u *p_titleold; ///< 'titleold' +EXTERN char_u *p_titlestring; ///< 'titlestring' +EXTERN char_u *p_tsr; ///< 'thesaurus' +EXTERN bool p_tgc; ///< 'termguicolors' +EXTERN int p_ttimeout; ///< 'ttimeout' +EXTERN long p_ttm; ///< 'ttimeoutlen' +EXTERN char_u *p_udir; ///< 'undodir' +EXTERN long p_ul; ///< 'undolevels' +EXTERN long p_ur; ///< 'undoreload' +EXTERN long p_uc; ///< 'updatecount' +EXTERN long p_ut; ///< 'updatetime' +EXTERN char_u *p_fcs; ///< 'fillchar' +EXTERN char_u *p_shada; ///< 'shada' +EXTERN char_u *p_vdir; ///< 'viewdir' +EXTERN char_u *p_vop; ///< 'viewoptions' +EXTERN unsigned vop_flags; ///< uses SSOP_ flags +EXTERN int p_vb; ///< 'visualbell' +EXTERN char_u *p_ve; ///< 'virtualedit' EXTERN unsigned ve_flags; # ifdef IN_OPTION_C static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", NULL}; @@ -665,6 +704,7 @@ enum { , BV_DEF , BV_INC , BV_EOL + , BV_FIXEOL , BV_EP , BV_ET , BV_FENC @@ -706,6 +746,7 @@ enum { , BV_SW , BV_SWF , BV_TAGS + , BV_TC , BV_TS , BV_TW , BV_TX diff --git a/src/nvim/options.lua b/src/nvim/options.lua index b22e994efe..218e34f595 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -15,7 +15,7 @@ -- } -- } -- types: bool, number, string --- lists: (nil), comma, flags, flagscomma +-- lists: (nil), comma, onecomma, flags, flagscomma -- scopes: global, buffer, window -- redraw options: statuslines, current_window, current_buffer, all_windows, -- everything, curswant @@ -133,7 +133,7 @@ return { }, { full_name='backspace', abbreviation='bs', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vim=true, varname='p_bs', @@ -149,7 +149,7 @@ return { }, { full_name='backupcopy', abbreviation='bkc', - type='string', list='comma', scope={'global', 'buffer'}, + type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, vim=true, varname='p_bkc', @@ -161,7 +161,7 @@ return { }, { full_name='backupdir', abbreviation='bdir', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, vi_def=true, @@ -179,7 +179,7 @@ return { }, { full_name='backupskip', abbreviation='bsk', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, vi_def=true, varname='p_bsk', defaults={if_true={vi=""}} @@ -227,7 +227,7 @@ return { }, { full_name='breakindentopt', abbreviation='briopt', - type='string', list='comma', scope={'window'}, + type='string', list='onecomma', scope={'window'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -268,7 +268,7 @@ return { }, { full_name='casemap', abbreviation='cmp', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_cmp', @@ -307,7 +307,7 @@ return { }, { full_name='cinkeys', abbreviation='cink', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -316,7 +316,7 @@ return { }, { full_name='cinoptions', abbreviation='cino', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -325,7 +325,7 @@ return { }, { full_name='cinwords', abbreviation='cinw', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -334,7 +334,7 @@ return { }, { full_name='clipboard', abbreviation='cb', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_cb', @@ -357,7 +357,7 @@ return { }, { full_name='colorcolumn', abbreviation='cc', - type='string', list='comma', scope={'window'}, + type='string', list='onecomma', scope={'window'}, deny_duplicates=true, vi_def=true, redraw={'current_window'}, @@ -375,7 +375,7 @@ return { }, { full_name='comments', abbreviation='com', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -403,7 +403,7 @@ return { }, { full_name='complete', abbreviation='cpt', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, alloced=true, varname='p_cpt', @@ -435,7 +435,7 @@ return { }, { full_name='completeopt', abbreviation='cot', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_cot', @@ -483,7 +483,7 @@ return { }, { full_name='cscopequickfix', abbreviation='csqf', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_csqf', @@ -568,7 +568,7 @@ return { }, { full_name='dictionary', abbreviation='dict', - type='string', list='comma', scope={'global', 'buffer'}, + type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, vi_def=true, expand=true, @@ -594,7 +594,7 @@ return { }, { full_name='diffopt', abbreviation='dip', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -612,7 +612,7 @@ return { }, { full_name='directory', abbreviation='dir', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, vi_def=true, @@ -622,7 +622,7 @@ return { }, { full_name='display', abbreviation='dy', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vim=true, redraw={'all_windows'}, @@ -696,7 +696,7 @@ return { }, { full_name='errorformat', abbreviation='efm', - type='string', list='comma', scope={'global', 'buffer'}, + type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, vi_def=true, varname='p_efm', @@ -711,7 +711,7 @@ return { }, { full_name='eventignore', abbreviation='ei', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_ei', @@ -745,10 +745,10 @@ return { }, { full_name='fileencodings', abbreviation='fencs', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, vi_def=true, varname='p_fencs', - defaults={if_true={vi="ucs-bom"}} + defaults={if_true={vi="ucs-bom,utf-8,default,latin1"}} }, { full_name='fileformat', abbreviation='ff', @@ -762,7 +762,7 @@ return { }, { full_name='fileformats', abbreviation='ffs', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vim=true, varname='p_ffs', @@ -791,7 +791,7 @@ return { }, { full_name='fillchars', abbreviation='fcs', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, redraw={'all_windows'}, @@ -799,6 +799,14 @@ return { defaults={if_true={vi="vert:|,fold:-"}} }, { + full_name='fixendofline', abbreviation='fixeol', + type='bool', scope={'buffer'}, + vi_def=true, + redraw={'statuslines'}, + varname='p_fixeol', + defaults={if_true={vi=true}} + }, + { full_name='fkmap', abbreviation='fk', type='bool', scope={'global'}, vi_def=true, @@ -807,7 +815,7 @@ return { }, { full_name='foldclose', abbreviation='fcl', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, redraw={'current_window'}, @@ -863,7 +871,7 @@ return { }, { full_name='foldmarker', abbreviation='fmr', - type='string', list='comma', scope={'window'}, + type='string', list='onecomma', scope={'window'}, deny_duplicates=true, vi_def=true, vim=true, @@ -896,7 +904,7 @@ return { }, { full_name='foldopen', abbreviation='fdo', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, redraw={'curswant'}, @@ -951,7 +959,6 @@ return { type='bool', scope={'global'}, secure=true, vi_def=true, - enable_if='HAVE_FSYNC', varname='p_fs', defaults={if_true={vi=true}} }, @@ -965,7 +972,7 @@ return { }, { full_name='grepformat', abbreviation='gfm', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_gefm', @@ -988,7 +995,7 @@ return { }, { full_name='guicursor', abbreviation='gcr', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_guicursor', @@ -996,7 +1003,7 @@ return { }, { full_name='guifont', abbreviation='gfn', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, redraw={'everything'}, @@ -1004,14 +1011,14 @@ return { }, { full_name='guifontset', abbreviation='gfs', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, vi_def=true, redraw={'everything'}, enable_if=false, }, { full_name='guifontwide', abbreviation='gfw', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, redraw={'everything'}, @@ -1063,7 +1070,7 @@ return { }, { full_name='helplang', abbreviation='hlg', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, vi_def=true, varname='p_hlg', defaults={if_true={vi=""}} @@ -1077,7 +1084,7 @@ return { }, { full_name='highlight', abbreviation='hl', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, redraw={'everything'}, @@ -1137,20 +1144,6 @@ return { defaults={if_true={vi=false}} }, { - full_name='imactivatefunc', abbreviation='imaf', - type='string', scope={'global'}, - secure=true, - vi_def=true, - enable_if=false, - }, - { - full_name='imactivatekey', abbreviation='imak', - type='string', scope={'global'}, - vi_def=true, - enable_if=false, - defaults={if_true={vi=""}} - }, - { full_name='imcmdline', abbreviation='imc', type='bool', scope={'global'}, vi_def=true, @@ -1187,13 +1180,6 @@ return { } }, { - full_name='imstatusfunc', abbreviation='imsf', - type='string', scope={'global'}, - secure=true, - vi_def=true, - enable_if=false, - }, - { full_name='include', abbreviation='inc', type='string', scope={'global', 'buffer'}, vi_def=true, @@ -1227,7 +1213,7 @@ return { }, { full_name='indentkeys', abbreviation='indk', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -1278,7 +1264,7 @@ return { vim=true, alloced=true, varname='p_isk', - defaults={if_true={vi="@,48-57,_", vim=macros('ISK_LATIN1')}} + defaults={if_true={vi="@,48-57,_", vim="@,48-57,_,192-255"}} }, { full_name='isprint', abbreviation='isp', @@ -1287,10 +1273,7 @@ return { vi_def=true, redraw={'all_windows'}, varname='p_isp', - defaults={ - condition='MSWIN', - if_true={vi="@,~-255"}, - if_false={vi=macros("ISP_LATIN1")} + defaults={if_true={vi="@,161-255"} } }, { @@ -1314,7 +1297,7 @@ return { }, { full_name='keymodel', abbreviation='km', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_km', @@ -1333,7 +1316,7 @@ return { }, { full_name='langmap', abbreviation='lmap', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, vi_def=true, @@ -1402,7 +1385,7 @@ return { }, { full_name='lispwords', abbreviation='lw', - type='string', list='comma', scope={'global', 'buffer'}, + type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, vi_def=true, varname='p_lispwords', pv_name='p_lw', @@ -1417,7 +1400,7 @@ return { }, { full_name='listchars', abbreviation='lcs', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vim=true, redraw={'all_windows'}, @@ -1458,7 +1441,7 @@ return { }, { full_name='matchpairs', abbreviation='mps', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -1598,7 +1581,7 @@ return { }, { full_name='mouseshape', abbreviation='mouses', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, enable_if=false, @@ -1612,11 +1595,11 @@ return { }, { full_name='nrformats', abbreviation='nf', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, alloced=true, varname='p_nf', - defaults={if_true={vi="octal,hex", vim="hex"}} + defaults={if_true={vi="bin,octal,hex", vim="bin,hex"}} }, { full_name='number', abbreviation='nu', @@ -1779,7 +1762,7 @@ return { }, { full_name='printoptions', abbreviation='popt', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_popt', @@ -1852,13 +1835,6 @@ return { defaults={if_true={vi=2}} }, { - full_name='restorescreen', abbreviation='rs', - type='bool', scope={'global'}, - vi_def=true, - enable_if=false, - defaults={if_true={vi=true}} - }, - { full_name='revins', abbreviation='ri', type='bool', scope={'global'}, vi_def=true, @@ -1901,7 +1877,7 @@ return { }, { full_name='runtimepath', abbreviation='rtp', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, vi_def=true, @@ -1943,7 +1919,7 @@ return { }, { full_name='scrollopt', abbreviation='sbo', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_sbo', @@ -1973,7 +1949,7 @@ return { }, { full_name='selectmode', abbreviation='slm', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_slm', @@ -1981,7 +1957,7 @@ return { }, { full_name='sessionoptions', abbreviation='ssop', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vim=true, varname='p_ssop', @@ -1992,7 +1968,7 @@ return { }, { full_name='shada', abbreviation='sd', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, varname='p_shada', @@ -2216,7 +2192,7 @@ return { }, { full_name='spellfile', abbreviation='spf', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, secure=true, vi_def=true, alloced=true, @@ -2226,7 +2202,7 @@ return { }, { full_name='spelllang', abbreviation='spl', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, vi_def=true, alloced=true, expand=true, @@ -2236,7 +2212,7 @@ return { }, { full_name='spellsuggest', abbreviation='sps', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, secure=true, vi_def=true, expand=true, @@ -2276,7 +2252,7 @@ return { }, { full_name='suffixes', abbreviation='su', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_su', @@ -2284,7 +2260,7 @@ return { }, { full_name='suffixesadd', abbreviation='sua', - type='string', list='comma', scope={'buffer'}, + type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, vi_def=true, alloced=true, @@ -2300,15 +2276,8 @@ return { defaults={if_true={vi=true}} }, { - full_name='swapsync', abbreviation='sws', - type='string', scope={'global'}, - vi_def=true, - varname='p_sws', - defaults={if_true={vi="fsync"}} - }, - { full_name='switchbuf', abbreviation='swb', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_swb', @@ -2363,6 +2332,13 @@ return { defaults={if_true={vi=true}} }, { + full_name='tagcase', abbreviation='tc', + type='string', scope={'global', 'buffer'}, + vim=true, + varname='p_tc', + defaults={if_true={vi="followic", vim="followic"}} + }, + { full_name='taglength', abbreviation='tl', type='number', scope={'global'}, vi_def=true, @@ -2378,7 +2354,7 @@ return { }, { full_name='tags', abbreviation='tag', - type='string', list='comma', scope={'global', 'buffer'}, + type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, vi_def=true, expand=true, @@ -2407,6 +2383,14 @@ return { defaults={if_true={vi=""}} }, { + full_name='termguicolors', abbreviation='tgc', + type='bool', scope={'global'}, + vi_def=false, + redraw={'everything'}, + varname='p_tgc', + defaults={if_true={vi=false}} + }, + { full_name='terse', type='bool', scope={'global'}, vi_def=true, @@ -2424,7 +2408,7 @@ return { }, { full_name='thesaurus', abbreviation='tsr', - type='string', list='comma', scope={'global', 'buffer'}, + type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, vi_def=true, expand=true, @@ -2509,7 +2493,7 @@ return { }, { full_name='undodir', abbreviation='udir', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, vi_def=true, @@ -2580,7 +2564,7 @@ return { }, { full_name='viewoptions', abbreviation='vop', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_vop', @@ -2588,7 +2572,7 @@ return { }, { full_name='viminfo', abbreviation='vi', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, varname='p_shada', @@ -2596,7 +2580,7 @@ return { }, { full_name='virtualedit', abbreviation='ve', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, vim=true, @@ -2641,7 +2625,7 @@ return { }, { full_name='wildignore', abbreviation='wig', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, varname='p_wig', @@ -2663,7 +2647,7 @@ return { }, { full_name='wildmode', abbreviation='wim', - type='string', list='comma', scope={'global'}, + type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vim=true, varname='p_wim', diff --git a/src/nvim/os/dl.c b/src/nvim/os/dl.c index b4a35e203e..fef02cc784 100644 --- a/src/nvim/os/dl.c +++ b/src/nvim/os/dl.c @@ -63,7 +63,7 @@ bool os_libcall(const char *libname, // call the library and save the result // TODO(aktau): catch signals and use jmp (if available) to handle - // exceptions. jmp's on UNIX seem to interact trickily with signals as + // exceptions. jmp's on Unix seem to interact trickily with signals as // well. So for now we only support those libraries that are well-behaved. if (str_out) { str_str_fn sfn = (str_str_fn) fn; diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 0e052ced55..edc430410c 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -46,7 +46,19 @@ bool os_env_exists(const char *name) int os_setenv(const char *name, const char *value, int overwrite) FUNC_ATTR_NONNULL_ALL { +#ifdef HAVE_SETENV return setenv(name, value, overwrite); +#elif defined(HAVE_PUTENV_S) + if (!overwrite && os_getenv(name) != NULL) { + return 0; + } + if (_putenv_s(name, value) == 0) { + return 0; + } + return -1; +#else +# error "This system has no implementation available for os_setenv()" +#endif } /// Unset environment variable @@ -130,12 +142,12 @@ void os_get_hostname(char *hostname, size_t len) /// - go to that directory /// - do os_dirname() to get the real name of that directory. /// This also works with mounts and links. -/// Don't do this for MS-DOS, it will change the "current dir" for a drive. +/// Don't do this for Windows, it will change the "current dir" for a drive. static char_u *homedir = NULL; void init_homedir(void) { - /* In case we are called a second time (when 'encoding' changes). */ + // In case we are called a second time (when 'encoding' changes). xfree(homedir); homedir = NULL; @@ -164,16 +176,16 @@ void init_homedir(void) if (var != NULL) { #ifdef UNIX - /* - * Change to the directory and get the actual path. This resolves - * links. Don't do it when we can't return. - */ + // Change to the directory and get the actual path. This resolves + // links. Don't do it when we can't return. if (os_dirname(NameBuff, MAXPATHL) == OK && os_chdir((char *)NameBuff) == 0) { - if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) + if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) { var = IObuff; - if (os_chdir((char *)NameBuff) != 0) + } + if (os_chdir((char *)NameBuff) != 0) { EMSG(_(e_prev_dir)); + } } #endif homedir = vim_strsave(var); @@ -227,31 +239,49 @@ void expand_env(char_u *src, char_u *dst, int dstlen) /// "~/" is also expanded, using $HOME. For Unix "~user/" is expanded. /// Skips over "\ ", "\~" and "\$" (not for Win32 though). /// If anything fails no expansion is done and dst equals src. -/// startstr recognize the start of a new name, for '~' expansion. +/// prefix recognize the start of a new name, for '~' expansion. /// @param srcp Input string e.g. "$HOME/vim.hlp" /// @param dst Where to put the result /// @param dstlen Maximum length of the result /// @param esc Should we escape spaces in expanded variables? /// @param one Should we expand more than one '~'? -/// @param startstr Common prefix for paths, can be NULL -void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, - char_u *startstr) +/// @param prefix Common prefix for paths, can be NULL +void expand_env_esc(char_u *restrict srcp, + char_u *restrict dst, + int dstlen, + bool esc, + bool one, + char_u *prefix) { - char_u *src; char_u *tail; - int c; char_u *var; bool copy_char; bool mustfree; // var was allocated, need to free it later bool at_start = true; // at start of a name - int startstr_len = 0; - if (startstr != NULL) - startstr_len = (int)STRLEN(startstr); + int prefix_len = (prefix == NULL) ? 0 : (int)STRLEN(prefix); - src = skipwhite(srcp); - --dstlen; // leave one char space for "\," + char_u *src = skipwhite(srcp); + dstlen--; // leave one char space for "\," while (*src && dstlen > 0) { + // Skip over `=expr`. + if (src[0] == '`' && src[1] == '=') { + var = src; + src += 2; + (void)skip_expr(&src); + if (*src == '`') { + src++; + } + size_t len = (size_t)(src - var); + if (len > (size_t)dstlen) { + len = (size_t)dstlen; + } + memcpy((char *)dst, (char *)var, len); + dst += len; + dstlen -= (int)len; + continue; + } + copy_char = true; if ((*src == '$') || (*src == '~' && at_start)) { mustfree = false; @@ -261,14 +291,15 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, if (*src != '~') { // environment var tail = src + 1; var = dst; - c = dstlen - 1; + int c = dstlen - 1; #ifdef UNIX // Unix has ${var-name} type environment vars if (*tail == '{' && !vim_isIDc('{')) { - tail++; /* ignore '{' */ - while (c-- > 0 && *tail && *tail != '}') + tail++; // ignore '{' + while (c-- > 0 && *tail != NUL && *tail != '}') { *var++ = *tail++; + } } else // NOLINT #endif { @@ -278,7 +309,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, } #if defined(UNIX) - // Verify that we have found the end of a UNIX ${VAR} style variable + // Verify that we have found the end of a Unix ${VAR} style variable if (src[1] == '{' && *tail != '}') { var = NULL; } else { @@ -292,7 +323,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, #if defined(UNIX) } #endif - } else if ( src[1] == NUL /* home directory */ + } else if (src[1] == NUL // home directory || vim_ispathsep(src[1]) || vim_strchr((char_u *)" ,\t\n", src[1]) != NULL) { var = homedir; @@ -302,12 +333,13 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, // Copy ~user to dst[], so we can put a NUL after it. tail = src; var = dst; - c = dstlen - 1; - while ( c-- > 0 - && *tail - && vim_isfilec(*tail) - && !vim_ispathsep(*tail)) + int c = dstlen - 1; + while (c-- > 0 + && *tail + && vim_isfilec(*tail) + && !vim_ispathsep(*tail)) { *var++ = *tail++; + } *var = NUL; // Use os_get_user_directory() to get the user directory. // If this function fails, the shell is used to @@ -315,8 +347,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, // does not support ~user (old versions of /bin/sh). var = (char_u *)os_get_user_directory((char *)dst + 1); mustfree = true; - if (var == NULL) - { + if (var == NULL) { expand_T xpc; ExpandInit(&xpc); @@ -352,8 +383,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL) { char_u *p = vim_strsave_escaped(var, (char_u *)" \t"); - if (mustfree) + if (mustfree) { xfree(var); + } var = p; mustfree = true; } @@ -362,7 +394,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, && (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen)) { STRCPY(dst, var); dstlen -= (int)STRLEN(var); - c = (int)STRLEN(var); + int c = (int)STRLEN(var); // if var[] ends in a path separator and tail[] starts // with it, skip a character if (*var != NUL && after_pathsep((char *)dst, (char *)dst + c) @@ -375,8 +407,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, src = tail; copy_char = false; } - if (mustfree) + if (mustfree) { xfree(var); + } } if (copy_char) { // copy at least one char @@ -393,9 +426,10 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one, *dst++ = *src++; --dstlen; - if (startstr != NULL && src - startstr_len >= srcp - && STRNCMP(src - startstr_len, startstr, startstr_len) == 0) + if (prefix != NULL && src - prefix_len >= srcp + && STRNCMP(src - prefix_len, prefix, prefix_len) == 0) { at_start = true; + } } } *dst = NUL; @@ -422,17 +456,37 @@ static char *vim_version_dir(const char *vimdir) return NULL; } -/// If the string between "p" and "pend" ends in "name/", return "pend" minus -/// the length of "name/". Otherwise return "pend". -static char *remove_tail(char *p, char *pend, char *name) +/// If `dirname + "/"` precedes `pend` in the path, return the pointer to +/// `dirname + "/" + pend`. Otherwise return `pend`. +/// +/// Examples (path = /usr/local/share/nvim/runtime/doc/help.txt): +/// +/// pend = help.txt +/// dirname = doc +/// -> doc/help.txt +/// +/// pend = doc/help.txt +/// dirname = runtime +/// -> runtime/doc/help.txt +/// +/// pend = runtime/doc/help.txt +/// dirname = vim74 +/// -> runtime/doc/help.txt +/// +/// @param path Path to a file +/// @param pend A suffix of the path +/// @param dirname The immediate path fragment before the pend +/// @return The new pend including dirname or just pend +static char *remove_tail(char *path, char *pend, char *dirname) { - size_t len = STRLEN(name) + 1; - char *newend = pend - len; + size_t len = STRLEN(dirname); + char *new_tail = pend - len - 1; - if (newend >= p - && fnamencmp((char_u *)newend, (char_u *)name, len - 1) == 0 - && (newend == p || after_pathsep(p, newend))) - return newend; + if (new_tail >= path + && fnamencmp((char_u *)new_tail, (char_u *)dirname, len) == 0 + && (new_tail == path || after_pathsep(path, new_tail))) { + return new_tail; + } return pend; } @@ -716,9 +770,10 @@ void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) /// @param src Input file name char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET { - size_t len = 3; /* space for "~/" and trailing NUL */ - if (src != NULL) /* just in case */ + size_t len = 3; // space for "~/" and trailing NUL + if (src != NULL) { // just in case len += STRLEN(src); + } char_u *dst = xmalloc(len); home_replace(buf, src, dst, (int)len, true); return dst; @@ -730,15 +785,15 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET void vim_setenv(const char *name, const char *val) { os_setenv(name, val, 1); - /* - * When setting $VIMRUNTIME adjust the directory to find message - * translations to $VIMRUNTIME/lang. - */ +#ifndef LOCALE_INSTALL_DIR + // When setting $VIMRUNTIME adjust the directory to find message + // translations to $VIMRUNTIME/lang. if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0) { char *buf = (char *)concat_str((char_u *)val, (char_u *)"/lang"); - bindtextdomain(VIMPACKAGE, buf); + bindtextdomain(PROJECT_NAME, buf); xfree(buf); } +#endif } @@ -754,8 +809,7 @@ char_u *get_env_name(expand_T *xp, int idx) STRLCPY(name, envname, ENVNAMELEN); xfree(envname); return name; - } else { - return NULL; } + return NULL; } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index d59b66e773..49a74cf0d1 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -2,6 +2,7 @@ #include <stdbool.h> #include <assert.h> +#include <fcntl.h> #include "nvim/os/os.h" #include "nvim/os/os_defs.h" @@ -59,6 +60,23 @@ int os_dirname(char_u *buf, size_t len) return OK; } +/// Check if the given path is a directory and not a symlink to a directory. +/// @return `true` if `name` is a directory and NOT a symlink to a directory. +/// `false` if `name` is not a directory or if an error occurred. +bool os_isrealdir(const char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + uv_fs_t request; + if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) { + return false; + } + if (S_ISLNK(request.statbuf.st_mode)) { + return false; + } else { + return S_ISDIR(request.statbuf.st_mode); + } +} + /// Check if the given path is a directory or not. /// /// @return `true` if `fname` is a directory. @@ -77,10 +95,76 @@ bool os_isdir(const char_u *name) return true; } +/// Check what `name` is: +/// @return NODE_NORMAL: file or directory (or doesn't exist) +/// NODE_WRITABLE: writable device, socket, fifo, etc. +/// NODE_OTHER: non-writable things +int os_nodetype(const char *name) +{ +#ifdef WIN32 + // Edge case from Vim os_win32.c: + // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read + // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. + if (STRNCMP(name, "\\\\.\\", 4) == 0) { + return NODE_WRITABLE; + } +#endif + + uv_stat_t statbuf; + if (os_stat(name, &statbuf) == 0) { + return NODE_NORMAL; + } + +#ifndef WIN32 + // libuv does not handle BLK and DIR in uv_handle_type. + // Related: https://github.com/joyent/libuv/pull/1421 + if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) { + return NODE_NORMAL; + } + if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable + return NODE_OTHER; + } +#endif + + // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015): + // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { + // wn = enc_to_utf16(name, NULL); + // hFile = CreatFile(wn, ...) + // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we + // give it. uv_fs_open calls fs__capture_path which does a similar dance and + // saves us the hassle. + + int nodetype = NODE_WRITABLE; + int fd = os_open(name, O_RDONLY, 0); + switch(uv_guess_handle(fd)) { + case UV_TTY: // FILE_TYPE_CHAR + nodetype = NODE_WRITABLE; + break; + case UV_FILE: // FILE_TYPE_DISK + nodetype = NODE_NORMAL; + break; + case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c + case UV_UDP: // unix only + case UV_TCP: // unix only + case UV_UNKNOWN_HANDLE: + default: +#ifdef WIN32 + nodetype = NODE_NORMAL; +#else + nodetype = NODE_WRITABLE; // Everything else is writable? +#endif + break; + } + + close(fd); + return nodetype; +} + /// Checks if the given path represents an executable file. /// -/// @param[in] name The name of the executable. +/// @param[in] name Name of the executable. /// @param[out] abspath Path of the executable, if found and not `NULL`. +/// @param[in] use_path If 'false', only check if "name" is executable /// /// @return `true` if `name` is executable and /// - can be found in $PATH, @@ -88,14 +172,18 @@ bool os_isdir(const char_u *name) /// - is absolute. /// /// @return `false` otherwise. -bool os_can_exe(const char_u *name, char_u **abspath) +bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) FUNC_ATTR_NONNULL_ARG(1) { - // If it's an absolute or relative path don't need to use $PATH. - if (path_is_absolute_path(name) || - (name[0] == '.' && (name[1] == '/' || - (name[1] == '.' && name[2] == '/')))) { - if (is_executable(name)) { + // when use_path is false or if it's an absolute or relative path don't + // need to use $PATH. + if (!use_path || path_is_absolute_path(name) + || (name[0] == '.' + && (name[1] == '/' + || (name[1] == '.' && name[2] == '/')))) { + // There must be a path separator, files in the current directory + // can't be executed + if (gettail_dir(name) != name && is_executable(name)) { if (abspath != NULL) { *abspath = save_absolute_path(name); } @@ -146,17 +234,27 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) } size_t buf_len = STRLEN(name) + STRLEN(path) + 2; + +#ifdef WIN32 + const char *pathext = os_getenv("PATHEXT"); + if (!pathext) { + pathext = ".com;.exe;.bat;.cmd"; + } + + buf_len += STRLEN(pathext); +#endif + char_u *buf = xmalloc(buf_len); // Walk through all entries in $PATH to check if "name" exists there and // is an executable file. for (;; ) { - const char *e = xstrchrnul(path, ':'); + const char *e = xstrchrnul(path, ENV_SEPCHAR); // Glue together the given directory from $PATH with name and save into // buf. STRLCPY(buf, path, e - path + 1); - append_path((char *) buf, (const char *) name, (int)buf_len); + append_path((char *) buf, (const char *) name, buf_len); if (is_executable(buf)) { // Check if the caller asked for a copy of the path. @@ -169,7 +267,39 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) return true; } - if (*e != ':') { +#ifdef WIN32 + // Try appending file extensions from $PATHEXT to the name. + char *buf_end = xstrchrnul((char *)buf, '\0'); + for (const char *ext = pathext; *ext; ext++) { + // Skip the extension if there is no suffix after a '.'. + if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ';')) { + *ext++; + + continue; + } + + const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR); + STRLCPY(buf_end, ext, ext_end - ext + 1); + + if (is_executable(buf)) { + // Check if the caller asked for a copy of the path. + if (abspath != NULL) { + *abspath = save_absolute_path(buf); + } + + xfree(buf); + + return true; + } + + if (*ext_end != ENV_SEPCHAR) { + break; + } + ext = ext_end; + } +#endif + + if (*e != ENV_SEPCHAR) { // End of $PATH without finding any executable called name. xfree(buf); return false; @@ -202,6 +332,19 @@ int os_open(const char* path, int flags, int mode) return r; } +/// Flushes file modifications to disk. +/// +/// @param fd the file descriptor of the file to flush to disk. +/// +/// @return `0` on success, a libuv error code on failure. +int os_fsync(int fd) +{ + uv_fs_t fsync_req; + int r = uv_fs_fsync(&fs_loop, &fsync_req, fd, NULL); + uv_fs_req_cleanup(&fsync_req); + return r; +} + /// Get stat information for a file. /// /// @return libuv return code. diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h index 52b2841514..0bd9c37750 100644 --- a/src/nvim/os/fs_defs.h +++ b/src/nvim/os/fs_defs.h @@ -26,4 +26,10 @@ typedef struct { /// negative libuv error codes are returned by a number of os functions. #define os_strerror uv_strerror +// Values returned by os_nodetype() +#define NODE_NORMAL 0 // file or directory, check with os_isdir() +#define NODE_WRITABLE 1 // something we can write to (character + // device, fifo, socket, ..) +#define NODE_OTHER 2 // non-writable thing (e.g., block device) + #endif // NVIM_OS_FS_DEFS_H diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index ef6b5ff6f5..7687b14f02 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -19,6 +19,7 @@ #include "nvim/getchar.h" #include "nvim/main.h" #include "nvim/misc1.h" +#include "nvim/misc2.h" #define READ_BUFFER_SIZE 0xfff #define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4) @@ -174,8 +175,9 @@ size_t input_enqueue(String keys) char *ptr = keys.data, *end = ptr + keys.size; while (rbuffer_space(input_buffer) >= 6 && ptr < end) { - uint8_t buf[6] = {0}; - unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true); + uint8_t buf[6] = { 0 }; + unsigned int new_size = trans_special((const uint8_t **)&ptr, keys.size, + buf, true); if (new_size) { new_size = handle_mouse_event(&ptr, buf, new_size); @@ -249,6 +251,14 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, int col, row, advance; if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance) != EOF && advance) { if (col >= 0 && row >= 0) { + // Make sure the mouse position is valid. Some terminals may + // return weird values. + if (col >= Columns) { + col = (int)Columns - 1; + } + if (row >= Rows) { + row = (int)Rows - 1; + } mouse_row = row; mouse_col = col; } @@ -357,7 +367,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, static void process_interrupts(void) { - if (mapped_ctrl_c) { + if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { return; } @@ -393,9 +403,9 @@ static int push_event_key(uint8_t *buf, int maxlen) // Check if there's pending input static bool input_ready(void) { - return typebuf_was_filled || // API call filled typeahead - rbuffer_size(input_buffer) || // Input buffer filled - pending_events(); // Events must be processed + return (typebuf_was_filled // API call filled typeahead + || rbuffer_size(input_buffer) // Input buffer filled + || pending_events()); // Events must be processed } // Exit because of an input read error. diff --git a/src/nvim/os/mem.c b/src/nvim/os/mem.c index 5e483c0c3d..871ece7a0e 100644 --- a/src/nvim/os/mem.c +++ b/src/nvim/os/mem.c @@ -8,5 +8,5 @@ uint64_t os_get_total_mem_kib(void) { // Convert bytes to KiB. - return uv_get_total_memory() >> 10; + return uv_get_total_memory() / 1024; } diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 7d77899287..eee0cdd10b 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -13,24 +13,7 @@ # include "nvim/os/unix_defs.h" #endif -#if defined(DIRSIZ) && !defined(MAXNAMLEN) -# define MAXNAMLEN DIRSIZ -#endif - -#if defined(UFS_MAXNAMLEN) && !defined(MAXNAMLEN) -# define MAXNAMLEN UFS_MAXNAMLEN /* for dynix/ptx */ -#endif - -#if defined(NAME_MAX) && !defined(MAXNAMLEN) -# define MAXNAMLEN NAME_MAX /* for Linux before .99p3 */ -#endif - -// Default value. -#ifndef MAXNAMLEN -# define MAXNAMLEN 512 -#endif - -#define BASENAMELEN (MAXNAMLEN - 5) +#define BASENAMELEN (NAME_MAX - 5) // Use the system path length if it makes sense. #if defined(PATH_MAX) && (PATH_MAX > 1000) @@ -51,25 +34,6 @@ # define DFLT_MAXMEMTOT (10*1024) #endif -#if !defined(S_ISDIR) && defined(S_IFDIR) -# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#endif -#if !defined(S_ISREG) && defined(S_IFREG) -# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#endif -#if !defined(S_ISBLK) && defined(S_IFBLK) -# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) -#endif -#if !defined(S_ISSOCK) && defined(S_IFSOCK) -# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) -#endif -#if !defined(S_ISFIFO) && defined(S_IFIFO) -# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) -#endif -#if !defined(S_ISCHR) && defined(S_IFCHR) -# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) -#endif - // Note: Some systems need both string.h and strings.h (Savage). However, // some systems can't handle both, only use string.h in that case. #include <string.h> diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 57e25560de..f5a1637c94 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -36,7 +36,6 @@ typedef struct { # include "os/shell.c.generated.h" #endif - /// Builds the argument vector for running the user-configured 'shell' (p_sh) /// with an optional command prefixed by 'shellcmdflag' (p_shcf). /// @@ -45,9 +44,10 @@ typedef struct { /// @return A newly allocated argument vector. It must be freed with /// `shell_free_argv` when no longer needed. char **shell_build_argv(const char *cmd, const char *extra_args) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { - size_t argc = tokenize(p_sh, NULL) + tokenize(p_shcf, NULL); - char **rv = xmalloc((unsigned)((argc + 4) * sizeof(char *))); + size_t argc = tokenize(p_sh, NULL) + (cmd ? tokenize(p_shcf, NULL) : 0); + char **rv = xmalloc((argc + 4) * sizeof(*rv)); // Split 'shell' size_t i = tokenize(p_sh, rv); @@ -338,24 +338,22 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, /// @param argv The vector that will be filled with copies of the parsed /// words. It can be NULL if the caller only needs to count words. /// @return The number of words parsed. -static size_t tokenize(const char_u *str, char **argv) +static size_t tokenize(const char_u *const str, char **const argv) + FUNC_ATTR_NONNULL_ARG(1) { - size_t argc = 0, len; - char_u *p = (char_u *) str; + size_t argc = 0; + const char *p = (const char *) str; while (*p != NUL) { - len = word_length(p); + const size_t len = word_length((const char_u *) p); if (argv != NULL) { // Fill the slot - argv[argc] = xmalloc(len + 1); - memcpy(argv[argc], p, len); - argv[argc][len] = NUL; + argv[argc] = vim_strnsave_unquoted(p, len); } argc++; - p += len; - p = skipwhite(p); + p = (const char *) skipwhite((char_u *) (p + len)); } return argc; @@ -377,6 +375,9 @@ static size_t word_length(const char_u *str) if (*p == '"') { // Found a quote character, switch the `inquote` flag inquote = !inquote; + } else if (*p == '\\' && inquote) { + p++; + length++; } p++; @@ -418,7 +419,8 @@ static void read_input(DynamicBuffer *buf) // Finished a line, add a NL, unless this line should not have one. // FIXME need to make this more readable if (lnum != curbuf->b_op_end.lnum - || !curbuf->b_p_bin + || (!curbuf->b_p_bin + && curbuf->b_p_fixeol) || (lnum != curbuf->b_no_eol_lnum && (lnum != curbuf->b_ml.ml_line_count diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index c9631a434c..81ceb919c4 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -22,9 +22,9 @@ static const char *xdg_env_vars[] = { static const char *const xdg_defaults[] = { #ifdef WIN32 // Windows - [kXDGConfigHome] = "$LOCALAPPDATA\\nvim\\config", - [kXDGDataHome] = "$LOCALAPPDATA\\nvim\\data", - [kXDGCacheHome] = "$LOCALAPPDATA\\nvim\\cache", + [kXDGConfigHome] = "$LOCALAPPDATA", + [kXDGDataHome] = "$LOCALAPPDATA", + [kXDGCacheHome] = "$TEMP", [kXDGRuntimeDir] = NULL, [kXDGConfigDirs] = NULL, [kXDGDataDirs] = NULL, @@ -66,12 +66,21 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) /// @param[in] idx XDG directory to use. /// /// @return [allocated] `{xdg_directory}/nvim` +/// +/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to +/// avoid storing configuration and data files in the same path. static char *get_xdg_home(const XDGVarType idx) FUNC_ATTR_WARN_UNUSED_RESULT { char *dir = stdpaths_get_xdg_var(idx); if (dir) { +#if defined(WIN32) + dir = concat_fnames_realloc(dir, + (idx == kXDGDataHome ? "nvim-data" : "nvim"), + true); +#else dir = concat_fnames_realloc(dir, "nvim", true); +#endif } return dir; } diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index ba1dcf631a..188f0802c9 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -93,7 +93,7 @@ struct tm *os_localtime_r(const time_t *restrict clock, #endif } -/// Obtains the current UNIX timestamp and adjusts it to local time +/// Obtains the current Unix timestamp and adjusts it to local time. /// /// @param result Pointer to a 'struct tm' where the result should be placed /// @return A pointer to a 'struct tm' in the current time zone (the 'result' @@ -104,7 +104,7 @@ struct tm *os_get_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL return os_localtime_r(&rawtime, result); } -/// Obtains the current UNIX timestamp +/// Obtains the current Unix timestamp. /// /// @return Seconds since epoch. Timestamp os_time(void) diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index e3ba3262f4..690a39c3cd 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -1,13 +1,12 @@ #ifndef NVIM_OS_UNIX_DEFS_H #define NVIM_OS_UNIX_DEFS_H +// Windows doesn't have unistd.h, so we include it here to avoid numerous +// instances of `#ifdef WIN32'. #include <unistd.h> -#include <signal.h> -// Defines BSD, if it's a BSD system. -#ifdef HAVE_SYS_PARAM_H -# include <sys/param.h> -#endif +// POSIX.1-2008 says that NAME_MAX should be in here +#include <limits.h> #define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"} #define TEMP_FILE_PATH_MAXLEN 256 @@ -17,4 +16,7 @@ // Special wildcards that need to be handled by the shell. #define SPECIAL_WILDCHAR "`'{" +// Separator character for environment variables. +#define ENV_SEPCHAR ':' + #endif // NVIM_OS_UNIX_DEFS_H diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 637a86c74f..8ebb7562ef 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -42,17 +42,17 @@ int os_get_usernames(garray_T *users) int os_get_user_name(char *s, size_t len) { #ifdef UNIX - return os_get_uname(getuid(), s, len); + return os_get_uname((uv_uid_t)getuid(), s, len); #else // TODO(equalsraf): Windows GetUserName() - return os_get_uname(0, s, len); + return os_get_uname((uv_uid_t)0, s, len); #endif } // Insert user name for "uid" in s[len]. // Return OK if a name found. // If the name is not found, write the uid into s[len] and return FAIL. -int os_get_uname(uid_t uid, char *s, size_t len) +int os_get_uname(uv_uid_t uid, char *s, size_t len) { #if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) struct passwd *pw; diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 32960dfbe9..6a29f86e79 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -1,13 +1,33 @@ #ifndef NVIM_OS_WIN_DEFS_H #define NVIM_OS_WIN_DEFS_H +// winsock2.h must be first to avoid incompatibilities +// with winsock.h (included by windows.h) +#include <winsock2.h> #include <windows.h> +#include <sys/stat.h> +#include <io.h> +#include <stdio.h> + +// Windows does not have S_IFLNK but libuv defines it +// and sets the flag for us when calling uv_fs_stat. +#include <uv.h> + +#define NAME_MAX _MAX_PATH #define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""} #define TEMP_FILE_PATH_MAXLEN _MAX_PATH +#define FNAME_ILLEGAL "\"*?><|" + +// Separator character for environment variables. +#define ENV_SEPCHAR ';' + #define USE_CRNL +// We have our own RGB macro in macros.h. +#undef RGB + #ifdef _MSC_VER # ifndef inline # define inline __inline @@ -15,8 +35,19 @@ # ifndef restrict # define restrict __restrict # endif +# ifndef STDOUT_FILENO +# define STDOUT_FILENO _fileno(stdout) +# endif +# ifndef STDERR_FILENO +# define STDERR_FILENO _fileno(stderr) +# endif +# ifndef S_IXUSR +# define S_IXUSR S_IEXEC +# endif #endif +#define BACKSLASH_IN_FILENAME + #ifdef _MSC_VER typedef SSIZE_T ssize_t; #endif @@ -29,4 +60,30 @@ typedef SSIZE_T ssize_t; # endif #endif +#ifndef O_NOFOLLOW +# define O_NOFOLLOW 0 +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif + #endif // NVIM_OS_WIN_DEFS_H diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 62b264046c..2ed0c2c856 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -34,7 +34,6 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/tempfile.h" #include "nvim/ui.h" #include "nvim/types.h" #include "nvim/os/os.h" @@ -44,10 +43,6 @@ #include "nvim/os/signal.h" #include "nvim/msgpack_rpc/helpers.h" -#ifdef HAVE_STROPTS_H -# include <stropts.h> -#endif - #ifdef HAVE_SELINUX # include <selinux/selinux.h> static int selinux_enabled = -1; @@ -141,26 +136,6 @@ void mch_free_acl(vim_acl_T aclent) } #endif -/* - * Check what "name" is: - * NODE_NORMAL: file or directory (or doesn't exist) - * NODE_WRITABLE: writable device, socket, fifo, etc. - * NODE_OTHER: non-writable things - */ -int mch_nodetype(char_u *name) -{ - struct stat st; - - if (stat((char *)name, &st)) - return NODE_NORMAL; - if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) - return NODE_NORMAL; - if (S_ISBLK(st.st_mode)) /* block device isn't writable */ - return NODE_OTHER; - /* Everything else is writable? */ - return NODE_WRITABLE; -} - void mch_exit(int r) { exiting = true; @@ -179,13 +154,6 @@ void mch_exit(int r) exit(r); } -#ifndef SEEK_SET -# define SEEK_SET 0 -#endif -#ifndef SEEK_END -# define SEEK_END 2 -#endif - #define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|" /// Does wildcard pattern matching using the shell. @@ -608,9 +576,11 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) continue; - /* Skip files that are not executable if we check for that. */ - if (!dir && (flags & EW_EXEC) && !os_can_exe((*file)[i], NULL)) + // Skip files that are not executable if we check for that. + if (!dir && (flags & EW_EXEC) + && !os_can_exe((*file)[i], NULL, !(flags & EW_SHELLCMD))) { continue; + } p = xmalloc(STRLEN((*file)[i]) + 1 + dir); STRCPY(p, (*file)[i]); diff --git a/src/nvim/os_unix.h b/src/nvim/os_unix.h index 5a3eb84ba4..d627b16ec0 100644 --- a/src/nvim/os_unix.h +++ b/src/nvim/os_unix.h @@ -4,12 +4,6 @@ #include "nvim/types.h" // for vim_acl_T #include "nvim/os/shell.h" -/* Values returned by mch_nodetype() */ -#define NODE_NORMAL 0 /* file or directory, check with os_isdir()*/ -#define NODE_WRITABLE 1 /* something we can write to (character - device, fifo, socket, ..) */ -#define NODE_OTHER 2 /* non-writable thing (e.g., block device) */ - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os_unix.h.generated.h" #endif diff --git a/src/nvim/path.c b/src/nvim/path.c index 253035ed99..41fd69f238 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -196,7 +196,7 @@ char_u *get_past_head(char_u *path) int vim_ispathsep(int c) { #ifdef UNIX - return c == '/'; /* UNIX has ':' inside file names */ + return c == '/'; // Unix has ':' inside file names #else # ifdef BACKSLASH_IN_FILENAME return c == ':' || c == '/' || c == '\\'; @@ -268,16 +268,13 @@ char_u *shorten_dir(char_u *str) */ bool dir_of_file_exists(char_u *fname) { - char_u *p; - int c; - bool retval; - - p = path_tail_with_sep(fname); - if (p == fname) + char_u *p = path_tail_with_sep(fname); + if (p == fname) { return true; - c = *p; + } + char_u c = *p; *p = NUL; - retval = os_isdir(fname); + bool retval = os_isdir(fname); *p = c; return retval; } @@ -539,15 +536,10 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff, int flags, bool didstar) FUNC_ATTR_NONNULL_ALL { - char_u *buf; - char_u *p, *s, *e; int start_len = gap->ga_len; - char_u *pat; - int starts_with_dot; - int matches; - int len; + size_t len; bool starstar = false; - static int stardepth = 0; /* depth for "**" expansion */ + static int stardepth = 0; // depth for "**" expansion /* Expanding "**" may take a long time, check for CTRL-C. */ if (stardepth > 0) { @@ -556,17 +548,16 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, return 0; } - /* make room for file name */ - buf = xmalloc(STRLEN(path) + BASENAMELEN + 5); + // Make room for file name. When doing encoding conversion the actual + // length may be quite a bit longer, thus use the maximum possible length. + char_u *buf = xmalloc(MAXPATHL); - /* - * Find the first part in the path name that contains a wildcard. - * When EW_ICASE is set every letter is considered to be a wildcard. - * Copy it into "buf", including the preceding characters. - */ - p = buf; - s = buf; - e = NULL; + // Find the first part in the path name that contains a wildcard. + // When EW_ICASE is set every letter is considered to be a wildcard. + // Copy it into "buf", including the preceding characters. + char_u *p = buf; + char_u *s = buf; + char_u *e = NULL; const char_u *path_end = path; while (*path_end != NUL) { /* May ignore a wildcard that has a backslash before it; it will @@ -579,11 +570,15 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, s = p + 1; } else if (path_end >= path + wildoff && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL +#ifndef WIN32 || (!p_fic && (flags & EW_ICASE) - && isalpha(PTR2CHAR(path_end))))) + && isalpha(PTR2CHAR(path_end))) +#endif + )) { e = p; + } if (has_mbyte) { - len = (*mb_ptr2len)(path_end); + len = (size_t)(*mb_ptr2len)(path_end); STRNCPY(p, path_end, len); p += len; path_end += len; @@ -608,9 +603,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (p[0] == '*' && p[1] == '*') starstar = true; - /* convert the file pattern to a regexp pattern */ - starts_with_dot = (*s == '.'); - pat = file_pat_to_reg_pat(s, e, NULL, FALSE); + // convert the file pattern to a regexp pattern + int starts_with_dot = *s == '.'; + char_u *pat = file_pat_to_reg_pat(s, e, NULL, false); if (pat == NULL) { xfree(buf); return 0; @@ -641,9 +636,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (!didstar && stardepth < 100 && starstar && e - s == 2 && *path_end == '/') { STRCPY(s, path_end + 1); - ++stardepth; - (void)do_path_expand(gap, buf, (int)(s - buf), flags, true); - --stardepth; + stardepth++; + (void)do_path_expand(gap, buf, (size_t)(s - buf), flags, true); + stardepth--; } *s = NUL; @@ -652,9 +647,12 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (os_file_is_readable(dirpath) && os_scandir(&dir, dirpath)) { // Find all matching entries. char_u *name; - scandir_next_with_dots(NULL /* initialize */); - while((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { - if ((name[0] != '.' || starts_with_dot) + scandir_next_with_dots(NULL); // initialize + while ((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { + if ((name[0] != '.' + || starts_with_dot + || ((flags & EW_DODOT) + && name[1] != NUL && (name[1] != '.' || name[2] != NUL))) && ((regmatch.regprog != NULL && vim_regexec(®match, name, 0)) || ((flags & EW_NOTWILD) && fnamencmp(path + (s - buf), name, e - s) == 0))) { @@ -677,11 +675,16 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, /* remove backslashes for the remaining components only */ (void)do_path_expand(gap, buf, len + 1, flags, false); } else { - /* no more wildcards, check if there is a match */ - /* remove backslashes for the remaining components only */ - if (*path_end != NUL) + FileInfo file_info; + + // no more wildcards, check if there is a match + // remove backslashes for the remaining components only + if (*path_end != NUL) { backslash_halve(buf + len + 1); - if (os_file_exists(buf)) { /* add existing file */ + } + // add existing file or symbolic link + if ((flags & EW_ALLLINKS) ? os_fileinfo_link((char *)buf, &file_info) + : os_file_exists(buf)) { addfile(gap, buf, flags); } } @@ -693,10 +696,11 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, xfree(buf); vim_regfree(regmatch.regprog); - matches = gap->ga_len - start_len; - if (matches > 0) + size_t matches = (size_t)(gap->ga_len - start_len); + if (matches > 0) { qsort(((char_u **)gap->ga_data) + start_len, matches, - sizeof(char_u *), pstrcmp); + sizeof(char_u *), pstrcmp); + } return matches; } @@ -726,27 +730,24 @@ static int find_previous_pathsep(char_u *path, char_u **psep) */ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i) { - int candidate_len; - int other_path_len; - char_u **other_paths = (char_u **)gap->ga_data; - char_u *rival; + char_u **other_paths = (char_u **)gap->ga_data; for (int j = 0; j < gap->ga_len; j++) { - if (j == i) - continue; /* don't compare it with itself */ - - candidate_len = (int)STRLEN(maybe_unique); - other_path_len = (int)STRLEN(other_paths[j]); - if (other_path_len < candidate_len) - continue; /* it's different when it's shorter */ - - rival = other_paths[j] + other_path_len - candidate_len; + if (j == i) { + continue; // don't compare it with itself + } + size_t candidate_len = STRLEN(maybe_unique); + size_t other_path_len = STRLEN(other_paths[j]); + if (other_path_len < candidate_len) { + continue; // it's different when it's shorter + } + char_u *rival = other_paths[j] + other_path_len - candidate_len; if (fnamecmp(maybe_unique, rival) == 0 - && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) - return false; /* match */ + && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) { + return false; // match + } } - - return true; /* no match found */ + return true; // no match found } /* @@ -760,12 +761,8 @@ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i) */ static void expand_path_option(char_u *curdir, garray_T *gap) { - char_u *path_option = *curbuf->b_p_path == NUL - ? p_path : curbuf->b_p_path; - char_u *buf; - int len; - - buf = xmalloc(MAXPATHL); + char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; + char_u *buf = xmalloc(MAXPATHL); while (*path_option != NUL) { copy_option_part(&path_option, buf, MAXPATHL, " ,"); @@ -777,26 +774,27 @@ static void expand_path_option(char_u *curdir, garray_T *gap) if (curbuf->b_ffname == NULL) continue; char_u *p = path_tail(curbuf->b_ffname); - len = (int)(p - curbuf->b_ffname); - if (len + (int)STRLEN(buf) >= MAXPATHL) + size_t len = (size_t)(p - curbuf->b_ffname); + if (len + STRLEN(buf) >= MAXPATHL) { continue; - if (buf[1] == NUL) + } + if (buf[1] == NUL) { buf[len] = NUL; - else + } else { STRMOVE(buf + len, buf + 2); + } memmove(buf, curbuf->b_ffname, len); simplify_filename(buf); - } else if (buf[0] == NUL) - /* relative to current directory */ - STRCPY(buf, curdir); - else if (path_with_url((char *)buf)) - /* URL can't be used here */ - continue; - else if (!path_is_absolute_path(buf)) { - /* Expand relative path to their full path equivalent */ - len = (int)STRLEN(curdir); - if (len + (int)STRLEN(buf) + 3 > MAXPATHL) + } else if (buf[0] == NUL) { + STRCPY(buf, curdir); // relative to current directory + } else if (path_with_url((char *)buf)) { + continue; // URL can't be used here + } else if (!path_is_absolute_path(buf)) { + // Expand relative path to their full path equivalent + size_t len = STRLEN(curdir); + if (len + STRLEN(buf) + 3 > MAXPATHL) { continue; + } STRMOVE(buf + len + 1, buf); STRCPY(buf, curdir); buf[len] = PATHSEP; @@ -850,31 +848,25 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap) */ static void uniquefy_paths(garray_T *gap, char_u *pattern) { - int len; - char_u **fnames = (char_u **)gap->ga_data; + char_u **fnames = (char_u **)gap->ga_data; bool sort_again = false; - char_u *pat; - char_u *file_pattern; - char_u *curdir; regmatch_T regmatch; garray_T path_ga; - char_u **in_curdir = NULL; - char_u *short_name; + char_u **in_curdir = NULL; + char_u *short_name; ga_remove_duplicate_strings(gap); ga_init(&path_ga, (int)sizeof(char_u *), 1); - /* - * We need to prepend a '*' at the beginning of file_pattern so that the - * regex matches anywhere in the path. FIXME: is this valid for all - * possible patterns? - */ - len = (int)STRLEN(pattern); - file_pattern = xmalloc(len + 2); + // We need to prepend a '*' at the beginning of file_pattern so that the + // regex matches anywhere in the path. FIXME: is this valid for all + // possible patterns? + size_t len = STRLEN(pattern); + char_u *file_pattern = xmalloc(len + 2); file_pattern[0] = '*'; file_pattern[1] = NUL; STRCAT(file_pattern, pattern); - pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE); + char_u *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true); xfree(file_pattern); if (pat == NULL) return; @@ -885,11 +877,11 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) if (regmatch.regprog == NULL) return; - curdir = xmalloc(MAXPATHL); + char_u *curdir = xmalloc(MAXPATHL); os_dirname(curdir, MAXPATHL); expand_path_option(curdir, &path_ga); - in_curdir = xcalloc(gap->ga_len, sizeof(char_u *)); + in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *)); for (int i = 0; i < gap->ga_len && !got_int; i++) { char_u *path = fnames[i]; @@ -898,7 +890,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) char_u *pathsep_p; char_u *path_cutoff; - len = (int)STRLEN(path); + len = STRLEN(path); is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0 && curdir[dir_end - path] == NUL; if (is_in_curdir) @@ -989,12 +981,12 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) * "/path/file", "/path/dir/", "/path//dir", "/file" * ^ ^ ^ ^ */ -static char_u *gettail_dir(char_u *fname) +char_u *gettail_dir(const char_u *fname) { - char_u *dir_end = fname; - char_u *next_dir_end = fname; + const char_u *dir_end = fname; + const char_u *next_dir_end = fname; bool look_for_sep = true; - char_u *p; + const char_u *p; for (p = fname; *p != NUL; ) { if (vim_ispathsep(*p)) { @@ -1009,7 +1001,7 @@ static char_u *gettail_dir(char_u *fname) } mb_ptr_adv(p); } - return dir_end; + return (char_u *)dir_end; } @@ -1103,9 +1095,8 @@ static bool has_special_wildchar(char_u *p) int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) { - int i; garray_T ga; - char_u *p; + char_u *p; static bool recursive = false; int add_pat; bool did_expand_in_path = false; @@ -1130,11 +1121,11 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, * avoids starting the shell for each argument separately. * For `=expr` do use the internal function. */ - for (i = 0; i < num_pat; i++) { + for (int i = 0; i < num_pat; i++) { if (has_special_wildchar(pat[i]) - && !(vim_backtick(pat[i]) && pat[i][1] == '=') - ) + && !(vim_backtick(pat[i]) && pat[i][1] == '=')) { return mch_expand_wildcards(num_pat, pat, num_file, file, flags); + } } #endif @@ -1145,16 +1136,21 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, */ ga_init(&ga, (int)sizeof(char_u *), 30); - for (i = 0; i < num_pat; ++i) { + for (int i = 0; i < num_pat; ++i) { add_pat = -1; p = pat[i]; - if (vim_backtick(p)) + if (vim_backtick(p)) { add_pat = expand_backtick(&ga, p, flags); - else { - /* - * First expand environment variables, "~/" and "~user/". - */ + if (add_pat == -1) { + recursive = false; + FreeWild(ga.ga_len, (char_u **)ga.ga_data); + *num_file = 0; + *file = NULL; + return FAIL; + } + } else { + // First expand environment variables, "~/" and "~user/". if (has_env_var(p) || *p == '~') { p = expand_env_save_opt(p, true); if (p == NULL) @@ -1197,7 +1193,9 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, recursive = true; did_expand_in_path = true; } else { - add_pat = path_expand(&ga, p, flags); + size_t tmp_add_pat = path_expand(&ga, p, flags); + assert(tmp_add_pat <= INT_MAX); + add_pat = (int)tmp_add_pat; } } } @@ -1225,7 +1223,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, recursive = false; - return (ga.ga_data != NULL) ? OK : FAIL; + return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL; } @@ -1237,26 +1235,21 @@ static int vim_backtick(char_u *p) return *p == '`' && *(p + 1) != NUL && *(p + STRLEN(p) - 1) == '`'; } -/* - * Expand an item in `backticks` by executing it as a command. - * Currently only works when pat[] starts and ends with a `. - * Returns number of file names found. - */ -static int -expand_backtick ( +// Expand an item in `backticks` by executing it as a command. +// Currently only works when pat[] starts and ends with a `. +// Returns number of file names found, -1 if an error is encountered. +static int expand_backtick( garray_T *gap, char_u *pat, int flags /* EW_* flags */ ) { - char_u *p; - char_u *cmd; - char_u *buffer; + char_u *p; + char_u *buffer; int cnt = 0; - int i; - /* Create the command: lop off the backticks. */ - cmd = vim_strnsave(pat + 1, (int)STRLEN(pat) - 2); + // Create the command: lop off the backticks. + char_u *cmd = vim_strnsave(pat + 1, STRLEN(pat) - 2); if (*cmd == '=') /* `={expr}`: Expand expression */ buffer = eval_to_string(cmd + 1, &p, TRUE); @@ -1264,8 +1257,9 @@ expand_backtick ( buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL); xfree(cmd); - if (buffer == NULL) - return 0; + if (buffer == NULL) { + return -1; + } cmd = buffer; while (*cmd != NUL) { @@ -1275,7 +1269,7 @@ expand_backtick ( ++p; /* add an entry if it is not empty */ if (p > cmd) { - i = *p; + char_u i = *p; *p = NUL; addfile(gap, cmd, flags); *p = i; @@ -1290,26 +1284,52 @@ expand_backtick ( return cnt; } -/* - * Add a file to a file list. Accepted flags: - * EW_DIR add directories - * EW_FILE add files - * EW_EXEC add executable files - * EW_NOTFOUND add even when it doesn't exist - * EW_ADDSLASH add slash after directory name - */ -void -addfile ( +#ifdef BACKSLASH_IN_FILENAME +/// Replace all slashes by backslashes. +/// This used to be the other way around, but MS-DOS sometimes has problems +/// with slashes (e.g. in a command name). We can't have mixed slashes and +/// backslashes, because comparing file names will not work correctly. The +/// commands that use a file name should try to avoid the need to type a +/// backslash twice. +/// When 'shellslash' set do it the other way around. +/// When the path looks like a URL leave it unmodified. +void slash_adjust(char_u *p) +{ + if (path_with_url(p)) { + return; + } + while (*p) { + if (*p == psepcN) { + *p = psepc; + } + mb_ptr_adv(p); + } +} +#endif + +// Add a file to a file list. Accepted flags: +// EW_DIR add directories +// EW_FILE add files +// EW_EXEC add executable files +// EW_NOTFOUND add even when it doesn't exist +// EW_ADDSLASH add slash after directory name +// EW_ALLLINKS add symlink also when the referred file does not exist +void addfile( garray_T *gap, char_u *f, /* filename */ int flags ) { bool isdir; + FileInfo file_info; - /* if the file/dir doesn't exist, may not add it */ - if (!(flags & EW_NOTFOUND) && !os_file_exists(f)) + // if the file/dir/link doesn't exist, may not add it + if (!(flags & EW_NOTFOUND) + && ((flags & EW_ALLLINKS) + ? !os_fileinfo_link((char *)f, &file_info) + : !os_file_exists(f))) { return; + } #ifdef FNAME_ILLEGAL /* if the file/dir contains illegal characters, don't add it */ @@ -1321,9 +1341,12 @@ addfile ( if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE))) return; - /* If the file isn't executable, may not add it. Do accept directories. */ - if (!isdir && (flags & EW_EXEC) && !os_can_exe(f, NULL)) + // If the file isn't executable, may not add it. Do accept directories. + // When invoked from expand_shellcmd() do not use $PATH. + if (!isdir && (flags & EW_EXEC) + && !os_can_exe(f, NULL, !(flags & EW_SHELLCMD))) { return; + } char_u *p = xmalloc(STRLEN(f) + 1 + isdir); @@ -1387,9 +1410,9 @@ void simplify_filename(char_u *filename) --p; /* strip preceding path separator */ STRMOVE(p, tail); } - } else if (p[0] == '.' && p[1] == '.' && - (vim_ispathsep(p[2]) || p[2] == NUL)) { - /* Skip to after ".." or "../" or "..///". */ + } else if (p[0] == '.' && p[1] == '.' + && (vim_ispathsep(p[2]) || p[2] == NUL)) { + // Skip to after ".." or "../" or "..///". tail = p + 2; while (vim_ispathsep(*tail)) mb_ptr_adv(tail); @@ -1502,13 +1525,12 @@ void simplify_filename(char_u *filename) } while (*p != NUL); } -static char_u *eval_includeexpr(char_u *ptr, size_t len) +static char *eval_includeexpr(const char *const ptr, const size_t len) { - assert(len <= INT_MAX); - set_vim_var_string(VV_FNAME, ptr, (int)len); - char_u *res = eval_to_string_safe(curbuf->b_p_inex, NULL, - was_set_insecurely((char_u *)"includeexpr", - OPT_LOCAL)); + set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t) len); + char *res = (char *) eval_to_string_safe( + curbuf->b_p_inex, NULL, was_set_insecurely((char_u *)"includeexpr", + OPT_LOCAL)); set_vim_var_string(VV_FNAME, NULL, 0); return res; } @@ -1526,12 +1548,11 @@ find_file_name_in_path ( char_u *rel_fname /* file we are searching relative to */ ) { - char_u *file_name; - int c; - char_u *tofree = NULL; + char_u *file_name; + char_u *tofree = NULL; if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) { - tofree = eval_includeexpr(ptr, len); + tofree = (char_u *) eval_includeexpr((char *) ptr, len); if (tofree != NULL) { ptr = tofree; len = STRLEN(ptr); @@ -1539,8 +1560,8 @@ find_file_name_in_path ( } if (options & FNAME_EXP) { - file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, - TRUE, rel_fname); + file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, true, + rel_fname); /* * If the file could not be found in a normal way, try applying @@ -1548,7 +1569,7 @@ find_file_name_in_path ( */ if (file_name == NULL && !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) { - tofree = eval_includeexpr(ptr, len); + tofree = (char_u *) eval_includeexpr((char *) ptr, len); if (tofree != NULL) { ptr = tofree; len = STRLEN(ptr); @@ -1557,7 +1578,7 @@ find_file_name_in_path ( } } if (file_name == NULL && (options & FNAME_MESS)) { - c = ptr[len]; + char_u c = ptr[len]; ptr[len] = NUL; EMSG2(_("E447: Can't find file \"%s\" in path"), ptr); ptr[len] = c; @@ -1616,7 +1637,7 @@ bool vim_isAbsName(char_u *name) /// @param force is a flag to force expanding even if the path is absolute /// /// @return FAIL for failure, OK otherwise -int vim_FullName(const char *fname, char *buf, int len, bool force) +int vim_FullName(const char *fname, char *buf, size_t len, bool force) FUNC_ATTR_NONNULL_ARG(2) { int retval = OK; @@ -1764,19 +1785,20 @@ bool same_directory(char_u *f1, char_u *f2) */ int pathcmp(const char *p, const char *q, int maxlen) { - int i; + int i, j; int c1, c2; const char *s = NULL; - for (i = 0; maxlen < 0 || i < maxlen; i += MB_PTR2LEN((char_u *)p + i)) { + for (i = 0, j = 0; maxlen < 0 || (i < maxlen && j < maxlen);) { c1 = PTR2CHAR((char_u *)p + i); - c2 = PTR2CHAR((char_u *)q + i); + c2 = PTR2CHAR((char_u *)q + j); /* End of "p": check if "q" also ends or just has a slash. */ if (c1 == NUL) { if (c2 == NUL) /* full match */ return 0; s = q; + i = j; break; } @@ -1800,9 +1822,13 @@ int pathcmp(const char *p, const char *q, int maxlen) return p_fic ? vim_toupper(c1) - vim_toupper(c2) : c1 - c2; /* no match */ } + + i += MB_PTR2LEN((char_u *)p + i); + j += MB_PTR2LEN((char_u *)q + j); } - if (s == NULL) /* "i" ran into "maxlen" */ + if (s == NULL) { // "i" or "j" ran into "maxlen" return 0; + } c1 = PTR2CHAR((char_u *)s + i); c2 = PTR2CHAR((char_u *)s + i + MB_PTR2LEN((char_u *)s + i)); @@ -1927,7 +1953,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, /// If FAIL is returned, *num_file and *file are either /// unchanged or *num_file is set to 0 and *file is set to /// NULL or points to "". -int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, +int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, int flags) { int retval; @@ -1935,7 +1961,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, char_u *p; int non_suf_match; /* number without matching suffix */ - retval = gen_expand_wildcards(num_pat, pat, num_file, file, flags); + retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags); /* When keeping all matches, return here */ if ((flags & EW_KEEPALL) || retval == FAIL) @@ -1947,18 +1973,20 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, if (*p_wig) { char_u *ffname; - /* check all files in (*file)[] */ - for (i = 0; i < *num_file; ++i) { - ffname = (char_u *)FullName_save((char *)(*file)[i], FALSE); - if (ffname == NULL) /* out of memory */ + // check all filess in (*files)[] + for (i = 0; i < *num_files; i++) { + ffname = (char_u *)FullName_save((char *)(*files)[i], false); + if (ffname == NULL) { // out of memory break; - if (match_file_list(p_wig, (*file)[i], ffname)) { - /* remove this matching file from the list */ - xfree((*file)[i]); - for (j = i; j + 1 < *num_file; ++j) - (*file)[j] = (*file)[j + 1]; - --*num_file; - --i; + } + if (match_file_list(p_wig, (*files)[i], ffname)) { + // remove this matching files from the list + xfree((*files)[i]); + for (j = i; j + 1 < *num_files; j++) { + (*files)[j] = (*files)[j + 1]; + } + (*num_files)--; + i--; } xfree(ffname); } @@ -1967,26 +1995,28 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, /* * Move the names where 'suffixes' match to the end. */ - if (*num_file > 1) { + if (*num_files > 1) { non_suf_match = 0; - for (i = 0; i < *num_file; ++i) { - if (!match_suffix((*file)[i])) { - /* - * Move the name without matching suffix to the front - * of the list. - */ - p = (*file)[i]; - for (j = i; j > non_suf_match; --j) - (*file)[j] = (*file)[j - 1]; - (*file)[non_suf_match++] = p; + for (i = 0; i < *num_files; i++) { + if (!match_suffix((*files)[i])) { + // + // Move the name without matching suffix to the front + // of the list. + // + p = (*files)[i]; + for (j = i; j > non_suf_match; j--) { + (*files)[j] = (*files)[j - 1]; + } + (*files)[non_suf_match++] = p; } } } // Free empty array of matches - if (*num_file == 0) { - xfree(*file); - *file = NULL; + if (*num_files == 0) { + xfree(*files); + *files = NULL; + return FAIL; } return retval; @@ -1997,14 +2027,12 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, */ int match_suffix(char_u *fname) { - int fnamelen, setsuflen; - char_u *setsuf; -#define MAXSUFLEN 30 /* maximum length of a file suffix */ +#define MAXSUFLEN 30 // maximum length of a file suffix char_u suf_buf[MAXSUFLEN]; - fnamelen = (int)STRLEN(fname); - setsuflen = 0; - for (setsuf = p_su; *setsuf; ) { + size_t fnamelen = STRLEN(fname); + size_t setsuflen = 0; + for (char_u *setsuf = p_su; *setsuf; ) { setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,"); if (setsuflen == 0) { char_u *tail = path_tail(fname); @@ -2016,9 +2044,9 @@ int match_suffix(char_u *fname) } } else { if (fnamelen >= setsuflen - && fnamencmp(suf_buf, fname + fnamelen - setsuflen, - (size_t)setsuflen) == 0) + && fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) { break; + } setsuflen = 0; } } @@ -2029,7 +2057,7 @@ int match_suffix(char_u *fname) /// /// @param directory Directory name, relative to current directory. /// @return `FAIL` for failure, `OK` for success. -int path_full_dir_name(char *directory, char *buffer, int len) +int path_full_dir_name(char *directory, char *buffer, size_t len) { int SUCCESS = 0; int retval = OK; @@ -2071,10 +2099,10 @@ int path_full_dir_name(char *directory, char *buffer, int len) // Append to_append to path with a slash in between. // Append to_append to path with a slash in between. -int append_path(char *path, const char *to_append, int max_len) +int append_path(char *path, const char *to_append, size_t max_len) { - int current_length = STRLEN(path); - int to_append_length = STRLEN(to_append); + size_t current_length = strlen(path); + size_t to_append_length = strlen(to_append); // Do not append empty strings. if (to_append_length == 0) { @@ -2109,12 +2137,14 @@ int append_path(char *path, const char *to_append, int max_len) /// Expand a given file to its absolute path. /// -/// @param fname The filename which should be expanded. -/// @param buf Buffer to store the absolute path of `fname`. -/// @param len Length of `buf`. -/// @param force Also expand when `fname` is already absolute. -/// @return `FAIL` for failure, `OK` for success. -static int path_get_absolute_path(const char_u *fname, char_u *buf, int len, int force) +/// @param fname filename which should be expanded. +/// @param buf buffer to store the absolute path of "fname". +/// @param len length of "buf". +/// @param force also expand when "fname" is already absolute. +/// +/// @return FAIL for failure, OK for success. +static int path_get_absolute_path(const char_u *fname, char_u *buf, + size_t len, int force) { char_u *p; *buf = NUL; diff --git a/src/nvim/path.h b/src/nvim/path.h index 628ea335ed..4e466d1b71 100644 --- a/src/nvim/path.h +++ b/src/nvim/path.h @@ -20,6 +20,11 @@ #define EW_KEEPDOLLAR 0x800 /* do not escape $, $var is expanded */ /* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND * is used when executing commands and EW_SILENT for interactive expanding. */ +#define EW_ALLLINKS 0x1000 // also links not pointing to existing file +#define EW_SHELLCMD 0x2000 // called from expand_shellcmd(), don't check + // if executable is in $PATH +#define EW_DODOT 0x4000 // also files starting with a dot +#define EW_EMPTYOK 0x8000 // no matches is not an error /// Return value for the comparison of two files. Also @see path_full_compare. typedef enum file_comparison { diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 6687918df4..184c4894b9 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -73,7 +73,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) install_helper( FILES ${moFile} DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${name}/LC_MESSAGES - RENAME nvim.mo) + RENAME ${PROJECT_NAME}.mo) list(APPEND LANGUAGE_MO_FILES ${moFile}) endmacro() diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po index 8215b31e65..5b0cb2260b 100644 --- a/src/nvim/po/eo.po +++ b/src/nvim/po/eo.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Esperanto)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-05-27 04:55+0200\n" +"POT-Creation-Date: 2015-07-30 17:54+0200\n" +"PO-Revision-Date: 2015-07-30 18:00+0200\n" "Last-Translator: Dominique PELLÉ <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: eo\n" @@ -115,11 +115,6 @@ msgstr "E84: Neniu modifita bufro trovita" msgid "E85: There is no listed buffer" msgstr "E85: Estas neniu listigita bufro" -#: ../buffer.c:913 -#, c-format -msgid "E86: Buffer %<PRId64> does not exist" -msgstr "E86: La bufro %<PRId64> ne ekzistas" - #: ../buffer.c:915 msgid "E87: Cannot go beyond last buffer" msgstr "E87: Ne eblas iri preter la lastan bufron" @@ -692,6 +687,10 @@ msgstr "E696: Mankas komo en Listo: %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: Mankas fino de Listo ']': %s" +#: ../eval.c:5750 +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "Ne sufiĉa memory por valorigi referencojn, senrubigado ĉesigita!" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -1616,14 +1615,13 @@ msgstr "E173: %<PRId64> pliaj redaktendaj dosieroj" msgid "E174: Command already exists: add ! to replace it" msgstr "E174: La komando jam ekzistas: aldonu ! por anstataÅigi Äin" -# DP: malfacilas traduki tion, kaj samtempe honori spacetojn #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" Nomo Arg Interv Kompleto Difino" +" Nomo Argumentoj Adreso Kompleto Difino" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" @@ -1649,6 +1647,10 @@ msgstr "E178: Nevalida defaÅlta valoro de kvantoro" msgid "E179: argument required for -complete" msgstr "E179: argumento bezonata por -complete" +#: ../ex_docmd.c:4933 +msgid "E179: argument required for -addr" +msgstr "E179: argumento bezonata por -addr" + #: ../ex_docmd.c:4635 #, c-format msgid "E181: Invalid attribute: %s" @@ -1671,6 +1673,11 @@ msgstr "E841: Rezervita nomo, neuzebla por komando difinita de uzanto" msgid "E184: No such user-defined command: %s" msgstr "E184: Neniu komando-difinita-de-uzanto kiel: %s" +#: ../ex_docmd.c:5516 +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Nevalida valoro de tipo de adreso: %s" + #: ../ex_docmd.c:5219 #, c-format msgid "E180: Invalid complete value: %s" @@ -2951,6 +2958,11 @@ msgstr "E363: Åablono uzas pli da memoro ol 'maxmempattern'" msgid "E749: empty buffer" msgstr "E749: malplena bufro" +#: ../globals.h:1226 +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: La bufro %<PRId64> ne ekzistas" + #: ../globals.h:1108 msgid "E682: Invalid search pattern or delimiter" msgstr "E682: Nevalida serĉa Åablono aÅ disigilo" @@ -4599,6 +4611,11 @@ msgstr "E522: Netrovita en termcap" msgid "E539: Illegal character <%s>" msgstr "E539: Nevalida signo <%s>" +#: ../option.c:2253 +#, c-format +msgid "For option %s" +msgstr "Por opcio %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: Ne eblas agordi 'term' al malplena ĉeno" @@ -6552,15 +6569,15 @@ msgstr "E446: Neniu dosiernomo sub la kursoro" #~ msgid "Reading from stdin..." #~ msgstr "Legado el stdin..." -#~ msgid "[blowfish]" -#~ msgstr "[blowfish]" - #~ msgid "[crypted]" #~ msgstr "[ĉifrita]" #~ msgid "E821: File is encrypted with unknown method" #~ msgstr "E821: Dosiero estas ĉifrata per nekonata metodo" +#~ msgid "Warning: Using a weak encryption method; see :help 'cm'" +#~ msgstr "Averto: uzo de malfortika ĉifrada metodo; vidu :help 'cm'" + #~ msgid "NetBeans disallows writes of unmodified buffers" #~ msgstr "NetBeans malpermesas skribojn de neÅanÄitaj bufroj" @@ -6676,8 +6693,8 @@ msgstr "E446: Neniu dosiernomo sub la kursoro" #~ msgid "Vim: Received \"die\" request from session manager\n" #~ msgstr "Vim: Ricevis peton \"die\" (morti) el la seanca administrilo\n" -#~ msgid "Close" -#~ msgstr "Fermi" +#~ msgid "Close tab" +#~ msgstr "Fermi langeton" #~ msgid "New tab" #~ msgstr "Nova langeto" @@ -6733,9 +6750,6 @@ msgstr "E446: Neniu dosiernomo sub la kursoro" #~ msgid "E672: Unable to open window inside MDI application" #~ msgstr "E672: Ne eblas malfermi fenestron interne de aplikaĵo MDI" -#~ msgid "Close tab" -#~ msgstr "Fermi langeton" - #~ msgid "Open tab..." #~ msgstr "Malfermi langeton..." diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po index 1a5ef991dc..8a9c86e88d 100644 --- a/src/nvim/po/es.po +++ b/src/nvim/po/es.po @@ -4276,7 +4276,8 @@ msgstr "" "&Abrir para lectura únicamente\n" "&Editar de todas formas\n" "&Recuperar\n" -"&Borrar&Salir\n" +"&Borrar\n" +"&Salir\n" "&Abortar" #. diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index 9b2d44ce7d..41efd5c5e3 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -6,7 +6,7 @@ # FIRST AUTHOR DindinX <David.Odin@bigfoot.com> 2000. # SECOND AUTHOR Adrien Beau <version.francaise@free.fr> 2002, 2003. # THIRD AUTHOR David Blanchet <david.blanchet@free.fr> 2006, 2008. -# FOURTH AUTHOR Dominique Pellé <dominique.pelle@gmail.com> 2008, 2013. +# FOURTH AUTHOR Dominique Pellé <dominique.pelle@gmail.com> 2008, 2015. # # Latest translation available at: # http://dominique.pelle.free.fr/vim-fr.php @@ -15,8 +15,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Français)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-05-27 10:22+0200\n" +"POT-Creation-Date: 2015-07-30 17:54+0200\n" +"PO-Revision-Date: 2015-07-30 18:00+0200\n" "Last-Translator: Dominique Pellé <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: fr\n" @@ -113,11 +113,6 @@ msgstr "E84: Aucun tampon n'est modifié" msgid "E85: There is no listed buffer" msgstr "E85: Aucun tampon n'est listé" -#: ../buffer.c:913 -#, c-format -msgid "E86: Buffer %<PRId64> does not exist" -msgstr "E86: Le tampon %<PRId64> n'existe pas" - # AB - Je ne suis pas sûr que l'on puisse obtenir ce message. #: ../buffer.c:915 msgid "E87: Cannot go beyond last buffer" @@ -756,6 +751,11 @@ msgstr "E696: Il manque une virgule dans la Liste %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: Il manque ']' à la fin de la Liste %s" +#: ../eval.c:5750 +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "" +"Pas assez de mémoire pour les références, arrêt du ramassage de miètes !" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -832,15 +832,15 @@ msgstr "E785: complete() n'est utilisable que dans le mode Insertion" msgid "&Ok" msgstr "&Ok" -#: ../eval.c:8676 -#, c-format -msgid "E737: Key already exists: %s" -msgstr "E737: un mappage existe déjà pour %s" - #: ../eval.c:8692 msgid "extend() argument" msgstr "argument de extend()" +#: ../eval.c:9345 +#, c-format +msgid "E737: Key already exists: %s" +msgstr "E737: un mappage existe déjà pour %s" + #: ../eval.c:8915 msgid "map() argument" msgstr "argument de map()" @@ -1798,10 +1798,10 @@ msgstr "E174: La commande existe déjà : ajoutez ! pour la redéfinir" #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" Nom Args Plage Complet. Définition" +" Nom Args Adresse Complet. Définition" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" @@ -1827,6 +1827,10 @@ msgstr "E178: La valeur par défaut du quantificateur est invalide" msgid "E179: argument required for -complete" msgstr "E179: argument requis avec -complete" +#: ../ex_docmd.c:4933 +msgid "E179: argument required for -addr" +msgstr "E179: argument requis avec -addr" + #: ../ex_docmd.c:4635 #, c-format msgid "E181: Invalid attribute: %s" @@ -1850,6 +1854,11 @@ msgstr "" msgid "E184: No such user-defined command: %s" msgstr "E184: Aucune commande %s définie par l'utilisateur" +#: ../ex_docmd.c:5516 +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Valeur de type d'adresse invalide : %s" + #: ../ex_docmd.c:5219 #, c-format msgid "E180: Invalid complete value: %s" @@ -3155,6 +3164,11 @@ msgstr "E363: le motif utilise plus de mémoire que 'maxmempattern'" msgid "E749: empty buffer" msgstr "E749: tampon vide" +#: ../buffer.c:1587 +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: Le tampon %<PRId64> n'existe pas" + #: ../globals.h:1108 msgid "E682: Invalid search pattern or delimiter" msgstr "E682: Délimiteur ou motif de recherche invalide" @@ -4308,7 +4322,7 @@ msgid "" " to recover the changes (see \":help recovery\").\n" msgstr "" "\"\n" -" pour récupérer le fichier (voir \":help recovery\").\n" +" pour récupérer le fichier (consultez \":help recovery\").\n" #: ../memline.c:3250 msgid " If you did this already, delete the swap file \"" @@ -4805,6 +4819,11 @@ msgstr "E522: Introuvable dans termcap" msgid "E539: Illegal character <%s>" msgstr "E539: Caractère <%s> invalide" +#: ../option.c:2253 +#, c-format +msgid "For option %s" +msgstr "Pour l'option %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: 'term' ne doit pas être une chaîne vide" @@ -6779,9 +6798,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "Reading from stdin..." #~ msgstr "Lecture de stdin..." -#~ msgid "[blowfish]" -#~ msgstr "[blowfish]" - #~ msgid "[crypted]" #~ msgstr "[chiffré]" @@ -6909,8 +6925,8 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgstr "" #~ "Vim : Une requête \"die\" a été reçue par le gestionnaire de session\n" -#~ msgid "Close" -#~ msgstr "Fermer" +#~ msgid "Close tab" +#~ msgstr "Fermer l'onglet" #~ msgid "New tab" #~ msgstr "Nouvel onglet" @@ -6968,13 +6984,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E672: Unable to open window inside MDI application" #~ msgstr "E672: Impossible d'ouvrir une fenêtre dans une application MDI" -# DB - Les quelques messages qui suivent se retrouvent aussi ici : -# gui_gtk_x11.c:3170 et suivants. -# Les libellés changent un peu (majuscule par exemple). -# La VF tâche de les unifier. -#~ msgid "Close tab" -#~ msgstr "Fermer l'onglet" - #~ msgid "Open tab..." #~ msgstr "Ouvrir dans un onglet..." @@ -7148,9 +7157,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E836: This Vim cannot execute :python after using :py3" #~ msgstr "E836: Vim ne peut pas exécuter :python après avoir utilisé :py3" -#~ msgid "only string keys are allowed" -#~ msgstr "seule une chaine est autorisée comme clé" - #~ msgid "" #~ "E263: Sorry, this command is disabled, the Python library could not be " #~ "loaded." @@ -7684,12 +7690,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E338: Sorry, no file browser in console mode" #~ msgstr "E338: Désolé, pas de sélecteur de fichiers en mode console" -#~ msgid "Vim: preserving files...\n" -#~ msgstr "Vim : préservation des fichiers...\n" - -#~ msgid "Vim: Finished.\n" -#~ msgstr "Vim : Fini.\n" - #~ msgid "ERROR: " #~ msgstr "ERREUR : " @@ -7718,6 +7718,10 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E547: Illegal mouseshape" #~ msgstr "E547: Forme de curseur invalide" +#~ msgid "Warning: Using a weak encryption method; see :help 'cm'" +#~ msgstr "" +#~ "Alerte : utilisation d'une méthode de chiffrage faible ; consultez :help 'cm'" + #~ msgid "Enter encryption key: " #~ msgstr "Tapez la clé de chiffrement : " @@ -7861,15 +7865,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E245: Illegal char '%c' in font name \"%s\"" #~ msgstr "E245: Caractère '%c' invalide dans le nom de fonte \"%s\"" -#~ msgid "Vim: Double signal, exiting\n" -#~ msgstr "Vim : Double signal, sortie\n" - -#~ msgid "Vim: Caught deadly signal %s\n" -#~ msgstr "Vim : Signal mortel %s intercepté\n" - -#~ msgid "Vim: Caught deadly signal\n" -#~ msgstr "Vim : Signal mortel intercepté\n" - #~ msgid "Opening the X display took %<PRId64> msec" #~ msgstr "L'ouverture du display X a pris %<PRId64> ms" @@ -7970,7 +7965,7 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgstr "" #~ "VIMRUN.EXE est introuvable votre $PATH.\n" #~ "Les commandes externes ne feront pas de pause une fois terminées.\n" -#~ "Voir :help win32-vimrun pour plus d'informations." +#~ "Consultez :help win32-vimrun pour plus d'informations." #~ msgid "Vim Warning" #~ msgstr "Alerte Vim" @@ -7982,11 +7977,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgstr "" #~ "E868: Erreur lors de la construction du NFA avec classe d'équivalence" -#~ msgid "E999: (NFA regexp internal error) Should not process NOT node !" -#~ msgstr "" -#~ "E999: (erreur interne du regexp NFA) Un noeud 'NOT' ne devrait pas être " -#~ "traité !" - #~ msgid "E878: (NFA) Could not allocate memory for branch traversal!" #~ msgstr "" #~ "E878: (NFA) Impossible d'allouer la mémoire pour parcourir les branches!" @@ -8306,30 +8296,18 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "can't delete OutputObject attributes" #~ msgstr "impossible d'effacer les attributs d'OutputObject" -#~ msgid "softspace must be an integer" -#~ msgstr "softspace doit être un nombre entier" - #~ msgid "invalid attribute" #~ msgstr "attribut invalide" -#~ msgid "writelines() requires list of strings" -#~ msgstr "writelines() requiert une liste de chaînes" - #~ msgid "E264: Python: Error initialising I/O objects" #~ msgstr "E264: Python : Erreur d'initialisation des objets d'E/S" #~ msgid "empty keys are not allowed" #~ msgstr "les clés vides ne sont pas autorisées" -#~ msgid "Cannot delete DictionaryObject attributes" -#~ msgstr "Impossible d'effacer les attributs de DictionaryObject" - #~ msgid "Cannot modify fixed dictionary" #~ msgstr "Impossible de modifier un dictionnaire fixe" -#~ msgid "Cannot set this attribute" -#~ msgstr "Impossible d'initialiser cet attribut" - #~ msgid "dict is locked" #~ msgstr "dictionnaire est verrouillé" @@ -8348,15 +8326,9 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "Failed to add item to list" #~ msgstr "Ajout à la liste a échoué" -#~ msgid "can only assign lists to slice" -#~ msgstr "seules des tranches peuvent être assignées aux listes" - #~ msgid "internal error: failed to add item to list" #~ msgstr "erreur interne : ajout d'élément à la liste a échoué" -#~ msgid "can only concatenate with lists" -#~ msgstr "on ne peut que concaténer avec des listes" - #~ msgid "cannot delete vim.dictionary attributes" #~ msgstr "impossible d'effacer les attributs de vim.dictionary" @@ -8366,9 +8338,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "cannot set this attribute" #~ msgstr "impossible d'initialiser cet attribut" -#~ msgid "'self' argument must be a dictionary" -#~ msgstr "l'argument 'self' doit être un dictionnaire" - #~ msgid "failed to run function" #~ msgstr "exécution de la fonction a échoué" @@ -8378,24 +8347,9 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "unable to unset option without global value" #~ msgstr "impossible de désactiver une option sans une valeur globale" -#~ msgid "object must be integer" -#~ msgstr "objet doit être un nombre entier" - -#~ msgid "object must be string" -#~ msgstr "objet doit être de type string" - #~ msgid "attempt to refer to deleted tab page" #~ msgstr "tentative de référencer un onglet effacé" -#~ msgid "<tabpage object (deleted) at %p>" -#~ msgstr "<objet onglet (effacé) à %p>" - -#~ msgid "<tabpage object (unknown) at %p>" -#~ msgstr "<objet onglet (inconnu) à %p>" - -#~ msgid "<tabpage %d>" -#~ msgstr "<onglet %d>" - #~ msgid "no such tab page" #~ msgstr "cet onglet n'existe pas" @@ -8408,27 +8362,12 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "cursor position outside buffer" #~ msgstr "curseur positionné en dehors du tampon" -#~ msgid "<window object (deleted) at %p>" -#~ msgstr "<objet fenêtre (effacé) à %p>" - -#~ msgid "<window object (unknown) at %p>" -#~ msgstr "<objet fenêtre (inconnu) à %p>" - -#~ msgid "<window %d>" -#~ msgstr "<fenêtre %d>" - #~ msgid "no such window" #~ msgstr "Cette fenêtre n'existe pas" #~ msgid "attempt to refer to deleted buffer" #~ msgstr "tentative de référencer un tampon effacé" -#~ msgid "<buffer object (deleted) at %p>" -#~ msgstr "<objet tampon (effacé) à %p>" - -#~ msgid "key must be integer" -#~ msgstr "la clé doit être un nombre entier" - #~ msgid "expected vim.buffer object" #~ msgstr "objet vim.buffer attendu" @@ -8467,18 +8406,3 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "internal error: invalid value type" #~ msgstr "erreur interne : type de valeur invalide" - -#~ msgid "E860: Eval did not return a valid python 3 object" -#~ msgstr "E860: Eval n'a pas retourné un object python 3 valid" - -#~ msgid "E861: Failed to convert returned python 3 object to vim value" -#~ msgstr "E861: Conversion d'objet python 3 à une valeur de vim a échoué" - -#~ msgid "E863: return value must be an instance of str" -#~ msgstr "E863: valeur de retour doit être une instance de Str" - -#~ msgid "Only boolean objects are allowed" -#~ msgstr "Seuls les objets booléens sont autorisés" - -#~ msgid "no such key in dictionary" -#~ msgstr "cette clé est inexistante dans le dictionnaire" diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po index 7fe61c45bc..171e155689 100644 --- a/src/nvim/po/it.po +++ b/src/nvim/po/it.po @@ -11,10 +11,10 @@ # msgid "" msgstr "" -"Project-Id-Version: vim 7.4b\n" +"Project-Id-Version: vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-08-03 17:14+0200\n" +"POT-Creation-Date: 2015-08-11 20:58+0200\n" +"PO-Revision-Date: 2015-08-11 22:02+0200\n" "Last-Translator: Vlad Sandrini <vlad.gently@gmail.com>\n" "Language-Team: Italian Antonio Colombo <azc100@gmail." "com> Vlad Sandrini <vlad.gently@gmail." @@ -25,9 +25,8 @@ msgstr "" "Content-Transfer-Encoding: 8-bit\n" #: ../api/private/helpers.c:201 -#, fuzzy msgid "Unable to get option value" -msgstr "impossibile ottenere il valore di opzione" +msgstr "Impossibile ottenere il valore di opzione" #: ../api/private/helpers.c:204 msgid "internal error: unknown option type" @@ -105,11 +104,6 @@ msgstr "E84: Nessun buffer risulta modificato" msgid "E85: There is no listed buffer" msgstr "E85: Non c'è alcun buffer elencato" -#: ../buffer.c:913 -#, c-format -msgid "E86: Buffer %<PRId64> does not exist" -msgstr "E86: Non esiste il buffer %<PRId64>" - #: ../buffer.c:915 msgid "E87: Cannot go beyond last buffer" msgstr "E87: Non posso oltrepassare l'ultimo buffer" @@ -129,7 +123,7 @@ msgstr "" #. wrap around (may cause duplicates) #: ../buffer.c:1423 msgid "W14: Warning: List of file names overflow" -msgstr "W14: Attenzione: Superato limite della lista dei nomi di file" +msgstr "W14: Avviso: Superato limite della lista dei nomi di file" #: ../buffer.c:1555 ../quickfix.c:3361 #, c-format @@ -149,7 +143,7 @@ msgstr "E94: Nessun buffer corrispondente a %s" #: ../buffer.c:2161 #, c-format msgid "line %<PRId64>" -msgstr "linea %<PRId64>" +msgstr "riga %<PRId64>" #: ../buffer.c:2233 msgid "E95: Buffer with this name already exists" @@ -182,17 +176,17 @@ msgstr "[in sola lettura]" #: ../buffer.c:2524 #, c-format msgid "1 line --%d%%--" -msgstr "1 linea --%d%%--" +msgstr "1 riga --%d%%--" #: ../buffer.c:2526 #, c-format msgid "%<PRId64> lines --%d%%--" -msgstr "%<PRId64> linee --%d%%--" +msgstr "%<PRId64> righe --%d%%--" #: ../buffer.c:2530 #, c-format msgid "line %<PRId64> of %<PRId64> --%d%%-- col " -msgstr "linea %<PRId64> di %<PRId64> --%d%%-- col " +msgstr "riga %<PRId64> di %<PRId64> --%d%%-- col " #: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554 msgid "[No Name]" @@ -251,7 +245,7 @@ msgstr "Segni per %s:" #: ../buffer.c:4543 #, c-format msgid " line=%<PRId64> id=%d name=%s" -msgstr " linea=%<PRId64> id=%d, nome=%s" +msgstr " riga=%<PRId64> id=%d, nome=%s" #: ../cursor_shape.c:68 msgid "E545: Missing colon" @@ -347,11 +341,11 @@ msgstr " modalità ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" #: ../edit.c:85 msgid " Whole line completion (^L^N^P)" -msgstr " Completamento Linea Intera (^L^N^P)" +msgstr " Completamento riga intera (^L^N^P)" #: ../edit.c:86 msgid " File name completion (^F^N^P)" -msgstr " Completamento nomi File (^F^N^P)" +msgstr " Completamento nomi file (^F^N^P)" #: ../edit.c:87 msgid " Tag completion (^]^N^P)" @@ -375,7 +369,7 @@ msgstr " Completamento Thesaurus (^T^N^P)" #: ../edit.c:93 msgid " Command-line completion (^V^N^P)" -msgstr " Completamento linea comandi (^V^N^P)" +msgstr " Completamento riga comandi (^V^N^P)" #: ../edit.c:94 msgid " User defined completion (^U^N^P)" @@ -453,7 +447,7 @@ msgstr "Ritorno all'originale" #: ../edit.c:4621 msgid "Word from other line" -msgstr "Parola da un'altra linea" +msgstr "Parola da un'altra riga" #: ../edit.c:4624 msgid "The only match" @@ -681,6 +675,11 @@ msgstr "E696: Manca virgola nella Lista: %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: Manca ']' a fine Lista: %s" +#: ../eval.c:5750 +#, c-format +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "Memoria insufficiente per impostarlo, recupero memoria fallito!" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -755,15 +754,15 @@ msgstr "E785: complete() può essere usata solo in modalità inserimento" msgid "&Ok" msgstr "&OK" -#: ../eval.c:8676 -#, c-format -msgid "E737: Key already exists: %s" -msgstr "E737: Chiave già esistente: %s" - #: ../eval.c:8692 msgid "extend() argument" msgstr "argomento di extend()" +#: ../eval.c:9345 +#, c-format +msgid "E737: Key already exists: %s" +msgstr "E737: Chiave già esistente: %s" + #: ../eval.c:8915 msgid "map() argument" msgstr "argomento di map()" @@ -775,7 +774,7 @@ msgstr "argomento di filter()" #: ../eval.c:9229 #, c-format msgid "+-%s%3ld lines: " -msgstr "+-%s%3ld linee: " +msgstr "+-%s%3ld righe: " #: ../eval.c:9291 #, c-format @@ -827,18 +826,16 @@ msgid "sort() argument" msgstr "argomento di sort()" #: ../eval.c:13721 -#, fuzzy msgid "uniq() argument" -msgstr "argomento di add()" +msgstr "argomento di uniq()" #: ../eval.c:13776 msgid "E702: Sort compare function failed" msgstr "E702: Funzione confronto nel sort non riuscita" #: ../eval.c:13806 -#, fuzzy msgid "E882: Uniq compare function failed" -msgstr "E702: Funzione confronto nel sort non riuscita" +msgstr "E882: Funzione di comparazione 'uniq' fallita" #: ../eval.c:14085 msgid "(Invalid)" @@ -963,16 +960,12 @@ msgid "E129: Function name required" msgstr "E129: Nome funzione necessario" #: ../eval.c:17824 -#, fuzzy, c-format msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "" -"E128: Il nome funzione deve iniziare con una maiuscola o contenere ':': %s" +msgstr "E128: Il nome funzione deve iniziare con una maiuscola o \"s:\": %s" #: ../eval.c:17833 -#, fuzzy, c-format msgid "E884: Function name cannot contain a colon: %s" -msgstr "" -"E128: Il nome funzione deve iniziare con una maiuscola o contenere ':': %s" +msgstr "E884: Il nome funzione non può contenere una virgola: %s" #: ../eval.c:18336 #, c-format @@ -1050,21 +1043,21 @@ msgstr "> %d, Esa %08x, Ottale %o" #: ../ex_cmds.c:684 msgid "E134: Move lines into themselves" -msgstr "E134: Movimento di linee verso se stesse" +msgstr "E134: Movimento di righe verso se stesse" #: ../ex_cmds.c:747 msgid "1 line moved" -msgstr "1 linea mossa" +msgstr "1 riga mossa" #: ../ex_cmds.c:749 #, c-format msgid "%<PRId64> lines moved" -msgstr "%<PRId64> linee mosse" +msgstr "%<PRId64> righe mosse" #: ../ex_cmds.c:1175 #, c-format msgid "%<PRId64> lines filtered" -msgstr "%<PRId64> linee filtrate" +msgstr "%<PRId64> righe filtrate" #: ../ex_cmds.c:1194 msgid "E135: *Filter* Autocommands must not change current buffer" @@ -1077,7 +1070,7 @@ msgstr "[Non salvato dopo l'ultima modifica]\n" #: ../ex_cmds.c:1424 #, c-format msgid "%sviminfo: %s in line: " -msgstr "%sviminfo: %s nella linea: " +msgstr "%sviminfo: %s nella riga: " #: ../ex_cmds.c:1431 msgid "E136: viminfo: Too many errors, skipping rest of file" @@ -1246,12 +1239,12 @@ msgstr "%<PRId64> sostituzioni" #: ../ex_cmds.c:4392 msgid " on 1 line" -msgstr " in 1 linea" +msgstr " in 1 riga" #: ../ex_cmds.c:4395 #, c-format msgid " on %<PRId64> lines" -msgstr " in %<PRId64> linee" +msgstr " in %<PRId64> righe" #: ../ex_cmds.c:4438 msgid "E147: Cannot do :global recursive" @@ -1264,7 +1257,7 @@ msgstr "E148: Manca espressione regolare nel comando 'global'" #: ../ex_cmds.c:4508 #, c-format msgid "Pattern found in every line: %s" -msgstr "Espressione trovata su ogni linea: %s" +msgstr "Espressione trovata su ogni riga: %s" #: ../ex_cmds.c:4510 #, c-format @@ -1362,6 +1355,11 @@ msgstr "E158: Nome buffer non valido: %s" msgid "E157: Invalid sign ID: %<PRId64>" msgstr "E157: ID 'sign' non valido: %<PRId64>" +#: ../ex_cmds.c:5517 +#, c-format +msgid "E885: Not possible to change sign %s" +msgstr "E885: Impossibile cambiare segno %s" + #: ../ex_cmds.c:6066 msgid " (not supported)" msgstr " (non supportata)" @@ -1377,7 +1375,7 @@ msgstr "Entro modalità Debug. Batti \"cont\" per continuare." #: ../ex_cmds2.c:143 ../ex_docmd.c:759 #, c-format msgid "line %<PRId64>: %s" -msgstr "linea %<PRId64>: %s" +msgstr "riga %<PRId64>: %s" #: ../ex_cmds2.c:145 #, c-format @@ -1387,7 +1385,7 @@ msgstr "com: %s" #: ../ex_cmds2.c:322 #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" -msgstr "Pausa in \"%s%s\" linea %<PRId64>" +msgstr "Pausa in \"%s%s\" riga %<PRId64>" #: ../ex_cmds2.c:581 #, c-format @@ -1401,7 +1399,7 @@ msgstr "Nessun 'breakpoint' definito" #: ../ex_cmds2.c:617 #, c-format msgid "%3d %s %s line %<PRId64>" -msgstr "%3d %s %s linea %<PRId64>" +msgstr "%3d %s %s riga %<PRId64>" #: ../ex_cmds2.c:942 msgid "E750: First use \":profile start {fname}\"" @@ -1424,7 +1422,7 @@ msgstr "E162: Buffer \"%s\" non salvato dopo modifica" #: ../ex_cmds2.c:1480 msgid "Warning: Entered other buffer unexpectedly (check autocommands)" msgstr "" -"Attenzione: Entrato in altro buffer inaspettatamente (controllare " +"Avviso: Entrato in altro buffer inaspettatamente (controllare " "autocomandi)" #: ../ex_cmds2.c:1826 @@ -1472,7 +1470,7 @@ msgstr "non riesco ad eseguire \"%s\"" #: ../ex_cmds2.c:2520 #, c-format msgid "line %<PRId64>: could not source \"%s\"" -msgstr "linea %<PRId64>: non riesco ad eseguire \"%s\"" +msgstr "riga %<PRId64>: non riesco ad eseguire \"%s\"" #: ../ex_cmds2.c:2535 #, c-format @@ -1482,7 +1480,7 @@ msgstr "eseguo \"%s\"" #: ../ex_cmds2.c:2537 #, c-format msgid "line %<PRId64>: sourcing \"%s\"" -msgstr "linea %<PRId64>: eseguo \"%s\"" +msgstr "riga %<PRId64>: eseguo \"%s\"" #: ../ex_cmds2.c:2693 #, c-format @@ -1511,7 +1509,7 @@ msgstr "gestore di errore" #: ../ex_cmds2.c:3020 msgid "W15: Warning: Wrong line separator, ^M may be missing" -msgstr "W15: Attenzione: Separatore di linea errato, forse manca ^M" +msgstr "W15: Avviso: Separatore di riga errato, forse manca ^M" #: ../ex_cmds2.c:3139 msgid "E167: :scriptencoding used outside of a sourced file" @@ -1613,10 +1611,10 @@ msgstr "E174: Il comando esiste già: aggiungi ! per sostituirlo" #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" Nome Arg. Inter Completo Definizione" +" Nome Arg. Indir. Completo Definizione" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" @@ -1642,6 +1640,10 @@ msgstr "E178: Valore predefinito del contatore non valido" msgid "E179: argument required for -complete" msgstr "E179: argomento necessario per -complete" +#: ../ex_docmd.c:4923 +msgid "E179: argument required for -addr" +msgstr "E179: argomento necessario per -addr" + #: ../ex_docmd.c:4635 #, c-format msgid "E181: Invalid attribute: %s" @@ -1665,6 +1667,11 @@ msgstr "E841: Nome riservato, non usabile in un comando definito dall'utente" msgid "E184: No such user-defined command: %s" msgstr "E184: Comando definito dall'utente %s inesistente" +#: ../ex_docmd.c:5516 +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Tipo di indirizzo non valido: %s" + #: ../ex_docmd.c:5219 #, c-format msgid "E180: Invalid complete value: %s" @@ -1673,7 +1680,7 @@ msgstr "E180: Valore %s non valido per 'complete'" #: ../ex_docmd.c:5225 msgid "E468: Completion argument only allowed for custom completion" msgstr "" -"E468: Argomento di completamento permesso solo per completamento " +"E468: Argomento di completamento consentito solo per completamento " "personalizzato" #: ../ex_docmd.c:5231 @@ -1822,7 +1829,7 @@ msgstr "Eccezione scartata: %s" #: ../ex_eval.c:588 ../ex_eval.c:634 #, c-format msgid "%s, line %<PRId64>" -msgstr "%s, linea %<PRId64>" +msgstr "%s, riga %<PRId64>" #. always scroll up, don't overwrite #: ../ex_eval.c:608 @@ -1968,7 +1975,7 @@ msgstr "" #: ../ex_getln.c:5047 msgid "Command Line" -msgstr "Linea di Comando" +msgstr "Riga di Comando" #: ../ex_getln.c:5048 msgid "Search String" @@ -1980,7 +1987,7 @@ msgstr "Espressione" #: ../ex_getln.c:5050 msgid "Input Line" -msgstr "Linea di Input" +msgstr "Riga di Input" #: ../ex_getln.c:5117 msgid "E198: cmd_pchar beyond the command length" @@ -2098,7 +2105,7 @@ msgstr "[manca CR]" #: ../fileio.c:1819 msgid "[long lines split]" -msgstr "[linee lunghe divise]" +msgstr "[righe lunghe divise]" #: ../fileio.c:1823 ../fileio.c:3512 msgid "[NOT converted]" @@ -2111,12 +2118,12 @@ msgstr "[convertito]" #: ../fileio.c:1831 #, c-format msgid "[CONVERSION ERROR in line %<PRId64>]" -msgstr "[ERRORE DI CONVERSIONE alla linea %<PRId64>]" +msgstr "[ERRORE DI CONVERSIONE alla riga %<PRId64>]" #: ../fileio.c:1835 #, c-format msgid "[ILLEGAL BYTE in line %<PRId64>]" -msgstr "[BYTE NON VALIDO alla linea %<PRId64>]" +msgstr "[BYTE NON VALIDO alla riga %<PRId64>]" #: ../fileio.c:1838 msgid "[READ ERRORS]" @@ -2144,7 +2151,7 @@ msgstr "E203: Buffer in scrittura cancellato o scaricato dagli autocomandi" #: ../fileio.c:2486 msgid "E204: Autocommand changed number of lines in unexpected way" -msgstr "E204: L'autocomando ha modificato numero linee in maniera imprevista" +msgstr "E204: L'autocomando ha modificato numero righe in maniera imprevista" #: ../fileio.c:2548 ../fileio.c:2565 msgid "is not a file or writable device" @@ -2220,7 +2227,7 @@ msgid "" "E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to " "override)" msgstr "" -"E513: errore in scrittura, conversione fallita alla linea %<PRId64> (rendere " +"E513: errore in scrittura, conversione fallita alla riga %<PRId64> (rendere " "'fenc' nullo per eseguire comunque)" #: ../fileio.c:3448 @@ -2234,7 +2241,7 @@ msgstr " ERRORE DI CONVERSIONE" #: ../fileio.c:3509 #, c-format msgid " in line %<PRId64>;" -msgstr " alla linea %<PRId64>;" +msgstr " alla riga %<PRId64>;" #: ../fileio.c:3519 msgid "[Device]" @@ -2278,7 +2285,7 @@ msgid "" "WARNING: Original file may be lost or damaged\n" msgstr "" "\n" -"ATTENZIONE: Il file originale può essere perso o danneggiato\n" +"AVVISO: Il file originale può essere perso o danneggiato\n" #: ../fileio.c:3675 msgid "don't quit the editor until the file is successfully written!" @@ -2310,12 +2317,12 @@ msgstr "[in formato UNIX]" #: ../fileio.c:3831 msgid "1 line, " -msgstr "1 linea, " +msgstr "1 riga, " #: ../fileio.c:3833 #, c-format msgid "%<PRId64> lines, " -msgstr "%<PRId64> linee," +msgstr "%<PRId64> righe," #: ../fileio.c:3836 msgid "1 character" @@ -2332,14 +2339,14 @@ msgstr "[noeol]" #: ../fileio.c:3849 msgid "[Incomplete last line]" -msgstr "[Manca carattere di fine linea]" +msgstr "[Manca carattere di fine riga]" #. don't overwrite messages here #. must give this prompt #. don't use emsg() here, don't want to flush the buffers #: ../fileio.c:3865 msgid "WARNING: The file has been changed since reading it!!!" -msgstr "ATTENZIONE: File modificato dopo essere stato letto dall'Editor!!!" +msgstr "AVVISO: File modificato dopo essere stato letto dall'Editor!!!" #: ../fileio.c:3867 msgid "Do you really want to write to it" @@ -2375,7 +2382,7 @@ msgid "" "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as " "well" msgstr "" -"W12: Attenzione: File \"%s\" modificato su disco ed anche nel buffer di Vim" +"W12: Avviso: File \"%s\" modificato su disco ed anche nel buffer di Vim" #: ../fileio.c:4907 msgid "See \":help W12\" for more info." @@ -2384,7 +2391,7 @@ msgstr "Vedere \":help W12\" per ulteriori informazioni." #: ../fileio.c:4910 #, c-format msgid "W11: Warning: File \"%s\" has changed since editing started" -msgstr "W11: Attenzione: File \"%s\" modificato dopo l'apertura" +msgstr "W11: Avviso: File \"%s\" modificato dopo l'apertura" #: ../fileio.c:4911 msgid "See \":help W11\" for more info." @@ -2393,7 +2400,7 @@ msgstr "Vedere \":help W11\" per ulteriori informazioni." #: ../fileio.c:4914 #, c-format msgid "W16: Warning: Mode of file \"%s\" has changed since editing started" -msgstr "W16: Attenzione: Modo File \"%s\" modificato dopo l'apertura" +msgstr "W16: Avviso: Modo File \"%s\" modificato dopo l'apertura" #: ../fileio.c:4915 msgid "See \":help W16\" for more info." @@ -2402,11 +2409,11 @@ msgstr "Vedere \":help W16\" per ulteriori informazioni." #: ../fileio.c:4927 #, c-format msgid "W13: Warning: File \"%s\" has been created after editing started" -msgstr "W13: Attenzione: Il file \"%s\" risulta creato dopo l'apertura" +msgstr "W13: Avviso: Il file \"%s\" risulta creato dopo l'apertura" #: ../fileio.c:4947 msgid "Warning" -msgstr "Attenzione" +msgstr "Avviso" #: ../fileio.c:4948 msgid "" @@ -2520,7 +2527,7 @@ msgstr "E351: Non posso cancellare piegatura con il 'foldmethod' in uso" #: ../fold.c:1784 #, c-format msgid "+--%3ld lines folded " -msgstr "+--%3ld linee piegate" +msgstr "+--%3ld righe piegate" #. buffer has already been read #: ../getchar.c:273 @@ -2667,18 +2674,17 @@ msgid "E17: \"%s\" is a directory" msgstr "E17: \"%s\" è una directory" #: ../globals.h:1020 -#, fuzzy msgid "E900: Invalid job id" -msgstr "E49: Quantità di 'scroll' non valida" +msgstr "E900: 'Job id' non valido" #: ../globals.h:1021 msgid "E901: Job table is full" -msgstr "" +msgstr "E901: Job table piena" #: ../globals.h:1022 #, c-format msgid "E902: \"%s\" is not an executable" -msgstr "" +msgstr "E902: \"%s\" non è un esegubile" #: ../globals.h:1024 #, c-format @@ -2687,7 +2693,7 @@ msgstr "E364: Chiamata a libreria fallita per \"%s()\"" #: ../globals.h:1026 msgid "E19: Mark has invalid line number" -msgstr "E19: 'Mark' con numero linea non valido" +msgstr "E19: 'Mark' con numero riga non valido" #: ../globals.h:1027 msgid "E20: Mark not set" @@ -2703,7 +2709,7 @@ msgstr "E22: Script troppo nidificati" #: ../globals.h:1031 msgid "E23: No alternate file" -msgstr "E23: Nessun file alternato" +msgstr "E23: Nessun file alternativo" #: ../globals.h:1032 msgid "E24: No such abbreviation" @@ -2728,7 +2734,7 @@ msgstr "E29: Ancora nessun testo inserito" #: ../globals.h:1038 msgid "E30: No previous command line" -msgstr "E30: Nessuna linea comandi precedente" +msgstr "E30: Nessuna riga comandi precedente" #: ../globals.h:1039 msgid "E31: No such mapping" @@ -2791,9 +2797,8 @@ msgid "E37: No write since last change (add ! to override)" msgstr "E37: Non salvato dopo modifica (aggiungi ! per eseguire comunque)" #: ../globals.h:1055 -#, fuzzy msgid "E37: No write since last change" -msgstr "[Non salvato dopo l'ultima modifica]\n" +msgstr "E37: Non salvato dall'ultima modifica" #: ../globals.h:1056 msgid "E38: Null argument" @@ -2857,8 +2862,7 @@ msgstr "E46: Non posso cambiare la variabile read-only \"%s\"" #: ../globals.h:1075 #, c-format msgid "E794: Cannot set variable in the sandbox: \"%s\"" -msgstr "" -"E794: Non posso impostare la variabile read-only in ambiente protetto: \"%s\"" +msgstr "E794: Non posso impostare la variabile read-only in ambiente protetto: \"%s\"" #: ../globals.h:1076 msgid "E47: Error while reading errorfile" @@ -2957,6 +2961,11 @@ msgstr "E363: l'espressione usa troppa memoria rispetto a 'maxmempattern'" msgid "E749: empty buffer" msgstr "E749: buffer vuoto" +#: ../globals.h:1226 +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: Non esiste il buffer %<PRId64>" + #: ../globals.h:1108 msgid "E682: Invalid search pattern or delimiter" msgstr "E682: Espressione o delimitatore di ricerca non validi" @@ -3270,11 +3279,11 @@ msgid "" " # line" msgstr "" "\n" -" # linea" +" # riga" #: ../if_cscope.c:1713 msgid "filename / context / line\n" -msgstr "nomefile / contest / linea\n" +msgstr "nomefile / contest / riga\n" #: ../if_cscope.c:1809 #, c-format @@ -3336,16 +3345,16 @@ msgstr "Non posso aprire come script output: \"" #: ../main.c:1622 msgid "Vim: Warning: Output is not to a terminal\n" -msgstr "Vim: Attenzione: Output non diretto a un terminale\n" +msgstr "Vim: Avviso: Output non diretto a un terminale\n" #: ../main.c:1624 msgid "Vim: Warning: Input is not from a terminal\n" -msgstr "Vim: Attenzione: Input non proveniente da un terminale\n" +msgstr "Vim: Avviso: Input non proveniente da un terminale\n" #. just in case.. #: ../main.c:1891 msgid "pre-vimrc command line" -msgstr "linea comandi prima di vimrc" +msgstr "riga comandi prima di vimrc" #: ../main.c:1964 #, c-format @@ -3538,7 +3547,7 @@ msgstr "+\t\t\tPosizionati alla fine del file" #: ../main.c:2231 msgid "+<lnum>\t\tStart at line <lnum>" -msgstr "+<lnum>\t\tPosizionati alla linea <lnum>" +msgstr "+<lnum>\t\tPosizionati alla riga <lnum>" #: ../main.c:2232 msgid "--cmd <command>\tExecute <command> before loading any vimrc file" @@ -3599,7 +3608,7 @@ msgid "" "mark line col file/text" msgstr "" "\n" -"mark linea col.file/testo" +"mark riga col.file/testo" #. Highlight title #: ../mark.c:789 @@ -3608,7 +3617,7 @@ msgid "" " jump line col file/text" msgstr "" "\n" -" salt.linea col.file/testo" +" salt.riga col.file/testo" #. Highlight title #: ../mark.c:831 @@ -3617,7 +3626,7 @@ msgid "" "change line col text" msgstr "" "\n" -"modif linea col testo" +"modif riga col testo" #: ../mark.c:1238 msgid "" @@ -3777,7 +3786,7 @@ msgstr "File originale \"%s\"" #: ../memline.c:995 msgid "E308: Warning: Original file may have been changed" msgstr "" -"E308: Attenzione: il file originale può essere stato modificato nel frattempo" +"E308: Avviso: il file originale può essere stato modificato nel frattempo" #: ../memline.c:1061 #, c-format @@ -3786,7 +3795,7 @@ msgstr "E309: Impossibile leggere blocco 1 da %s" #: ../memline.c:1065 msgid "???MANY LINES MISSING" -msgstr "???MOLTE LINEE MANCANTI" +msgstr "???MOLTE RIGHE MANCANTI" #: ../memline.c:1076 msgid "???LINE COUNT WRONG" @@ -3798,7 +3807,7 @@ msgstr "???BLOCCO VUOTO" #: ../memline.c:1103 msgid "???LINES MISSING" -msgstr "???LINEE MANCANTI" +msgstr "???RIGHE MANCANTI" #: ../memline.c:1128 #, c-format @@ -3811,12 +3820,12 @@ msgstr "???BLOCCO MANCANTE" #: ../memline.c:1147 msgid "??? from here until ???END lines may be messed up" -msgstr "??? da qui fino a ???END le linee possono essere fuori ordine" +msgstr "??? da qui fino a ???END le righe possono essere fuori ordine" #: ../memline.c:1164 msgid "??? from here until ???END lines may have been inserted/deleted" msgstr "" -"??? da qui fino a ???END linee possono essere state inserite/cancellate" +"??? da qui fino a ???END righe possono essere state inserite/cancellate" #: ../memline.c:1181 msgid "???END" @@ -3829,7 +3838,7 @@ msgstr "E311: Recupero Interrotto" #: ../memline.c:1243 msgid "" "E312: Errors detected while recovering; look for lines starting with ???" -msgstr "E312: Errori durante recupero; controlla linee che iniziano con ???" +msgstr "E312: Errori durante recupero; controlla righe che iniziano con ???" #: ../memline.c:1245 msgid "See \":help E312\" for more information." @@ -3990,12 +3999,12 @@ msgstr "E314: Preservazione fallita" #: ../memline.c:1819 #, c-format msgid "E315: ml_get: invalid lnum: %<PRId64>" -msgstr "E315: ml_get: numero linea non valido: %<PRId64>" +msgstr "E315: ml_get: numero riga non valido: %<PRId64>" #: ../memline.c:1851 #, c-format msgid "E316: ml_get: cannot find line %<PRId64>" -msgstr "E316: ml_get: non riesco a trovare la linea %<PRId64>" +msgstr "E316: ml_get: non riesco a trovare la riga %<PRId64>" #: ../memline.c:2236 msgid "E317: pointer block id wrong 3" @@ -4020,7 +4029,7 @@ msgstr "cancellato blocco 1?" #: ../memline.c:2707 #, c-format msgid "E320: Cannot find line %<PRId64>" -msgstr "E320: Non riesco a trovare la linea %<PRId64>" +msgstr "E320: Non riesco a trovare la riga %<PRId64>" #: ../memline.c:2916 msgid "E317: pointer block id wrong" @@ -4033,12 +4042,12 @@ msgstr "pe_line_count a zero" #: ../memline.c:2955 #, c-format msgid "E322: line number out of range: %<PRId64> past the end" -msgstr "E322: numero linea non ammissibile: %<PRId64> dopo la fine" +msgstr "E322: numero riga non ammissibile: %<PRId64> dopo la fine" #: ../memline.c:2959 #, c-format msgid "E323: line count wrong in block %<PRId64>" -msgstr "E323: contatore linee errato nel blocco %<PRId64>" +msgstr "E323: contatore righe errato nel blocco %<PRId64>" #: ../memline.c:2999 msgid "Stack size increases" @@ -4252,7 +4261,7 @@ msgstr "Errore/i eseguendo %s:" #: ../message.c:445 #, c-format msgid "line %4ld:" -msgstr "linea %4ld:" +msgstr "riga %4ld:" #: ../message.c:617 #, c-format @@ -4274,7 +4283,7 @@ msgstr "Premi INVIO o un comando per proseguire" #: ../message.c:1843 #, c-format msgid "%s line %<PRId64>" -msgstr "%s linea %<PRId64>" +msgstr "%s riga %<PRId64>" #: ../message.c:2392 msgid "-- More --" @@ -4282,7 +4291,7 @@ msgstr "-- Ancora --" #: ../message.c:2398 msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit " -msgstr " SPAZIO/d/j: schermo/pagina/linea giù, b/u/k: su, q: abbandona " +msgstr " SPAZIO/d/j: schermo/pagina/riga giù, b/u/k: su, q: abbandona " #: ../message.c:3021 ../message.c:3031 msgid "Question" @@ -4334,7 +4343,7 @@ msgstr "E767: Troppi argomenti per printf()" #: ../misc1.c:2256 msgid "W10: Warning: Changing a readonly file" -msgstr "W10: Attenzione: Modifica a un file in sola-lettura" +msgstr "W10: Avviso: Modifica a un file in sola-lettura" #: ../misc1.c:2537 msgid "Type number and <Enter> or click with mouse (empty cancels): " @@ -4347,21 +4356,21 @@ msgstr "Inserire numero e <Invio> (vuoto per annullare): " #: ../misc1.c:2585 msgid "1 more line" -msgstr "1 linea in più" +msgstr "1 riga in più" #: ../misc1.c:2588 msgid "1 line less" -msgstr "1 linea in meno" +msgstr "1 riga in meno" #: ../misc1.c:2593 #, c-format msgid "%<PRId64> more lines" -msgstr "%<PRId64> linee in più" +msgstr "%<PRId64> righe in più" #: ../misc1.c:2596 #, c-format msgid "%<PRId64> fewer lines" -msgstr "%<PRId64> linee in meno" +msgstr "%<PRId64> righe in meno" #: ../misc1.c:2599 msgid " (Interrupted)" @@ -4386,7 +4395,7 @@ msgstr "E774: opzione 'operatorfunc' non impostata" #: ../normal.c:2637 msgid "Warning: terminal cannot highlight" -msgstr "Attenzione: il terminale non è in grado di evidenziare" +msgstr "Avviso: il terminale non è in grado di evidenziare" #: ../normal.c:2807 msgid "E348: No string under cursor" @@ -4415,36 +4424,36 @@ msgstr "Batti :quit<Invio> per uscire da Vim" #: ../ops.c:248 #, c-format msgid "1 line %sed 1 time" -msgstr "1 linea %sa 1 volta" +msgstr "1 riga %sa 1 volta" #: ../ops.c:250 #, c-format msgid "1 line %sed %d times" -msgstr "1 linea %sa %d volte" +msgstr "1 riga %sa %d volte" #: ../ops.c:253 #, c-format msgid "%<PRId64> lines %sed 1 time" -msgstr "%<PRId64> linee %se 1 volta" +msgstr "%<PRId64> righe %se 1 volta" #: ../ops.c:256 #, c-format msgid "%<PRId64> lines %sed %d times" -msgstr "%<PRId64> linee %se %d volte" +msgstr "%<PRId64> righe %se %d volte" #: ../ops.c:592 #, c-format msgid "%<PRId64> lines to indent... " -msgstr "%<PRId64> linee da rientrare... " +msgstr "%<PRId64> righe da rientrare... " #: ../ops.c:634 msgid "1 line indented " -msgstr "1 linea rientrata " +msgstr "1 riga rientrata " #: ../ops.c:636 #, c-format msgid "%<PRId64> lines indented " -msgstr "%<PRId64> linee rientrate " +msgstr "%<PRId64> righe rientrate " #: ../ops.c:938 msgid "E748: No previously used register" @@ -4457,30 +4466,30 @@ msgstr "non riesco a salvare in un registro; cancello comunque" #: ../ops.c:1929 msgid "1 line changed" -msgstr "1 linea cambiata" +msgstr "1 riga cambiata" #: ../ops.c:1931 #, c-format msgid "%<PRId64> lines changed" -msgstr "%<PRId64> linee cambiate" +msgstr "%<PRId64> righe cambiate" #: ../ops.c:2521 msgid "block of 1 line yanked" -msgstr "blocco di 1 linea messo in registro" +msgstr "blocco di 1 riga messo in registro" #: ../ops.c:2523 msgid "1 line yanked" -msgstr "1 linea messa in registro" +msgstr "1 riga messa in registro" #: ../ops.c:2525 #, c-format msgid "block of %<PRId64> lines yanked" -msgstr "blocco di %<PRId64> linee messo in registro" +msgstr "blocco di %<PRId64> righe messo in registro" #: ../ops.c:2528 #, c-format msgid "%<PRId64> lines yanked" -msgstr "%<PRId64> linee messe in registro" +msgstr "%<PRId64> righe messe in registro" #: ../ops.c:2710 #, c-format @@ -4513,6 +4522,13 @@ msgstr "" msgid "E574: Unknown register type %d" msgstr "E574: Tipo di registro sconosciuto: %d" +#: ../ops.c:4897 +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "E883: espressione di ricerca e registro dell'espressione non possono " +"contenere due o più righe" + #: ../ops.c:5089 #, c-format msgid "%<PRId64> Cols; " @@ -4524,7 +4540,7 @@ msgid "" "Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " "%<PRId64> of %<PRId64> Bytes" msgstr "" -"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; " +"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; " "%<PRId64> di %<PRId64> Caratt." #: ../ops.c:5105 @@ -4533,7 +4549,7 @@ msgid "" "Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " "%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes" msgstr "" -"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; " +"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; " "%<PRId64> di %<PRId64> Caratt.; %<PRId64> di %<PRId64> Byte" #: ../ops.c:5123 @@ -4542,7 +4558,7 @@ msgid "" "Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte " "%<PRId64> of %<PRId64>" msgstr "" -"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " +"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " "Caratt. %<PRId64> di %<PRId64>" #: ../ops.c:5133 @@ -4551,7 +4567,7 @@ msgid "" "Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char " "%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>" msgstr "" -"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " +"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; " "Caratt. %<PRId64> di %<PRId64>; Byte %<PRId64> di %<PRId64>" #: ../ops.c:5146 @@ -4597,6 +4613,11 @@ msgstr "E522: Non trovato in 'termcap'" msgid "E539: Illegal character <%s>" msgstr "E539: Carattere non ammesso <%s>" +#: ../option.c:2253 +#, c-format +msgid "For option %s" +msgstr "Per opzione %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: Non posso assegnare a 'term' il valore 'stringa nulla'" @@ -4675,7 +4696,7 @@ msgstr "W17: Arabo richiede UTF-8, esegui ':set encoding=utf-8'" #: ../option.c:5623 #, c-format msgid "E593: Need at least %d lines" -msgstr "E593: Servono almeno %d linee" +msgstr "E593: Servono almeno %d righe" #: ../option.c:5631 #, c-format @@ -4832,7 +4853,7 @@ msgstr "(%d di %d)%s%s: " #: ../quickfix.c:1676 msgid " (line deleted)" -msgstr " (linea cancellata)" +msgstr " (riga cancellata)" #: ../quickfix.c:1863 msgid "E380: At bottom of quickfix stack" @@ -4869,9 +4890,8 @@ msgid "E777: String or List expected" msgstr "E777: aspettavo Stringa o Lista" #: ../regexp.c:359 -#, fuzzy, c-format msgid "E369: invalid item in %s%%[]" -msgstr "E239: Testo 'sign' non valido: %s" +msgstr "E369: elemento non valido in %s%%[]" #: ../regexp.c:374 #, c-format @@ -4984,6 +5004,10 @@ msgstr "E554: Errore sintattico in %s{...}" msgid "External submatches:\n" msgstr "Sotto-corrispondenze esterne:\n" +#: ../regexp.c:2470 +msgid "E888: (NFA regexp) cannot repeat %s" +msgstr "E888: (NFA regexp) non riesco a ripetere %s" + #: ../regexp.c:7022 msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " @@ -4992,6 +5016,10 @@ msgstr "" "E864: \\%#= può essere seguito solo da 0, 1 o 2. Sarà usato il motore " "automatico " +#: ../regexp.c:7039 +msgid "Switching to backtracking RE engine for pattern: " +msgstr "Passo alla ricerca di RE col vecchio metodo: " + #: ../regexp_nfa.c:239 msgid "E865: (NFA) Regexp end encountered prematurely" msgstr "E865: (NFA) Fine prematura dell'espressione regolare" @@ -5004,7 +5032,7 @@ msgstr "E866: (NFA regexp) %c fuori posto" #: ../regexp_nfa.c:242 #, c-format msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" -msgstr "" +msgstr "E877: (NFA regexp) Classe di caratteri non valida: %<PRId64>" #: ../regexp_nfa.c:1261 #, c-format @@ -5124,7 +5152,7 @@ msgstr " VISUALE" #: ../screen.c:7470 msgid " VISUAL LINE" -msgstr " VISUALE LINEA" +msgstr " VISUALE RIGA" #: ../screen.c:7471 msgid " VISUAL BLOCK" @@ -5136,7 +5164,7 @@ msgstr " SELEZIONA" #: ../screen.c:7473 msgid " SELECT LINE" -msgstr " SELEZIONA LINEA" +msgstr " SELEZIONA RIGA" #: ../screen.c:7474 msgid " SELECT BLOCK" @@ -5202,7 +5230,7 @@ msgstr "Cerco nel file incluso: %s" #: ../search.c:4405 msgid "E387: Match is on current line" -msgstr "E387: Corrispondenza nella linea corrente" +msgstr "E387: Corrispondenza nella riga corrente" #: ../search.c:4517 msgid "All included files were found" @@ -5246,12 +5274,12 @@ msgstr "E758: File ortografico troncato" #: ../spell.c:953 #, c-format msgid "Trailing text in %s line %d: %s" -msgstr "Testo in eccesso in %s linea %d: %s" +msgstr "Testo in eccesso in %s riga %d: %s" #: ../spell.c:954 #, c-format msgid "Affix name too long in %s line %d: %s" -msgstr "Nome affisso troppo lungo in %s linea %d: %s" +msgstr "Nome affisso troppo lungo in %s riga %d: %s" #: ../spell.c:955 msgid "E761: Format error in affix file FOL, LOW or UPP" @@ -5272,7 +5300,7 @@ msgstr "E756: Controllo ortografico non abilitato" #: ../spell.c:2249 #, c-format msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" -msgstr "Attenzione: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\"" +msgstr "Avviso: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\"" #: ../spell.c:2473 #, c-format @@ -5298,7 +5326,7 @@ msgstr "E770: Sezione non supportata nel file ortografico" #: ../spell.c:3762 #, c-format msgid "Warning: region %s not supported" -msgstr "Attenzione: regione %s non supportata" +msgstr "Avviso: regione %s non supportata" #: ../spell.c:4550 #, c-format @@ -5308,7 +5336,7 @@ msgstr "Lettura file affissi %s ..." #: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140 #, c-format msgid "Conversion failure for word in %s line %d: %s" -msgstr "Conversione fallita per una parola in %s linea %d: %s" +msgstr "Conversione fallita per una parola in %s riga %d: %s" #: ../spell.c:4630 ../spell.c:6170 #, c-format @@ -5318,12 +5346,12 @@ msgstr "Conversione in %s non supportata: da %s a %s" #: ../spell.c:4642 #, c-format msgid "Invalid value for FLAG in %s line %d: %s" -msgstr "Valore di FLAG non valido in %s linea %d: %s" +msgstr "Valore di FLAG non valido in %s riga %d: %s" #: ../spell.c:4655 #, c-format msgid "FLAG after using flags in %s line %d: %s" -msgstr "FLAG dopo l'uso di flags in %s linea %d: %s" +msgstr "FLAG dopo l'uso di flags in %s riga %d: %s" #: ../spell.c:4723 #, c-format @@ -5332,7 +5360,7 @@ msgid "" "%d" msgstr "" "Definire COMPOUNDFORBIDFLAG dopo l'elemento PFX potrebbe dare risultati " -"errati in %s linea %d" +"errati in %s riga %d" #: ../spell.c:4731 #, c-format @@ -5341,43 +5369,43 @@ msgid "" "%d" msgstr "" "Definire COMPOUNDPERMITFLAG dopo l'elemento PFX potrebbe dare risultati " -"errati in %s linea %d" +"errati in %s riga %d" #: ../spell.c:4747 #, c-format msgid "Wrong COMPOUNDRULES value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDRULES in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDRULES in %s riga %d: %s" #: ../spell.c:4771 #, c-format msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDWORDMAX in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDWORDMAX in %s riga %d: %s" #: ../spell.c:4777 #, c-format msgid "Wrong COMPOUNDMIN value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDMIN in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDMIN in %s riga %d: %s" #: ../spell.c:4783 #, c-format msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s" -msgstr "Valore errato per COMPOUNDSYLMAX in %s linea %d: %s" +msgstr "Valore errato per COMPOUNDSYLMAX in %s riga %d: %s" #: ../spell.c:4795 #, c-format msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s" -msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s linea %d: %s" +msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s riga %d: %s" #: ../spell.c:4847 #, c-format msgid "Different combining flag in continued affix block in %s line %d: %s" msgstr "" -"Flag combinazione diverso in blocco affissi continuo in %s linea %d: %s" +"Flag combinazione diverso in blocco affissi continuo in %s riga %d: %s" #: ../spell.c:4850 #, c-format msgid "Duplicate affix in %s line %d: %s" -msgstr "Affisso duplicato in %s linea %d: %s" +msgstr "Affisso duplicato in %s riga %d: %s" #: ../spell.c:4871 #, c-format @@ -5386,42 +5414,42 @@ msgid "" "line %d: %s" msgstr "" "Affisso usato anche per BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST " -"in %s linea %d: %s" +"in %s riga %d: %s" #: ../spell.c:4893 #, c-format msgid "Expected Y or N in %s line %d: %s" -msgstr "Y o N deve essere presente in %s linea %d: %s" +msgstr "Y o N deve essere presente in %s riga %d: %s" #: ../spell.c:4968 #, c-format msgid "Broken condition in %s line %d: %s" -msgstr "Condizione non rispettata in %s linea %d: %s" +msgstr "Condizione non rispettata in %s riga %d: %s" #: ../spell.c:5091 #, c-format msgid "Expected REP(SAL) count in %s line %d" -msgstr "Contatore REP(SAL) necessario in %s linea %d" +msgstr "Contatore REP(SAL) necessario in %s riga %d" #: ../spell.c:5120 #, c-format msgid "Expected MAP count in %s line %d" -msgstr "Contatore MAP necessario in %s linea %d" +msgstr "Contatore MAP necessario in %s riga %d" #: ../spell.c:5132 #, c-format msgid "Duplicate character in MAP in %s line %d" -msgstr "Carattere duplicato in MAP in %s linea %d" +msgstr "Carattere duplicato in MAP in %s riga %d" #: ../spell.c:5176 #, c-format msgid "Unrecognized or duplicate item in %s line %d: %s" -msgstr "Elemento non riconosciuto o duplicato in %s linea %d: %s" +msgstr "Elemento non riconosciuto o duplicato in %s riga %d: %s" #: ../spell.c:5197 #, c-format msgid "Missing FOL/LOW/UPP line in %s" -msgstr "Linea FOL/LOW/UPP mancante in %s" +msgstr "Riga FOL/LOW/UPP mancante in %s" #: ../spell.c:5220 msgid "COMPOUNDSYLMAX used without SYLLABLE" @@ -5442,22 +5470,22 @@ msgstr "Troppi suffissi e/o flag composti" #: ../spell.c:5250 #, c-format msgid "Missing SOFO%s line in %s" -msgstr "Linea SOFO%s mancante in %s" +msgstr "Riga SOFO%s mancante in %s" #: ../spell.c:5253 #, c-format msgid "Both SAL and SOFO lines in %s" -msgstr "Linee sia SAL che SOFO in %s" +msgstr "Riga sia SAL che SOFO in %s" #: ../spell.c:5331 #, c-format msgid "Flag is not a number in %s line %d: %s" -msgstr "Il flag non è un numero in %s linea %d: %s" +msgstr "Il flag non è un numero in %s riga %d: %s" #: ../spell.c:5334 #, c-format msgid "Illegal flag in %s line %d: %s" -msgstr "Flag non ammesso in %s linea %d: %s" +msgstr "Flag non ammesso in %s riga %d: %s" #: ../spell.c:5493 ../spell.c:5501 #, c-format @@ -5477,17 +5505,17 @@ msgstr "E760: Nessun contatore parole in %s" #: ../spell.c:5669 #, c-format msgid "line %6d, word %6d - %s" -msgstr "linea %6d, parola %6d - %s" +msgstr "riga %6d, parola %6d - %s" #: ../spell.c:5691 #, c-format msgid "Duplicate word in %s line %d: %s" -msgstr "Parola duplicata in %s linea %d: %s" +msgstr "Parola duplicata in %s riga %d: %s" #: ../spell.c:5694 #, c-format msgid "First duplicate word in %s line %d: %s" -msgstr "Prima parola duplicata in %s linea %d: %s" +msgstr "Prima parola duplicata in %s riga %d: %s" #: ../spell.c:5746 #, c-format @@ -5507,37 +5535,37 @@ msgstr "Lettura file parole %s ..." #: ../spell.c:6155 #, c-format msgid "Duplicate /encoding= line ignored in %s line %d: %s" -msgstr "Linea /encoding= duplicata ignorata in %s linea %d: %s" +msgstr "Riga /encoding= duplicata ignorata in %s riga %d: %s" #: ../spell.c:6159 #, c-format msgid "/encoding= line after word ignored in %s line %d: %s" -msgstr "Linea /encoding= dopo parola ignorata in %s linea %d: %s" +msgstr "Riga /encoding= dopo parola ignorata in %s riga %d: %s" #: ../spell.c:6180 #, c-format msgid "Duplicate /regions= line ignored in %s line %d: %s" -msgstr "Linea /regions= duplicata ignorata in %s linea %d: %s" +msgstr "Riga /regions= duplicata ignorata in %s riga %d: %s" #: ../spell.c:6185 #, c-format msgid "Too many regions in %s line %d: %s" -msgstr "Troppe regioni in %s linea %d: %s" +msgstr "Troppe regioni in %s riga %d: %s" #: ../spell.c:6198 #, c-format msgid "/ line ignored in %s line %d: %s" -msgstr "Linea / ignorata in %s linea %d: %s" +msgstr "Riga / ignorata in %s riga %d: %s" #: ../spell.c:6224 #, c-format msgid "Invalid region nr in %s line %d: %s" -msgstr "N. regione non valido in %s linea %d: %s" +msgstr "N. regione non valido in %s riga %d: %s" #: ../spell.c:6230 #, c-format msgid "Unrecognized flags in %s line %d: %s" -msgstr "Flag non riconosciuti in %s linea %d: %s" +msgstr "Flag non riconosciuti in %s riga %d: %s" #: ../spell.c:6257 #, c-format @@ -5594,7 +5622,7 @@ msgstr "E755: Regione non valida in %s" #: ../spell.c:7907 msgid "Warning: both compounding and NOBREAK specified" -msgstr "Attenzione: specificati sia composizione sia NOBREAK" +msgstr "Avviso: specificati sia composizione sia NOBREAK" #: ../spell.c:7920 #, c-format @@ -5611,14 +5639,12 @@ msgid "E765: 'spellfile' does not have %<PRId64> entries" msgstr "E765: 'spellfile' non ha %<PRId64> elementi" #: ../spell.c:8074 -#, fuzzy, c-format msgid "Word '%.*s' removed from %s" -msgstr "Parola rimossa da %s" +msgstr "Parola '%.*s' rimossa da %s" #: ../spell.c:8117 -#, fuzzy, c-format msgid "Word '%.*s' added to %s" -msgstr "Parola aggiunta a %s" +msgstr "Parola '%.*s' aggiunta a %s" #: ../spell.c:8381 msgid "E763: Word characters differ between spell files" @@ -5713,7 +5739,7 @@ msgstr "la sincronizzazione inizia " #: ../syntax.c:3443 ../syntax.c:3506 msgid " lines before top line" -msgstr " linee prima della linea iniziale" +msgstr " righe prima della riga iniziale" #: ../syntax.c:3448 msgid "" @@ -5758,7 +5784,7 @@ msgstr "; corrisp. " #: ../syntax.c:3515 msgid " line breaks" -msgstr " interruzioni di linea" +msgstr " interruzioni di riga" #: ../syntax.c:4076 msgid "E395: contains argument not accepted here" @@ -5822,7 +5848,7 @@ msgstr "E402: Spazzatura dopo espressione: %s" #: ../syntax.c:5120 msgid "E403: syntax sync: line continuations pattern specified twice" msgstr "" -"E403: syntax sync: espressione di continuazione linea specificata due volte" +"E403: syntax sync: espressione di continuazione riga specificata due volte" #: ../syntax.c:5169 #, c-format @@ -6011,7 +6037,7 @@ msgid "" " # TO tag FROM line in file/text" msgstr "" "\n" -" # A tag DA__ linea in file/testo" +" # A tag DA__ riga in file/testo" #: ../tag.c:1303 #, c-format @@ -6020,7 +6046,7 @@ msgstr "Ricerca nel tag file %s" #: ../tag.c:1545 msgid "Ignoring long line in tags file" -msgstr "Linea lunga ignorata nel tag file" +msgstr "Riga lunga ignorata nel tag file" #: ../tag.c:1915 #, c-format @@ -6100,9 +6126,8 @@ msgstr "Vim: Errore leggendo l'input, esco...\n" #. This happens when the FileChangedRO autocommand changes the #. * file in a way it becomes shorter. #: ../undo.c:379 -#, fuzzy msgid "E881: Line count changed unexpectedly" -msgstr "E834: Il conteggio delle righe è inaspettatamente cambiato" +msgstr "E881: Il conteggio delle righe è inaspettatamente cambiato" #: ../undo.c:627 #, c-format @@ -6191,23 +6216,23 @@ msgstr "E830: Undo numero %<PRId64> non trovato" #: ../undo.c:1979 msgid "E438: u_undo: line numbers wrong" -msgstr "E438: u_undo: numeri linee errati" +msgstr "E438: u_undo: numeri righe errati" #: ../undo.c:2183 msgid "more line" -msgstr "linea in più" +msgstr "riga in più" #: ../undo.c:2185 msgid "more lines" -msgstr "linee in più" +msgstr "righe in più" #: ../undo.c:2187 msgid "line less" -msgstr "linea in meno" +msgstr "riga in meno" #: ../undo.c:2189 msgid "fewer lines" -msgstr "linee in meno" +msgstr "righe in meno" #: ../undo.c:2193 msgid "change" @@ -6253,7 +6278,7 @@ msgstr "E439: lista 'undo' non valida" #: ../undo.c:2495 msgid "E440: undo line missing" -msgstr "E440: linea di 'undo' mancante" +msgstr "E440: riga di 'undo' mancante" #: ../version.c:600 msgid "" @@ -6453,6 +6478,10 @@ msgstr "E445: Altre finestre contengono modifiche" msgid "E446: No file name under cursor" msgstr "E446: Nessun nome file sotto il cursore" +#: ../window.c:5484 +msgid "List or number required" +msgstr "È necessaria una lista o un numero" + #~ msgid "E831: bf_key_init() called with empty password" #~ msgstr "E831: chiamata a bf_key_init() con password nulla" @@ -6538,15 +6567,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "Reading from stdin..." #~ msgstr "Leggo da 'stdin'..." -#~ msgid "[blowfish]" -#~ msgstr "[blowfish]" - -#~ msgid "[crypted]" -#~ msgstr "[cifrato]" - -#~ msgid "E821: File is encrypted with unknown method" -#~ msgstr "E821: File cifrato con metodo sconosciuto" - #~ msgid "NetBeans disallows writes of unmodified buffers" #~ msgstr "NetBeans non permette la scrittura di un buffer non modificato" @@ -6662,8 +6682,8 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "Vim: Received \"die\" request from session manager\n" #~ msgstr "Vim: Ricevuta richiesta \"die\" dal session manager\n" -#~ msgid "Close" -#~ msgstr "Chiusura" +#~ msgid "Close tab" +#~ msgstr "Chiudi linguetta" #~ msgid "New tab" #~ msgstr "Nuova linguetta" @@ -6719,9 +6739,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E672: Unable to open window inside MDI application" #~ msgstr "E672: Non posso aprire la finestra in un'applicazione MDI" -#~ msgid "Close tab" -#~ msgstr "Chiudi linguetta" - #~ msgid "Open tab..." #~ msgstr "Apri linguetta..." @@ -6844,13 +6861,13 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "non sono riuscito ad aprire il buffer" #~ msgid "cannot delete line" -#~ msgstr "non posso cancellare la linea" +#~ msgstr "non posso cancellare la riga" #~ msgid "cannot replace line" -#~ msgstr "non posso sostituire la linea" +#~ msgstr "non posso sostituire la riga" #~ msgid "cannot insert line" -#~ msgstr "non posso inserire la linea" +#~ msgstr "non posso inserire la riga" #~ msgid "string cannot contain newlines" #~ msgstr "la stringa non può contenere caratteri 'A CAPO'" @@ -6871,7 +6888,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "finestra non valida" #~ msgid "linenr out of range" -#~ msgstr "numero linea non nell'intervallo" +#~ msgstr "numero riga non nell'intervallo" #~ msgid "not allowed in the Vim sandbox" #~ msgstr "non ammesso in ambiente protetto" @@ -6879,9 +6896,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E836: This Vim cannot execute :python after using :py3" #~ msgstr "E836: Python: Impossibile usare :py e :py3 nella stessa sessione" -#~ msgid "E837: This Vim cannot execute :py3 after using :python" -#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python" - #~ msgid "" #~ "E263: Sorry, this command is disabled, the Python library could not be " #~ "loaded." @@ -6889,14 +6903,18 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ "E263: Spiacente, comando non disponibile, non riesco a caricare libreria " #~ "programmi Python." +#~ msgid "" +#~ "E887: Sorry, this command is disabled, the Python's site module could not be " +#~ "loaded." +#~ msgstr "" +#~ "E887: Spiacente, comando non disponibile, non riesco a caricare il modulo " +#~ "Python locale." + #~ msgid "E659: Cannot invoke Python recursively" #~ msgstr "E659: Python non può essere chiamato ricorsivamente" -#~ msgid "line number out of range" -#~ msgstr "numero linea non nell'intervallo" - -#~ msgid "invalid mark name" -#~ msgstr "nome di mark non valido" +#~ msgid "E837: This Vim cannot execute :py3 after using :python" +#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python" #~ msgid "E265: $_ must be an instance of String" #~ msgstr "E265: $_ deve essere un'istanza di String" @@ -7024,7 +7042,10 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "non ancora implementato" #~ msgid "cannot set line(s)" -#~ msgstr "non posso impostare linea(e)" +#~ msgstr "non posso impostare riga(he)" + +#~ msgid "invalid mark name" +#~ msgstr "nome di mark non valido" #~ msgid "mark not set" #~ msgstr "mark non impostato" @@ -7033,7 +7054,10 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "riga %d colonna %d" #~ msgid "cannot insert/append line" -#~ msgstr "non riesco a inserire/aggiungere linea" +#~ msgstr "non riesco a inserire/aggiungere riga" + +#~ msgid "line number out of range" +#~ msgstr "numero linea non nell'intervallo" #~ msgid "unknown flag: " #~ msgstr "opzione inesistente: " @@ -7081,7 +7105,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "E572: codice di uscita %d" #~ msgid "cannot get line" -#~ msgstr "non riesco a ottenere la linea" +#~ msgstr "non riesco a ottenere la riga" #~ msgid "Unable to register a command server name" #~ msgstr "Non riesco a registrare un nome di server comando" @@ -7307,7 +7331,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "E286: Apertura 'input method' fallita" #~ msgid "E287: Warning: Could not set destroy callback to IM" -#~ msgstr "E287: Attenzione: Non posso assegnare IM a 'destroy callback'" +#~ msgstr "E287: Avviso: Non posso assegnare IM a 'destroy callback'" #~ msgid "E288: input method doesn't support any style" #~ msgstr "E288: 'input method' non sopporta alcuno stile" @@ -7379,12 +7403,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E338: Sorry, no file browser in console mode" #~ msgstr "E338: Spiacente, niente esplorazione file in modalità console" -#~ msgid "Vim: preserving files...\n" -#~ msgstr "Vim: preservo file...\n" - -#~ msgid "Vim: Finished.\n" -#~ msgstr "Vim: Finito.\n" - #~ msgid "ERROR: " #~ msgstr "ERRORE: " @@ -7405,7 +7423,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ "\n" #~ msgid "E340: Line is becoming too long" -#~ msgstr "E340: La linea sta diventando troppo lunga" +#~ msgstr "E340: La riga sta diventando troppo lunga" #~ msgid "E341: Internal error: lalloc(%<PRId64>, )" #~ msgstr "E341: Errore interno: lalloc(%<PRId64>, )" @@ -7413,15 +7431,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E547: Illegal mouseshape" #~ msgstr "E547: Forma del mouse non valida" -#~ msgid "Enter encryption key: " -#~ msgstr "Immetti chiave di cifratura: " - -#~ msgid "Enter same key again: " -#~ msgstr "Ribatti per conferma la stessa chiave: " - -#~ msgid "Keys don't match!" -#~ msgstr "Le chiavi non corrispondono!" - #~ msgid "Cannot connect to Netbeans #2" #~ msgstr "Non posso connettermi a Netbeans #2" @@ -7451,7 +7460,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "E775: Funzionalità [eval] non disponibile" #~ msgid "freeing %<PRId64> lines" -#~ msgstr "libero %<PRId64> linee" +#~ msgstr "libero %<PRId64> righe" #~ msgid "E530: Cannot change term in GUI" #~ msgstr "E530: Non posso modificare 'term' mentre sono nella GUI" @@ -7552,15 +7561,6 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "E245: Illegal char '%c' in font name \"%s\"" #~ msgstr "E245: Carattere non ammesso '%c' nel font di nome \"%s\"" -#~ msgid "Vim: Double signal, exiting\n" -#~ msgstr "Vim: Segnale doppio, esco\n" - -#~ msgid "Vim: Caught deadly signal %s\n" -#~ msgstr "Vim: Intercettato segnale fatale %s\n" - -#~ msgid "Vim: Caught deadly signal\n" -#~ msgstr "Vim: Intercettato segnale fatale\n" - #~ msgid "Opening the X display took %<PRId64> msec" #~ msgstr "Attivazione visualizzazione X ha richiesto %<PRId64> msec" @@ -7624,7 +7624,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr "XSMP SmcOpenConnection fallita: %s" #~ msgid "At line" -#~ msgstr "Alla linea" +#~ msgstr "Alla riga" #~ msgid "Could not load vim32.dll!" #~ msgstr "Non riesco a caricare vim32.dll!" @@ -7889,7 +7889,7 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgstr " modo Vim predefinito " #~ msgid "WARNING: Windows 95/98/ME detected" -#~ msgstr "ATTENZIONE: Trovato Windows 95/98/ME" +#~ msgstr "AVVISO: Trovato Windows 95/98/ME" #~ msgid "type :help windows95<Enter> for info on this" #~ msgstr "batti :help windows95<Enter> per info al riguardo" @@ -7976,6 +7976,22 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "Need encryption key for \"%s\"" #~ msgstr "Serve una chiave di cifratura per \"%s\"" +#~ msgid "empty keys are not allowed" +#~ msgstr "chiavi nulle non consentite" + +#~ msgid "dictionary is locked" +#~ msgstr "il dizionario è bloccato" + +#~ msgid "list is locked" +#~ msgstr "la lista è bloccata" + +#~ msgid "failed to add key '%s' to dictionary" +#~ msgstr "non non riusciato ad aggiungere la chiave '%s' al dizionario" + +#~ #, c-format +#~ msgid "index must be int or slice, not %s" +#~ msgstr "l'indice deve'essere un intero o un intervallo, non %s" + #~ msgid "expected str() or unicode() instance, but got %s" #~ msgstr "attesa istanza di str() o unicode(), trovato invece %s" @@ -7999,8 +8015,8 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "value is too small to fit into C int type" #~ msgstr "valore troppo piccolo per il tipo int del C" -#~ msgid "number must be greater then zero" -#~ msgstr "il numero dev'essere maggiore di zero" +#~ msgid "number must be greater than zero" +#~ msgstr "il numero deve essere maggiore di zero" #~ msgid "number must be greater or equal to zero" #~ msgstr "il numero dev'essere maggiore o uguale a zero" @@ -8019,7 +8035,8 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "expected 3-tuple as imp.find_module() result, but got %s" #~ msgstr "" -#~ "atteso terzetto come risultato di imp.find_module(), trovato invece %s" +#~ "atteso terzetto come risultato di imp.find_module(), trovato invece tuple di " +#~ "dimens. %d" #~ msgid "" #~ "expected 3-tuple as imp.find_module() result, but got tuple of size %d" @@ -8056,15 +8073,30 @@ msgstr "E446: Nessun nome file sotto il cursore" #~ msgid "internal error: failed to get vim list item %d" #~ msgstr "errore interno: non ho potuto ottenere l'elemento di vim list %d" -#~ msgid "failed to add item to list" -#~ msgstr "non ho potuto aggiungere un elemento alla lista" +#~ msgid "slice step cannot be zero" +#~ msgstr "il passo scorrendo un intervallo non può essere zero" + +#~ #, c-format +#~ msgid "attempt to assign sequence of size greater than %d to extended slice" +#~ msgstr "tentativo di assegnare una sequenza maggiore di %d a un intervallo " +#~ "esteso" #~ msgid "internal error: no vim list item %d" #~ msgstr "errore interno: non c'è un elemento di vim list %d" +#~ msgid "internal error: not enough list items" +#~ msgstr "errore interno: non ci sono abbastanza elementi per la lista" + #~ msgid "internal error: failed to add item to list" #~ msgstr "errore interno: non ho potuto aggiungere un elemento alla lista" +#~ msgid "attempt to assign sequence of size %d to extended slice of size %d" +#~ msgstr "tentativo di assegnare sequenza di dimensione %d a un intervallo " +#~ " esteso di dimensione %d" + +#~ msgid "failed to add item to list" +#~ msgstr "non ho potuto aggiungere un elemento alla lista" + #~ msgid "cannot delete vim.List attributes" #~ msgstr "non riesco a cancellare gli attributi vim.List" diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 001740943f..5ad621e666 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -545,7 +545,11 @@ static int pum_set_selected(int n, int repeat) g_do_tagpreview = (int)p_pvh; } RedrawingDisabled++; + // Prevent undo sync here, if an autocommand syncs undo weird + // things can happen to the undo tree. + no_u_sync++; resized = prepare_tagpreview(false); + no_u_sync--; RedrawingDisabled--; g_do_tagpreview = 0; @@ -629,7 +633,9 @@ static int pum_set_selected(int n, int repeat) // the window when needed, otherwise it will always be // redraw. if (resized) { + no_u_sync++; win_enter(curwin_save, true); + no_u_sync--; update_topline(); } @@ -640,7 +646,9 @@ static int pum_set_selected(int n, int repeat) pum_do_redraw = FALSE; if (!resized && win_valid(curwin_save)) { + no_u_sync++; win_enter(curwin_save, true); + no_u_sync--; } // May need to update the screen again when there are diff --git a/src/nvim/pos.h b/src/nvim/pos.h index 7071df51e8..864f3fe866 100644 --- a/src/nvim/pos.h +++ b/src/nvim/pos.h @@ -2,7 +2,11 @@ #define NVIM_POS_H typedef long linenr_T; // line number type -typedef int colnr_T; // column number type + +/// Column number type +typedef int colnr_T; +/// Format used to print values which have colnr_T type +#define PRIdCOLNR "d" #define MAXLNUM 0x7fffffff // maximum (invalid) line number #define MAXCOL 0x7fffffff // maximum column number, 31 bits diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 8e6ae46a3b..17cb8a86aa 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -39,7 +39,6 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/ui.h" -#include "nvim/tempfile.h" #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -200,9 +199,8 @@ qf_init_ext ( char_u *pattern; char_u *fmtstr = NULL; int col = 0; - char_u use_viscol = FALSE; - int type = 0; - int valid; + bool use_viscol = false; + char_u type = 0; linenr_T buflnum = lnumfirst; long lnum = 0L; int enr = 0; @@ -220,16 +218,16 @@ qf_init_ext ( int i; int round; int idx = 0; - int multiline = FALSE; - int multiignore = FALSE; - int multiscan = FALSE; - int retval = -1; /* default: return error flag */ - char_u *directory = NULL; - char_u *currfile = NULL; - char_u *tail = NULL; - char_u *p_str = NULL; - listitem_T *p_li = NULL; - struct dir_stack_T *file_stack = NULL; + bool multiline = false; + bool multiignore = false; + bool multiscan = false; + int retval = -1; // default: return error flag + char_u *directory = NULL; + char_u *currfile = NULL; + char_u *tail = NULL; + char_u *p_str = NULL; + listitem_T *p_li = NULL; + struct dir_stack_T *file_stack = NULL; regmatch_T regmatch; static struct fmtpattern { char_u convchar; @@ -278,15 +276,16 @@ qf_init_ext ( /* * Get some space to modify the format string into. */ - i = 3 * FMT_PATTERNS + 4 * (int)STRLEN(efm); - for (round = FMT_PATTERNS; round > 0; ) - i += (int)STRLEN(fmt_pat[--round].pattern); + size_t fmtstr_size = 3 * FMT_PATTERNS + 4 * STRLEN(efm); + for (round = FMT_PATTERNS; round > 0; ) { + fmtstr_size += STRLEN(fmt_pat[--round].pattern); + } #ifdef COLON_IN_FILENAME - i += 12; /* "%f" can become twelve chars longer */ + fmtstr_size += 12; // "%f" can become twelve chars longer #else - i += 2; /* "%f" can become two chars longer */ + fmtstr_size += 2; // "%f" can become two chars longer #endif - fmtstr = xmalloc(i); + fmtstr = xmalloc(fmtstr_size); while (efm[0] != NUL) { /* @@ -530,11 +529,9 @@ qf_init_ext ( fmt_start = NULL; } - /* - * Try to match each part of 'errorformat' until we find a complete - * match or no match. - */ - valid = TRUE; + // Try to match each part of 'errorformat' until we find a complete + // match or no match. + bool valid = true; restofline: for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) { idx = fmt_ptr->prefix; @@ -546,7 +543,7 @@ restofline: errmsg[0] = NUL; lnum = 0; col = 0; - use_viscol = FALSE; + use_viscol = false; enr = -1; type = 0; tail = NULL; @@ -555,25 +552,23 @@ restofline: int r = vim_regexec(®match, IObuff, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; if (r) { - if ((idx == 'C' || idx == 'Z') && !multiline) + if ((idx == 'C' || idx == 'Z') && !multiline) { continue; - if (vim_strchr((char_u *)"EWI", idx) != NULL) - type = idx; - else + } + if (vim_strchr((char_u *)"EWI", idx) != NULL) { + type = (char_u)idx; + } else { type = 0; - /* - * Extract error message data from matched line. - * We check for an actual submatch, because "\[" and "\]" in - * the 'errorformat' may cause the wrong submatch to be used. - */ - if ((i = (int)fmt_ptr->addr[0]) > 0) { /* %f */ - int c; - - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + } + // Extract error message data from matched line. + // We check for an actual submatch, because "\[" and "\]" in + // the 'errorformat' may cause the wrong submatch to be used. + if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) { continue; - - /* Expand ~/file and $HOME/file to full path. */ - c = *regmatch.endp[i]; + } + // Expand ~/file and $HOME/file to full path. + char_u c = *regmatch.endp[i]; *regmatch.endp[i] = NUL; expand_env(regmatch.startp[i], namebuf, CMDBUFFSIZE); *regmatch.endp[i] = c; @@ -629,14 +624,14 @@ restofline: col -= col % 8; } } - ++col; - use_viscol = TRUE; + col++; + use_viscol = true; } if ((i = (int)fmt_ptr->addr[8]) > 0) { /* %v */ if (regmatch.startp[i] == NULL) continue; col = (int)atol((char *)regmatch.startp[i]); - use_viscol = TRUE; + use_viscol = true; } if ((i = (int)fmt_ptr->addr[9]) > 0) { /* %s */ if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) @@ -653,7 +648,7 @@ restofline: break; } } - multiscan = FALSE; + multiscan = false; if (fmt_ptr == NULL || idx == 'D' || idx == 'X') { if (fmt_ptr != NULL) { @@ -667,20 +662,21 @@ restofline: } else if (idx == 'X') /* leave directory */ directory = qf_pop_dir(&dir_stack); } - namebuf[0] = NUL; /* no match found, remove file name */ - lnum = 0; /* don't jump to this line */ - valid = FALSE; - STRCPY(errmsg, IObuff); /* copy whole line to error message */ - if (fmt_ptr == NULL) - multiline = multiignore = FALSE; + namebuf[0] = NUL; // no match found, remove file name + lnum = 0; // don't jump to this line + valid = false; + STRCPY(errmsg, IObuff); // copy whole line to error message + if (fmt_ptr == NULL) { + multiline = multiignore = false; + } } else if (fmt_ptr != NULL) { /* honor %> item */ if (fmt_ptr->conthere) fmt_start = fmt_ptr; if (vim_strchr((char_u *)"AEWI", idx) != NULL) { - multiline = TRUE; /* start of a multi-line message */ - multiignore = FALSE; /* reset continuation */ + multiline = true; // start of a multi-line message + multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { /* continuation of multi-line msg */ if (qfprev == NULL) @@ -702,15 +698,17 @@ restofline: qfprev->qf_viscol = use_viscol; if (!qfprev->qf_fnum) qfprev->qf_fnum = qf_get_fnum(directory, - *namebuf || directory ? namebuf - : currfile && valid ? currfile : 0); - if (idx == 'Z') - multiline = multiignore = FALSE; + *namebuf + || directory ? namebuf : currfile + && valid ? currfile : 0); + if (idx == 'Z') { + multiline = multiignore = false; + } line_breakcheck(); continue; } else if (vim_strchr((char_u *)"OPQ", idx) != NULL) { - /* global file names */ - valid = FALSE; + // global file names + valid = false; if (*namebuf == NUL || os_file_exists(namebuf)) { if (*namebuf && idx == 'P') currfile = qf_push_dir(namebuf, &file_stack); @@ -719,14 +717,15 @@ restofline: *namebuf = NUL; if (tail && *tail) { STRMOVE(IObuff, skipwhite(tail)); - multiscan = TRUE; + multiscan = true; goto restofline; } } } - if (fmt_ptr->flags == '-') { /* generally exclude this line */ - if (multiline) - multiignore = TRUE; /* also exclude continuation lines */ + if (fmt_ptr->flags == '-') { // generally exclude this line + if (multiline) { + multiignore = true; // also exclude continuation lines + } continue; } } @@ -867,26 +866,27 @@ void qf_free_all(win_T *wp) qf_free(qi, i); } -/* - * Add an entry to the end of the list of errors. - * Returns OK or FAIL. - */ -static int -qf_add_entry ( - qf_info_T *qi, /* quickfix list */ - qfline_T **prevp, /* nonnull pointer (to previously added entry or NULL) */ - char_u *dir, /* optional directory name */ - char_u *fname, /* file name or NULL */ - int bufnum, /* buffer number or zero */ - char_u *mesg, /* message */ - long lnum, /* line number */ - int col, /* column */ - int vis_col, /* using visual column */ - char_u *pattern, /* search pattern */ - int nr, /* error number */ - int type, /* type character */ - int valid /* valid entry */ -) +/// Add an entry to the end of the list of errors. +/// +/// @param qi quickfix list +/// @param prevp nonnull pointer (to previously added entry or NULL) +/// @param dir optional directory name +/// @param fname file name or NULL +/// @param bufnum buffer number or zero +/// @param mesg message +/// @param lnum line number +/// @param col column +/// @param vis_col using visual column +/// @param pattern search pattern +/// @param nr error number +/// @param type type character +/// @param valid valid entry +/// +/// @returns OK or FAIL. +static int qf_add_entry(qf_info_T *qi, qfline_T **prevp, char_u *dir, + char_u *fname, int bufnum, char_u *mesg, long lnum, + int col, char_u vis_col, char_u *pattern, int nr, + char_u type, char_u valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); @@ -1202,7 +1202,7 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr) /* * Check in which directory of the directory stack the given file can be * found. - * Returns a pointer to the directory name or NULL if not found + * Returns a pointer to the directory name or NULL if not found. * Cleans up intermediate directory entries. * * TODO: How to solve the following problem? @@ -1606,13 +1606,12 @@ win_found: } if (qf_ptr->qf_col > 0) { curwin->w_cursor.col = qf_ptr->qf_col - 1; - if (qf_ptr->qf_viscol == TRUE) { - /* - * Check each character from the beginning of the error - * line up to the error column. For each tab character - * found, reduce the error column value by the length of - * a tab character. - */ + curwin->w_cursor.coladd = 0; + if (qf_ptr->qf_viscol == true) { + // Check each character from the beginning of the error + // line up to the error column. For each tab character + // found, reduce the error column value by the length of + // a tab character. line = get_cursor_line_ptr(); screen_col = 0; for (char_col = 0; char_col < curwin->w_cursor.col; ++char_col) { @@ -1658,12 +1657,13 @@ win_found: * flag is present in 'shortmess'; But when not jumping, print the * whole message. */ i = msg_scroll; - if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) - msg_scroll = TRUE; - else if (!msg_scrolled && shortmess(SHM_OVERALL)) - msg_scroll = FALSE; - msg_attr_keep(IObuff, 0, TRUE); - msg_scroll = i; + if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) { + msg_scroll = true; + } else if (!msg_scrolled && shortmess(SHM_OVERALL)) { + msg_scroll = false; + } + msg_attr_keep(IObuff, 0, true); + msg_scroll = (int)i; } } else { if (opened_window) @@ -1828,10 +1828,12 @@ void qf_age(exarg_T *eap) } } - if (eap->addr_count != 0) - count = eap->line2; - else + if (eap->addr_count != 0) { + assert(eap->line2 <= INT_MAX); + count = (int)eap->line2; + } else { count = 1; + } while (count--) { if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder) { if (qi->qf_curlist == 0) { @@ -1883,6 +1885,8 @@ static void qf_free(qf_info_T *qi, int idx) --qi->qf_lists[idx].qf_count; } xfree(qi->qf_lists[idx].qf_title); + qi->qf_lists[idx].qf_start = NULL; + qi->qf_lists[idx].qf_ptr = NULL; qi->qf_lists[idx].qf_title = NULL; qi->qf_lists[idx].qf_index = 0; } @@ -1949,7 +1953,7 @@ static char_u *qf_types(int c, int nr) p = (char_u *)""; else { cc[0] = ' '; - cc[1] = c; + cc[1] = (char_u)c; cc[2] = NUL; p = cc; } @@ -2037,12 +2041,13 @@ void ex_copen(exarg_T *eap) } } - if (eap->addr_count != 0) - height = eap->line2; - else + if (eap->addr_count != 0) { + assert(eap->line2 <= INT_MAX); + height = (int)eap->line2; + } else { height = QF_WINHEIGHT; - - reset_VIsual_and_resel(); /* stop Visual mode */ + } + reset_VIsual_and_resel(); // stop Visual mode /* * Find existing quickfix window, or open a new one. @@ -2117,14 +2122,10 @@ void ex_copen(exarg_T *eap) prevwin = win; } - /* - * Fill the buffer with the quickfix list. - */ - qf_fill_buffer(qi); + qf_set_title_var(qi); - if (qi->qf_lists[qi->qf_curlist].qf_title != NULL) { - qf_set_title_var(qi); - } + // Fill the buffer with the quickfix list. + qf_fill_buffer(qi); curwin->w_cursor.lnum = qi->qf_lists[qi->qf_curlist].qf_index; curwin->w_cursor.col = 0; @@ -2256,16 +2257,13 @@ static void qf_update_buffer(qf_info_T *qi) /* set curwin/curbuf to buf and save a few things */ aucmd_prepbuf(&aco, buf); - qf_fill_buffer(qi); - - if (qi->qf_lists[qi->qf_curlist].qf_title != NULL - && (win = qf_find_win(qi)) != NULL) { + if ((win = qf_find_win(qi)) != NULL) { curwin_save = curwin; curwin = win; qf_set_title_var(qi); curwin = curwin_save; - } + qf_fill_buffer(qi); /* restore curwin/curbuf and a few other things */ aucmd_restbuf(&aco); @@ -2274,10 +2272,13 @@ static void qf_update_buffer(qf_info_T *qi) } } +// Set "w:quickfix_title" if "qi" has a title. static void qf_set_title_var(qf_info_T *qi) { - set_internal_string_var((char_u *)"w:quickfix_title", - qi->qf_lists[qi->qf_curlist].qf_title); + if (qi->qf_lists[qi->qf_curlist].qf_title != NULL) { + set_internal_string_var((char_u *)"w:quickfix_title", + qi->qf_lists[qi->qf_curlist].qf_title); + } } /* @@ -2304,13 +2305,15 @@ static void qf_fill_buffer(qf_info_T *qi) if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { - if (qfp->qf_type == 1) /* :helpgrep */ - STRCPY(IObuff, path_tail(errbuf->b_fname)); - else - STRCPY(IObuff, errbuf->b_fname); + if (qfp->qf_type == 1) { // :helpgrep + STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff)); + } else { + STRLCPY(IObuff, errbuf->b_fname, sizeof(IObuff)); + } len = (int)STRLEN(IObuff); - } else + } else { len = 0; + } IObuff[len++] = '|'; if (qfp->qf_lnum > 0) { @@ -2436,8 +2439,6 @@ int grep_internal(cmdidx_T cmdidx) void ex_make(exarg_T *eap) { char_u *fname; - char_u *cmd; - unsigned len; win_T *wp = NULL; qf_info_T *qi = &ql_info; int res; @@ -2475,29 +2476,28 @@ void ex_make(exarg_T *eap) return; os_remove((char *)fname); // in case it's not unique - /* - * If 'shellpipe' empty: don't redirect to 'errorfile'. - */ - len = (unsigned)STRLEN(p_shq) * 2 + (unsigned)STRLEN(eap->arg) + 1; - if (*p_sp != NUL) - len += (unsigned)STRLEN(p_sp) + (unsigned)STRLEN(fname) + 3; - cmd = xmalloc(len); - sprintf((char *)cmd, "%s%s%s", (char *)p_shq, (char *)eap->arg, - (char *)p_shq); - if (*p_sp != NUL) - append_redir(cmd, len, p_sp, fname); - /* - * Output a newline if there's something else than the :make command that - * was typed (in which case the cursor is in column 0). - */ - if (msg_col == 0) - msg_didout = FALSE; + // If 'shellpipe' empty: don't redirect to 'errorfile'. + const size_t len = (STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1 + + (*p_sp == NUL + ? 0 + : STRLEN(p_sp) + STRLEN(fname) + 3)); + char *const cmd = xmalloc(len); + snprintf(cmd, len, "%s%s%s", (char *)p_shq, (char *)eap->arg, + (char *)p_shq); + if (*p_sp != NUL) { + append_redir(cmd, len, (char *) p_sp, (char *) fname); + } + // Output a newline if there's something else than the :make command that + // was typed (in which case the cursor is in column 0). + if (msg_col == 0) { + msg_didout = false; + } msg_start(); MSG_PUTS(":!"); - msg_outtrans(cmd); /* show what we are doing */ + msg_outtrans((char_u *) cmd); // show what we are doing - /* let the shell know if we are redirecting output or not */ - do_shell(cmd, *p_sp != NUL ? kShellOptDoOut : 0); + // let the shell know if we are redirecting output or not + do_shell((char_u *) cmd, *p_sp != NUL ? kShellOptDoOut : 0); res = qf_init(wp, fname, (eap->cmdidx != CMD_make @@ -2551,11 +2551,11 @@ static char_u *get_mef_name(void) /* Keep trying until the name doesn't exist yet. */ for (;; ) { - if (start == -1) - start = os_get_pid(); - else + if (start == -1) { + start = (int)os_get_pid(); + } else { off += 19; - + } name = xmalloc(STRLEN(p_mef) + 30); STRCPY(name, p_mef); sprintf((char *)name + (p - p_mef), "%d%d", start, off); @@ -2571,9 +2571,159 @@ static char_u *get_mef_name(void) return name; } +/// Returns the number of valid entries in the current quickfix/location list. +size_t qf_get_size(exarg_T *eap) + FUNC_ATTR_NONNULL_ALL +{ + qf_info_T *qi = &ql_info; + if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) { + // Location list. + qi = GET_LOC_LIST(curwin); + if (qi == NULL) { + return 0; + } + } + + int prev_fnum = 0; + size_t sz = 0; + qfline_T *qfp; + size_t i; + assert(qi->qf_lists[qi->qf_curlist].qf_count >= 0); + for (i = 0, qfp = qi->qf_lists[qi->qf_curlist].qf_start; + i < (size_t)qi->qf_lists[qi->qf_curlist].qf_count && qfp != NULL; + i++, qfp = qfp->qf_next) { + if (!qfp->qf_valid) { + continue; + } + + if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo) { + // Count all valid entries. + sz++; + } else if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) { + // Count the number of files. + sz++; + prev_fnum = qfp->qf_fnum; + } + } + + return sz; +} + +/// Returns the current index of the quickfix/location list. +/// Returns 0 if there is an error. +size_t qf_get_cur_idx(exarg_T *eap) + FUNC_ATTR_NONNULL_ALL +{ + qf_info_T *qi = &ql_info; + + if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) { + // Location list. + qi = GET_LOC_LIST(curwin); + if (qi == NULL) { + return 0; + } + } + + assert(qi->qf_lists[qi->qf_curlist].qf_index >= 0); + return (size_t)qi->qf_lists[qi->qf_curlist].qf_index; +} + +/// Returns the current index in the quickfix/location list, +/// counting only valid entries. +/// Returns 1 if there are no valid entries. +int qf_get_cur_valid_idx(exarg_T *eap) + FUNC_ATTR_NONNULL_ALL +{ + qf_info_T *qi = &ql_info; + + if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) { + // Location list. + qi = GET_LOC_LIST(curwin); + if (qi == NULL) { + return 1; + } + } + + qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist]; + + // Check if the list has valid errors. + if (qfl->qf_count <= 0 || qfl->qf_nonevalid) { + return 1; + } + + int prev_fnum = 0; + int eidx = 0; + qfline_T *qfp; + size_t i; + assert(qfl->qf_index >= 0); + for (i = 1, qfp = qfl->qf_start; + i <= (size_t)qfl->qf_index && qfp != NULL; + i++, qfp = qfp->qf_next) { + if (!qfp->qf_valid) { + continue; + } + + if (eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { + if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) { + // Count the number of files. + eidx++; + prev_fnum = qfp->qf_fnum; + } + } else { + eidx++; + } + } + + return eidx != 0 ? eidx : 1; +} + +/// Get the 'n'th valid error entry in the quickfix or location list. +/// +/// Used by :cdo, :ldo, :cfdo and :lfdo commands. +/// For :cdo and :ldo, returns the 'n'th valid error entry. +/// For :cfdo and :lfdo, returns the 'n'th valid file entry. +static size_t qf_get_nth_valid_entry(qf_info_T *qi, size_t n, bool fdo) + FUNC_ATTR_NONNULL_ALL +{ + qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist]; + + // Check if the list has valid errors. + if (qfl->qf_count <= 0 || qfl->qf_nonevalid) { + return 1; + } + + int prev_fnum = 0; + size_t eidx = 0; + size_t i; + qfline_T *qfp; + assert(qfl->qf_count >= 0); + for (i = 1, qfp = qfl->qf_start; + i <= (size_t)qfl->qf_count && qfp != NULL; + i++, qfp = qfp->qf_next) { + if (qfp->qf_valid) { + if (fdo) { + if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) { + // Count the number of files. + eidx++; + prev_fnum = qfp->qf_fnum; + } + } else { + eidx++; + } + } + + if (eidx == n) { + break; + } + } + + return i <= (size_t)qfl->qf_count ? i : 1; +} + /* * ":cc", ":crewind", ":cfirst" and ":clast". * ":ll", ":lrewind", ":lfirst" and ":llast". + * ":cdo", ":ldo", ":cfdo" and ":lfdo". */ void ex_cc(exarg_T *eap) { @@ -2582,7 +2732,10 @@ void ex_cc(exarg_T *eap) if (eap->cmdidx == CMD_ll || eap->cmdidx == CMD_lrewind || eap->cmdidx == CMD_lfirst - || eap->cmdidx == CMD_llast) { + || eap->cmdidx == CMD_llast + || eap->cmdidx == CMD_llast + || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_lfdo) { qi = GET_LOC_LIST(curwin); if (qi == NULL) { EMSG(_(e_loclist)); @@ -2590,21 +2743,42 @@ void ex_cc(exarg_T *eap) } } - qf_jump(qi, 0, - eap->addr_count > 0 - ? (int)eap->line2 - : (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) - ? 0 - : (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind - || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst) - ? 1 - : 32767, - eap->forceit); + int errornr; + if (eap->addr_count > 0) { + errornr = (int)eap->line2; + } else if (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) { + errornr = 0; + } else if (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind + || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst) { + errornr = 1; + } else { + errornr = 32767; + } + + // For cdo and ldo commands, jump to the nth valid error. + // For cfdo and lfdo commands, jump to the nth valid file entry. + if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { + size_t n; + if (eap->addr_count > 0) { + assert(eap->line1 >= 0); + n = (size_t)eap->line1; + } else { + n = 1; + } + size_t valid_entry = qf_get_nth_valid_entry(qi, n, + eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo); + assert(valid_entry <= INT_MAX); + errornr = (int)valid_entry; + } + + qf_jump(qi, 0, errornr, eap->forceit); } /* * ":cnext", ":cnfile", ":cNext" and ":cprevious". * ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile". + * ":cdo", ":ldo", ":cfdo" and ":lfdo". */ void ex_cnext(exarg_T *eap) { @@ -2615,7 +2789,10 @@ void ex_cnext(exarg_T *eap) || eap->cmdidx == CMD_lprevious || eap->cmdidx == CMD_lnfile || eap->cmdidx == CMD_lNfile - || eap->cmdidx == CMD_lpfile) { + || eap->cmdidx == CMD_lpfile + || eap->cmdidx == CMD_lpfile + || eap->cmdidx == CMD_ldo + || eap->cmdidx == CMD_lfdo) { qi = GET_LOC_LIST(curwin); if (qi == NULL) { EMSG(_(e_loclist)); @@ -2623,15 +2800,26 @@ void ex_cnext(exarg_T *eap) } } - qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext) + int errornr; + if (eap->addr_count > 0 + && (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo + && eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) { + errornr = (int)eap->line2; + } else { + errornr = 1; + } + + qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext + || eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo) ? FORWARD - : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile) + : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile + || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) ? FORWARD_FILE : (eap->cmdidx == CMD_cpfile || eap->cmdidx == CMD_lpfile || eap->cmdidx == CMD_cNfile || eap->cmdidx == CMD_lNfile) ? BACKWARD_FILE : BACKWARD, - eap->addr_count > 0 ? (int)eap->line2 : 1, eap->forceit); + errornr, eap->forceit); } /* @@ -2714,7 +2902,7 @@ void ex_vimgrep(exarg_T *eap) int found_match; buf_T *first_match_buf = NULL; time_t seconds = 0; - int save_mls; + long save_mls; char_u *save_ei = NULL; aco_save_T aco; int flags = 0; @@ -2784,16 +2972,18 @@ void ex_vimgrep(exarg_T *eap) goto theend; } - if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd && - eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd) - || qi->qf_curlist == qi->qf_listcount) - /* make place for a new list */ + if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd + && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd) + || qi->qf_curlist == qi->qf_listcount) { + // make place for a new list qf_new_list(qi, *eap->cmdlinep); - else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) - /* Adding to existing list, find last entry. */ + } else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { + // Adding to existing list, find last entry. for (prevp = qi->qf_lists[qi->qf_curlist].qf_start; - prevp->qf_next != prevp; prevp = prevp->qf_next) - ; + prevp->qf_next != prevp; + prevp = prevp->qf_next) { + } + } /* parse the list of arguments */ if (get_arglist_exp(p, &fcount, &fnames, true) == FAIL) @@ -3271,18 +3461,12 @@ int get_errorlist(win_T *wp, list_T *list) */ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) { - listitem_T *li; - dict_T *d; - char_u *filename, *pattern, *text, *type; - int bufnum; - long lnum; - int col, nr; - int vcol; - qfline_T *prevp = NULL; - int valid, status; + listitem_T *li; + dict_T *d; + qfline_T *prevp = NULL; int retval = OK; - qf_info_T *qi = &ql_info; - int did_bufnr_emsg = FALSE; + qf_info_T *qi = &ql_info; + bool did_bufnr_emsg = false; if (wp != NULL) { qi = ll_get_or_alloc_list(wp); @@ -3309,21 +3493,22 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) if (d == NULL) continue; - filename = get_dict_string(d, (char_u *)"filename", TRUE); - bufnum = get_dict_number(d, (char_u *)"bufnr"); - lnum = get_dict_number(d, (char_u *)"lnum"); - col = get_dict_number(d, (char_u *)"col"); - vcol = get_dict_number(d, (char_u *)"vcol"); - nr = get_dict_number(d, (char_u *)"nr"); - type = get_dict_string(d, (char_u *)"type", TRUE); - pattern = get_dict_string(d, (char_u *)"pattern", TRUE); - text = get_dict_string(d, (char_u *)"text", TRUE); - if (text == NULL) + char_u *filename = get_dict_string(d, (char_u *)"filename", true); + int bufnum = (int)get_dict_number(d, (char_u *)"bufnr"); + long lnum = get_dict_number(d, (char_u *)"lnum"); + int col = (int)get_dict_number(d, (char_u *)"col"); + char_u vcol = (char_u)get_dict_number(d, (char_u *)"vcol"); + int nr = (int)get_dict_number(d, (char_u *)"nr"); + char_u *type = get_dict_string(d, (char_u *)"type", true); + char_u *pattern = get_dict_string(d, (char_u *)"pattern", true); + char_u *text = get_dict_string(d, (char_u *)"text", true); + if (text == NULL) { text = vim_strsave((char_u *)""); - - valid = TRUE; - if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) - valid = FALSE; + } + bool valid = true; + if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { + valid = false; + } /* Mark entries with non-existing buffer number as not valid. Give the * error message only once. */ @@ -3332,22 +3517,23 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) did_bufnr_emsg = TRUE; EMSGN(_("E92: Buffer %" PRId64 " not found"), bufnum); } - valid = FALSE; + valid = false; bufnum = 0; } - status = qf_add_entry(qi, &prevp, - NULL, /* dir */ - filename, - bufnum, - text, - lnum, - col, - vcol, /* vis_col */ - pattern, /* search pattern */ - nr, - type == NULL ? NUL : *type, - valid); + int status = qf_add_entry(qi, + &prevp, + NULL, // dir + filename, + bufnum, + text, + lnum, + col, + vcol, // vis_col + pattern, // search pattern + nr, + (char_u)(type == NULL ? NUL : *type), + valid); xfree(filename); xfree(pattern); diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index e2c4b590d0..886a48e7c5 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -796,13 +796,14 @@ static void reg_equi_class(int c) if (enc_utf8 || STRCMP(p_enc, "latin1") == 0 || STRCMP(p_enc, "iso-8859-15") == 0) { switch (c) { - case 'A': case '\300': case '\301': case '\302': + // Do not use '\300' style, it results in a negative number. + case 'A': case 0xc0: case 0xc1: case 0xc2: + case 0xc3: case 0xc4: case 0xc5: CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104) CASEMBC(0x1cd) CASEMBC(0x1de) CASEMBC(0x1e0) CASEMBC(0x1ea2) - case '\303': case '\304': case '\305': - regmbc('A'); regmbc('\300'); regmbc('\301'); - regmbc('\302'); regmbc('\303'); regmbc('\304'); - regmbc('\305'); + regmbc('A'); regmbc(0xc0); regmbc(0xc1); + regmbc(0xc2); regmbc(0xc3); regmbc(0xc4); + regmbc(0xc5); REGMBC(0x100) REGMBC(0x102) REGMBC(0x104) REGMBC(0x1cd) REGMBC(0x1de) REGMBC(0x1e0) REGMBC(0x1ea2) @@ -810,9 +811,9 @@ static void reg_equi_class(int c) case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06) regmbc('B'); REGMBC(0x1e02) REGMBC(0x1e06) return; - case 'C': case '\307': + case 'C': case 0xc7: CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a) CASEMBC(0x10c) - regmbc('C'); regmbc('\307'); + regmbc('C'); regmbc(0xc7); REGMBC(0x106) REGMBC(0x108) REGMBC(0x10a) REGMBC(0x10c) return; @@ -821,11 +822,11 @@ static void reg_equi_class(int c) regmbc('D'); REGMBC(0x10e) REGMBC(0x110) REGMBC(0x1e0a) REGMBC(0x1e0e) REGMBC(0x1e10) return; - case 'E': case '\310': case '\311': case '\312': case '\313': + case 'E': case 0xc8: case 0xc9: case 0xca: case 0xcb: CASEMBC(0x112) CASEMBC(0x114) CASEMBC(0x116) CASEMBC(0x118) CASEMBC(0x11a) CASEMBC(0x1eba) CASEMBC(0x1ebc) - regmbc('E'); regmbc('\310'); regmbc('\311'); - regmbc('\312'); regmbc('\313'); + regmbc('E'); regmbc(0xc8); regmbc(0xc9); + regmbc(0xca); regmbc(0xcb); REGMBC(0x112) REGMBC(0x114) REGMBC(0x116) REGMBC(0x118) REGMBC(0x11a) REGMBC(0x1eba) REGMBC(0x1ebc) @@ -845,11 +846,11 @@ static void reg_equi_class(int c) regmbc('H'); REGMBC(0x124) REGMBC(0x126) REGMBC(0x1e22) REGMBC(0x1e26) REGMBC(0x1e28) return; - case 'I': case '\314': case '\315': case '\316': case '\317': + case 'I': case 0xcc: case 0xcd: case 0xce: case 0xcf: CASEMBC(0x128) CASEMBC(0x12a) CASEMBC(0x12c) CASEMBC(0x12e) CASEMBC(0x130) CASEMBC(0x1cf) CASEMBC(0x1ec8) - regmbc('I'); regmbc('\314'); regmbc('\315'); - regmbc('\316'); regmbc('\317'); + regmbc('I'); regmbc(0xcc); regmbc(0xcd); + regmbc(0xce); regmbc(0xcf); REGMBC(0x128) REGMBC(0x12a) REGMBC(0x12c) REGMBC(0x12e) REGMBC(0x130) REGMBC(0x1cf) REGMBC(0x1ec8) @@ -871,20 +872,20 @@ static void reg_equi_class(int c) case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40) regmbc('M'); REGMBC(0x1e3e) REGMBC(0x1e40) return; - case 'N': case '\321': + case 'N': case 0xd1: CASEMBC(0x143) CASEMBC(0x145) CASEMBC(0x147) CASEMBC(0x1e44) CASEMBC(0x1e48) - regmbc('N'); regmbc('\321'); + regmbc('N'); regmbc(0xd1); REGMBC(0x143) REGMBC(0x145) REGMBC(0x147) REGMBC(0x1e44) REGMBC(0x1e48) return; - case 'O': case '\322': case '\323': case '\324': case '\325': - case '\326': case '\330': + case 'O': case 0xd2: case 0xd3: case 0xd4: case 0xd5: + case 0xd6: case 0xd8: CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150) CASEMBC(0x1a0) CASEMBC(0x1d1) CASEMBC(0x1ea) CASEMBC(0x1ec) CASEMBC(0x1ece) - regmbc('O'); regmbc('\322'); regmbc('\323'); - regmbc('\324'); regmbc('\325'); regmbc('\326'); - regmbc('\330'); + regmbc('O'); regmbc(0xd2); regmbc(0xd3); + regmbc(0xd4); regmbc(0xd5); regmbc(0xd6); + regmbc(0xd8); REGMBC(0x14c) REGMBC(0x14e) REGMBC(0x150) REGMBC(0x1a0) REGMBC(0x1d1) REGMBC(0x1ea) REGMBC(0x1ec) REGMBC(0x1ece) @@ -907,12 +908,12 @@ static void reg_equi_class(int c) regmbc('T'); REGMBC(0x162) REGMBC(0x164) REGMBC(0x166) REGMBC(0x1e6a) REGMBC(0x1e6e) return; - case 'U': case '\331': case '\332': case '\333': case '\334': + case 'U': case 0xd9: case 0xda: case 0xdb: case 0xdc: CASEMBC(0x168) CASEMBC(0x16a) CASEMBC(0x16c) CASEMBC(0x16e) CASEMBC(0x170) CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3) CASEMBC(0x1ee6) - regmbc('U'); regmbc('\331'); regmbc('\332'); - regmbc('\333'); regmbc('\334'); + regmbc('U'); regmbc(0xd9); regmbc(0xda); + regmbc(0xdb); regmbc(0xdc); REGMBC(0x168) REGMBC(0x16a) REGMBC(0x16c) REGMBC(0x16e) REGMBC(0x170) REGMBC(0x172) REGMBC(0x1af) REGMBC(0x1d3) REGMBC(0x1ee6) @@ -928,10 +929,10 @@ static void reg_equi_class(int c) case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c) regmbc('X'); REGMBC(0x1e8a) REGMBC(0x1e8c) return; - case 'Y': case '\335': + case 'Y': case 0xdd: CASEMBC(0x176) CASEMBC(0x178) CASEMBC(0x1e8e) CASEMBC(0x1ef2) CASEMBC(0x1ef6) CASEMBC(0x1ef8) - regmbc('Y'); regmbc('\335'); + regmbc('Y'); regmbc(0xdd); REGMBC(0x176) REGMBC(0x178) REGMBC(0x1e8e) REGMBC(0x1ef2) REGMBC(0x1ef6) REGMBC(0x1ef8) return; @@ -941,13 +942,13 @@ static void reg_equi_class(int c) REGMBC(0x17d) REGMBC(0x1b5) REGMBC(0x1e90) REGMBC(0x1e94) return; - case 'a': case '\340': case '\341': case '\342': - case '\343': case '\344': case '\345': + case 'a': case 0xe0: case 0xe1: case 0xe2: + case 0xe3: case 0xe4: case 0xe5: CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105) CASEMBC(0x1ce) CASEMBC(0x1df) CASEMBC(0x1e1) CASEMBC(0x1ea3) - regmbc('a'); regmbc('\340'); regmbc('\341'); - regmbc('\342'); regmbc('\343'); regmbc('\344'); - regmbc('\345'); + regmbc('a'); regmbc(0xe0); regmbc(0xe1); + regmbc(0xe2); regmbc(0xe3); regmbc(0xe4); + regmbc(0xe5); REGMBC(0x101) REGMBC(0x103) REGMBC(0x105) REGMBC(0x1ce) REGMBC(0x1df) REGMBC(0x1e1) REGMBC(0x1ea3) @@ -955,9 +956,9 @@ static void reg_equi_class(int c) case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07) regmbc('b'); REGMBC(0x1e03) REGMBC(0x1e07) return; - case 'c': case '\347': + case 'c': case 0xe7: CASEMBC(0x107) CASEMBC(0x109) CASEMBC(0x10b) CASEMBC(0x10d) - regmbc('c'); regmbc('\347'); + regmbc('c'); regmbc(0xe7); REGMBC(0x107) REGMBC(0x109) REGMBC(0x10b) REGMBC(0x10d) return; @@ -966,11 +967,11 @@ static void reg_equi_class(int c) regmbc('d'); REGMBC(0x10f) REGMBC(0x111) REGMBC(0x1e0b) REGMBC(0x1e0f) REGMBC(0x1e11) return; - case 'e': case '\350': case '\351': case '\352': case '\353': + case 'e': case 0xe8: case 0xe9: case 0xea: case 0xeb: CASEMBC(0x113) CASEMBC(0x115) CASEMBC(0x117) CASEMBC(0x119) CASEMBC(0x11b) CASEMBC(0x1ebb) CASEMBC(0x1ebd) - regmbc('e'); regmbc('\350'); regmbc('\351'); - regmbc('\352'); regmbc('\353'); + regmbc('e'); regmbc(0xe8); regmbc(0xe9); + regmbc(0xea); regmbc(0xeb); REGMBC(0x113) REGMBC(0x115) REGMBC(0x117) REGMBC(0x119) REGMBC(0x11b) REGMBC(0x1ebb) REGMBC(0x1ebd) @@ -991,11 +992,11 @@ static void reg_equi_class(int c) REGMBC(0x1e23) REGMBC(0x1e27) REGMBC(0x1e29) REGMBC(0x1e96) return; - case 'i': case '\354': case '\355': case '\356': case '\357': + case 'i': case 0xec: case 0xed: case 0xee: case 0xef: CASEMBC(0x129) CASEMBC(0x12b) CASEMBC(0x12d) CASEMBC(0x12f) CASEMBC(0x1d0) CASEMBC(0x1ec9) - regmbc('i'); regmbc('\354'); regmbc('\355'); - regmbc('\356'); regmbc('\357'); + regmbc('i'); regmbc(0xec); regmbc(0xed); + regmbc(0xee); regmbc(0xef); REGMBC(0x129) REGMBC(0x12b) REGMBC(0x12d) REGMBC(0x12f) REGMBC(0x1d0) REGMBC(0x1ec9) return; @@ -1016,20 +1017,20 @@ static void reg_equi_class(int c) case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41) regmbc('m'); REGMBC(0x1e3f) REGMBC(0x1e41) return; - case 'n': case '\361': + case 'n': case 0xf1: CASEMBC(0x144) CASEMBC(0x146) CASEMBC(0x148) CASEMBC(0x149) CASEMBC(0x1e45) CASEMBC(0x1e49) - regmbc('n'); regmbc('\361'); + regmbc('n'); regmbc(0xf1); REGMBC(0x144) REGMBC(0x146) REGMBC(0x148) REGMBC(0x149) REGMBC(0x1e45) REGMBC(0x1e49) return; - case 'o': case '\362': case '\363': case '\364': case '\365': - case '\366': case '\370': + case 'o': case 0xf2: case 0xf3: case 0xf4: case 0xf5: + case 0xf6: case 0xf8: CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151) CASEMBC(0x1a1) CASEMBC(0x1d2) CASEMBC(0x1eb) CASEMBC(0x1ed) CASEMBC(0x1ecf) - regmbc('o'); regmbc('\362'); regmbc('\363'); - regmbc('\364'); regmbc('\365'); regmbc('\366'); - regmbc('\370'); + regmbc('o'); regmbc(0xf2); regmbc(0xf3); + regmbc(0xf4); regmbc(0xf5); regmbc(0xf6); + regmbc(0xf8); REGMBC(0x14d) REGMBC(0x14f) REGMBC(0x151) REGMBC(0x1a1) REGMBC(0x1d2) REGMBC(0x1eb) REGMBC(0x1ed) REGMBC(0x1ecf) @@ -1052,12 +1053,12 @@ static void reg_equi_class(int c) regmbc('t'); REGMBC(0x163) REGMBC(0x165) REGMBC(0x167) REGMBC(0x1e6b) REGMBC(0x1e6f) REGMBC(0x1e97) return; - case 'u': case '\371': case '\372': case '\373': case '\374': + case 'u': case 0xf9: case 0xfa: case 0xfb: case 0xfc: CASEMBC(0x169) CASEMBC(0x16b) CASEMBC(0x16d) CASEMBC(0x16f) CASEMBC(0x171) CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4) CASEMBC(0x1ee7) - regmbc('u'); regmbc('\371'); regmbc('\372'); - regmbc('\373'); regmbc('\374'); + regmbc('u'); regmbc(0xf9); regmbc(0xfa); + regmbc(0xfb); regmbc(0xfc); REGMBC(0x169) REGMBC(0x16b) REGMBC(0x16d) REGMBC(0x16f) REGMBC(0x171) REGMBC(0x173) REGMBC(0x1b0) REGMBC(0x1d4) REGMBC(0x1ee7) @@ -1074,10 +1075,10 @@ static void reg_equi_class(int c) case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d) regmbc('x'); REGMBC(0x1e8b) REGMBC(0x1e8d) return; - case 'y': case '\375': case '\377': + case 'y': case 0xfd: case 0xff: CASEMBC(0x177) CASEMBC(0x1e8f) CASEMBC(0x1e99) CASEMBC(0x1ef3) CASEMBC(0x1ef7) CASEMBC(0x1ef9) - regmbc('y'); regmbc('\375'); regmbc('\377'); + regmbc('y'); regmbc(0xfd); regmbc(0xff); REGMBC(0x177) REGMBC(0x1e8f) REGMBC(0x1e99) REGMBC(0x1ef3) REGMBC(0x1ef7) REGMBC(0x1ef9) return; @@ -3444,13 +3445,14 @@ static long bt_regexec_both(char_u *line, c = regline[col]; if (prog->regstart == NUL || prog->regstart == c - || (ireg_ic && (( - (enc_utf8 && utf_fold(prog->regstart) == utf_fold(c))) - || (c < 255 && prog->regstart < 255 && - vim_tolower(prog->regstart) == vim_tolower(c))))) + || (ireg_ic + && (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c))) + || (c < 255 && prog->regstart < 255 + && vim_tolower(prog->regstart) == vim_tolower(c))))) { retval = regtry(prog, col); - else + } else { retval = 0; + } } else { int tm_count = 0; /* Messy cases: unanchored match. */ @@ -4120,15 +4122,15 @@ regmatch ( char_u *opnd; opnd = OPERAND(scan); - /* Inline the first byte, for speed. */ + // Inline the first byte, for speed. if (*opnd != *reginput - && (!ireg_ic || ( - !enc_utf8 && - vim_tolower(*opnd) != vim_tolower(*reginput)))) + && (!ireg_ic + || (!enc_utf8 + && vim_tolower(*opnd) != vim_tolower(*reginput)))) { status = RA_NOMATCH; - else if (*opnd == NUL) { - /* match empty string always works; happens when "~" is - * empty. */ + } else if (*opnd == NUL) { + // match empty string always works; happens when "~" is + // empty. } else { if (opnd[1] == NUL && !(enc_utf8 && ireg_ic)) { len = 1; /* matched a single byte above */ @@ -4186,7 +4188,7 @@ regmatch ( /* When only a composing char is given match at any * position where that composing char appears. */ status = RA_NOMATCH; - for (i = 0; reginput[i] != NUL; i += utf_char2len(inpc)) { + for (i = 0; reginput[i] != NUL; i += utf_ptr2len(reginput + i)) { inpc = mb_ptr2char(reginput + i); if (!utf_iscomposing(inpc)) { if (i > 0) @@ -5333,10 +5335,12 @@ do_class: if ((len = (*mb_ptr2len)(opnd)) > 1) { if (ireg_ic && enc_utf8) cf = utf_fold(utf_ptr2char(opnd)); - while (count < maxcount) { - for (i = 0; i < len; ++i) - if (opnd[i] != scan[i]) + while (count < maxcount && (*mb_ptr2len)(scan) >= len) { + for (i = 0; i < len; ++i) { + if (opnd[i] != scan[i]) { break; + } + } if (i < len && (!ireg_ic || !enc_utf8 || utf_fold(utf_ptr2char(scan)) != cf)) break; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index b96dcc66b3..7e53b2ccd1 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -3464,13 +3464,12 @@ static char *pim_info(nfa_pim_T *pim) #endif -/* Used during execution: whether a match has been found. */ +// Used during execution: whether a match has been found. static int nfa_match; +static proftime_T *nfa_time_limit; +static int nfa_time_count; - -/* - * Copy postponed invisible match info from "from" to "to". - */ +// Copy postponed invisible match info from "from" to "to". static void copy_pim(nfa_pim_T *to, nfa_pim_T *from) { to->result = from->result; @@ -4048,6 +4047,7 @@ skip_add: sub->list.multi[subidx].start_col = (colnr_T)(reginput - regline + off); } + sub->list.multi[subidx].end_lnum = -1; } else { if (subidx < sub->in_use) { save_ptr = sub->list.line[subidx].start; @@ -4565,7 +4565,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T if (log_fd != NULL) { fprintf(log_fd, "****************************\n"); fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n"); - fprintf(log_fd, "MATCH = %s\n", result == TRUE ? "OK" : "FALSE"); + fprintf(log_fd, "MATCH = %s\n", !result ? "FALSE" : "OK"); fprintf(log_fd, "****************************\n"); } else { EMSG(_( @@ -4762,66 +4762,71 @@ static int skip_to_start(int c, colnr_T *colp) */ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) { - colnr_T col = startcol; - int c1, c2; - int len1, len2; - int match; +#define PTR2LEN(x) enc_utf8 ? utf_ptr2len(x) : MB_PTR2LEN(x) - for (;; ) { - match = TRUE; - len2 = MB_CHAR2LEN(regstart); /* skip regstart */ - for (len1 = 0; match_text[len1] != NUL; len1 += MB_CHAR2LEN(c1)) { - c1 = PTR2CHAR(match_text + len1); - c2 = PTR2CHAR(regline + col + len2); - if (c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2))) { - match = FALSE; + colnr_T col = startcol; + int regstart_len = PTR2LEN(regline + startcol); + + for (;;) { + bool match = true; + char_u *s1 = match_text; + char_u *s2 = regline + col + regstart_len; // skip regstart + while (*s1) { + int c1_len = PTR2LEN(s1); + int c1 = PTR2CHAR(s1); + int c2_len = PTR2LEN(s2); + int c2 = PTR2CHAR(s2); + + if ((c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2))) + || c1_len != c2_len) { + match = false; break; } - len2 += MB_CHAR2LEN(c2); + s1 += c1_len; + s2 += c2_len; } if (match - /* check that no composing char follows */ - && !(enc_utf8 - && STRLEN(regline) > (size_t)(col + len2) - && utf_iscomposing(PTR2CHAR(regline + col + len2))) - ) { + // check that no composing char follows + && !(enc_utf8 && utf_iscomposing(PTR2CHAR(s2)))) { cleanup_subexpr(); if (REG_MULTI) { reg_startpos[0].lnum = reglnum; reg_startpos[0].col = col; reg_endpos[0].lnum = reglnum; - reg_endpos[0].col = col + len2; + reg_endpos[0].col = s2 - regline; } else { reg_startp[0] = regline + col; - reg_endp[0] = regline + col + len2; + reg_endp[0] = s2; } return 1L; } - /* Try finding regstart after the current match. */ - col += MB_CHAR2LEN(regstart); /* skip regstart */ - if (skip_to_start(regstart, &col) == FAIL) + // Try finding regstart after the current match. + col += regstart_len; // skip regstart + if (skip_to_start(regstart, &col) == FAIL) { break; + } } return 0L; + +#undef PTR2LEN } -/* - * Main matching routine. - * - * Run NFA to determine whether it matches reginput. - * - * When "nfa_endp" is not NULL it is a required end-of-match position. - * - * Return TRUE if there is a match, FALSE otherwise. - * When there is a match "submatch" contains the positions. - * Note: Caller must ensure that: start != NULL. - */ -static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *submatch, regsubs_T *m) +/// Main matching routine. +/// +/// Run NFA to determine whether it matches reginput. +/// +/// When "nfa_endp" is not NULL it is a required end-of-match position. +/// +/// Return TRUE if there is a match, FALSE otherwise. +/// When there is a match "submatch" contains the positions. +/// Note: Caller must ensure that: start != NULL. +static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, + regsubs_T *submatch, regsubs_T *m) { int result; int flag = 0; - int go_to_nextline = FALSE; + bool go_to_nextline = false; nfa_thread_T *t; nfa_list_T list[2]; int listidx; @@ -4838,18 +4843,22 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (debug == NULL) { EMSG2(_("(NFA) COULD NOT OPEN %s !"), NFA_REGEXP_DEBUG_LOG); - return FALSE; + return false; } #endif - /* Some patterns may take a long time to match, especially when using - * recursive_regmatch(). Allow interrupting them with CTRL-C. */ + // Some patterns may take a long time to match, especially when using + // recursive_regmatch(). Allow interrupting them with CTRL-C. fast_breakcheck(); - if (got_int) - return FALSE; + if (got_int) { + return false; + } + if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) { + return false; + } - nfa_match = FALSE; + nfa_match = false; - /* Allocate memory for the lists of nodes. */ + // Allocate memory for the lists of nodes. size_t size = (nstate + 1) * sizeof(nfa_thread_T); list[0].t = xmalloc(size); list[0].len = nstate + 1; @@ -4917,7 +4926,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } if (curc == NUL) { clen = 0; - go_to_nextline = FALSE; + go_to_nextline = false; } /* swap lists */ @@ -5000,7 +5009,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (enc_utf8 && !ireg_icombine && utf_iscomposing(curc)) { break; } - nfa_match = TRUE; + nfa_match = true; copy_sub(&submatch->norm, &t->subs.norm); if (nfa_has_zsubexpr) copy_sub(&submatch->synt, &t->subs.synt); @@ -5065,10 +5074,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm fprintf(log_fd, "Match found:\n"); log_subsexpr(m); #endif - nfa_match = TRUE; - /* See comment above at "goto nextchar". */ - if (nextlist->n == 0) + nfa_match = true; + // See comment above at "goto nextchar". + if (nextlist->n == 0) { clen = 0; + } goto nextchar; case NFA_START_INVISIBLE: @@ -5085,8 +5095,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm failure_chance(t->state->out, 0), failure_chance(t->state->out1->out, 0)); #endif - /* Do it directly if there already is a PIM or when - * nfa_postprocess() detected it will work better. */ + // Do it directly if there already is a PIM or when + // nfa_postprocess() detected it will work better. if (t->pim.result != NFA_PIM_UNUSED || t->state->c == NFA_START_INVISIBLE_FIRST || t->state->c == NFA_START_INVISIBLE_NEG_FIRST @@ -5094,42 +5104,40 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm || t->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST) { int in_use = m->norm.in_use; - /* Copy submatch info for the recursive call, opposite - * of what happens on success below. */ + // Copy submatch info for the recursive call, opposite + // of what happens on success below. copy_sub_off(&m->norm, &t->subs.norm); if (nfa_has_zsubexpr) copy_sub_off(&m->synt, &t->subs.synt); - /* - * First try matching the invisible match, then what - * follows. - */ - result = recursive_regmatch(t->state, NULL, prog, - submatch, m, &listids); + // First try matching the invisible match, then what + // follows. + result = recursive_regmatch(t->state, NULL, prog, submatch, m, + &listids); if (result == NFA_TOO_EXPENSIVE) { nfa_match = result; goto theend; } - /* for \@! and \@<! it is a match when the result is - * FALSE */ + // for \@! and \@<! it is a match when the result is + // FALSE if (result != (t->state->c == NFA_START_INVISIBLE_NEG || t->state->c == NFA_START_INVISIBLE_NEG_FIRST || t->state->c == NFA_START_INVISIBLE_BEFORE_NEG || t->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) { - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&t->subs.norm, &m->norm); if (nfa_has_zsubexpr) copy_sub_off(&t->subs.synt, &m->synt); - /* If the pattern has \ze and it matched in the - * sub pattern, use it. */ + // If the pattern has \ze and it matched in the + // sub pattern, use it. copy_ze_off(&t->subs.norm, &m->norm); - /* t->state->out1 is the corresponding - * END_INVISIBLE node; Add its out to the current - * list (zero-width match). */ + // t->state->out1 is the corresponding + // END_INVISIBLE node; Add its out to the current + // list (zero-width match). add_here = true; add_state = t->state->out1->out; } @@ -5137,12 +5145,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } else { nfa_pim_T pim; - /* - * First try matching what follows. Only if a match - * is found verify the invisible match matches. Add a - * nfa_pim_T to the following states, it contains info - * about the invisible match. - */ + // First try matching what follows. Only if a match + // is found verify the invisible match matches. Add a + // nfa_pim_T to the following states, it contains info + // about the invisible match. pim.state = t->state; pim.result = NFA_PIM_TODO; pim.subs.norm.in_use = 0; @@ -5153,9 +5159,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } else pim.end.ptr = reginput; - /* t->state->out1 is the corresponding END_INVISIBLE - * node; Add its out to the current list (zero-width - * match). */ + // t->state->out1 is the corresponding END_INVISIBLE + // node; Add its out to the current list (zero-width + // match). addstate_here(thislist, t->state->out1->out, &t->subs, &pim, &listidx); } @@ -5169,8 +5175,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm int skip_lid = 0; #endif - /* There is no point in trying to match the pattern if the - * output state is not going to be added to the list. */ + // There is no point in trying to match the pattern if the + // output state is not going to be added to the list. if (state_in_list(nextlist, t->state->out1->out, &t->subs)) { skip = t->state->out1->out; #ifdef REGEXP_DEBUG @@ -5199,15 +5205,16 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm #endif break; } - /* Copy submatch info to the recursive call, opposite of what - * happens afterwards. */ + // Copy submatch info to the recursive call, opposite of what + // happens afterwards. copy_sub_off(&m->norm, &t->subs.norm); - if (nfa_has_zsubexpr) + if (nfa_has_zsubexpr) { copy_sub_off(&m->synt, &t->subs.synt); + } - /* First try matching the pattern. */ - result = recursive_regmatch(t->state, NULL, prog, - submatch, m, &listids); + // First try matching the pattern. + result = recursive_regmatch(t->state, NULL, prog, submatch, m, + &listids); if (result == NFA_TOO_EXPENSIVE) { nfa_match = result; goto theend; @@ -5219,36 +5226,38 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm fprintf(log_fd, "NFA_START_PATTERN matches:\n"); log_subsexpr(m); #endif - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&t->subs.norm, &m->norm); - if (nfa_has_zsubexpr) + if (nfa_has_zsubexpr) { copy_sub_off(&t->subs.synt, &m->synt); - /* Now we need to skip over the matched text and then - * continue with what follows. */ - if (REG_MULTI) - /* TODO: multi-line match */ + } + // Now we need to skip over the matched text and then + // continue with what follows. + if (REG_MULTI) { + // TODO(RE): multi-line match bytelen = m->norm.list.multi[0].end_col - (int)(reginput - regline); - else + } else { bytelen = (int)(m->norm.list.line[0].end - reginput); + } #ifdef REGEXP_DEBUG fprintf(log_fd, "NFA_START_PATTERN length: %d\n", bytelen); #endif if (bytelen == 0) { - /* empty match, output of corresponding - * NFA_END_PATTERN/NFA_SKIP to be used at current - * position */ + // empty match, output of corresponding + // NFA_END_PATTERN/NFA_SKIP to be used at current + // position add_here = true; add_state = t->state->out1->out->out; } else if (bytelen <= clen) { - /* match current character, output of corresponding - * NFA_END_PATTERN to be used at next position. */ + // match current character, output of corresponding + // NFA_END_PATTERN to be used at next position. add_state = t->state->out1->out->out; add_off = clen; } else { - /* skip over the matched characters, set character - * count in NFA_SKIP */ + // skip over the matched characters, set character + // count in NFA_SKIP add_state = t->state->out1->out; add_off = bytelen; add_count = bytelen - clen; @@ -5272,23 +5281,25 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; case NFA_BOW: - result = TRUE; + result = true; - if (curc == NUL) - result = FALSE; - else if (has_mbyte) { + if (curc == NUL) { + result = false; + } else if (has_mbyte) { int this_class; - /* Get class of current and previous char (if it exists). */ + // Get class of current and previous char (if it exists). this_class = mb_get_class_buf(reginput, reg_buf); - if (this_class <= 1) - result = FALSE; - else if (reg_prev_class() == this_class) - result = FALSE; + if (this_class <= 1) { + result = false; + } else if (reg_prev_class() == this_class) { + result = false; + } } else if (!vim_iswordc_buf(curc, reg_buf) || (reginput > regline - && vim_iswordc_buf(reginput[-1], reg_buf))) - result = FALSE; + && vim_iswordc_buf(reginput[-1], reg_buf))) { + result = false; + } if (result) { add_here = true; add_state = t->state->out; @@ -5296,22 +5307,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; case NFA_EOW: - result = TRUE; - if (reginput == regline) - result = FALSE; - else if (has_mbyte) { + result = true; + if (reginput == regline) { + result = false; + } else if (has_mbyte) { int this_class, prev_class; - /* Get class of current and previous char (if it exists). */ + // Get class of current and previous char (if it exists). this_class = mb_get_class_buf(reginput, reg_buf); prev_class = reg_prev_class(); if (this_class == prev_class - || prev_class == 0 || prev_class == 1) - result = FALSE; + || prev_class == 0 || prev_class == 1) { + result = false; + } } else if (!vim_iswordc_buf(reginput[-1], reg_buf) || (reginput[0] != NUL - && vim_iswordc_buf(curc, reg_buf))) - result = FALSE; + && vim_iswordc_buf(curc, reg_buf))) { + result = false; + } if (result) { add_here = true; add_state = t->state->out; @@ -5346,30 +5359,31 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm sta = t->state->out; len = 0; if (utf_iscomposing(sta->c)) { - /* Only match composing character(s), ignore base - * character. Used for ".{composing}" and "{composing}" - * (no preceding character). */ + // Only match composing character(s), ignore base + // character. Used for ".{composing}" and "{composing}" + // (no preceding character). len += mb_char2len(mc); } if (ireg_icombine && len == 0) { - /* If \Z was present, then ignore composing characters. - * When ignoring the base character this always matches. */ - if (sta->c != curc) + // If \Z was present, then ignore composing characters. + // When ignoring the base character this always matches. + if (sta->c != curc) { result = FAIL; - else + } else { result = OK; - while (sta->c != NFA_END_COMPOSING) + } + while (sta->c != NFA_END_COMPOSING) { sta = sta->out; - } - /* Check base character matches first, unless ignored. */ - else if (len > 0 || mc == sta->c) { + } + } else if (len > 0 || mc == sta->c) { + // Check base character matches first, unless ignored. if (len == 0) { len += mb_char2len(mc); sta = sta->out; } - /* We don't care about the order of composing characters. - * Get them into cchars[] first. */ + // We don't care about the order of composing characters. + // Get them into cchars[] first. while (len < clen) { mc = mb_ptr2char(reginput + len); cchars[ccount++] = mc; @@ -5378,9 +5392,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; } - /* Check that each composing char in the pattern matches a - * composing char in the text. We do not check if all - * composing chars are matched. */ + // Check that each composing char in the pattern matches a + // composing char in the text. We do not check if all + // composing chars are matched. result = OK; while (sta->c != NFA_END_COMPOSING) { for (j = 0; j < ccount; ++j) @@ -5395,7 +5409,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } else result = FAIL; - end = t->state->out1; /* NFA_END_COMPOSING */ + end = t->state->out1; // NFA_END_COMPOSING ADD_STATE_IF_MATCH(end); break; } @@ -5403,13 +5417,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_NEWL: if (curc == NUL && !reg_line_lbr && REG_MULTI && reglnum <= reg_maxline) { - go_to_nextline = TRUE; - /* Pass -1 for the offset, which means taking the position - * at the start of the next line. */ + go_to_nextline = true; + // Pass -1 for the offset, which means taking the position + // at the start of the next line. add_state = t->state->out; add_off = -1; } else if (curc == '\n' && reg_line_lbr) { - /* match \n as if it is an ordinary character */ + // match \n as if it is an ordinary character add_state = t->state->out; add_off = 1; } @@ -5418,16 +5432,17 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_START_COLL: case NFA_START_NEG_COLL: { - /* What follows is a list of characters, until NFA_END_COLL. - * One of them must match or none of them must match. */ + // What follows is a list of characters, until NFA_END_COLL. + // One of them must match or none of them must match. nfa_state_T *state; int result_if_matched; int c1, c2; - /* Never match EOL. If it's part of the collection it is added - * as a separate state with an OR. */ - if (curc == NUL) + // Never match EOL. If it's part of the collection it is added + // as a separate state with an OR. + if (curc == NUL) { break; + } state = t->state->out; result_if_matched = (t->state->c == NFA_START_COLL); @@ -5438,7 +5453,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } if (state->c == NFA_RANGE_MIN) { c1 = state->val; - state = state->out; /* advance to NFA_RANGE_MAX */ + state = state->out; // advance to NFA_RANGE_MAX c2 = state->val; #ifdef REGEXP_DEBUG fprintf(log_fd, "NFA_RANGE_MIN curc=%d c1=%d c2=%d\n", @@ -5471,8 +5486,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm state = state->out; } if (result) { - /* next state is in out of the NFA_END_COLL, out1 of - * START points to the END state */ + // next state is in out of the NFA_END_COLL, out1 of + // START points to the END state add_state = t->state->out1->out; add_off = clen; } @@ -5480,7 +5495,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } case NFA_ANY: - /* Any char except '\0', (end of input) does not match. */ + // Any char except '\0', (end of input) does not match. if (curc > 0) { add_state = t->state->out; add_off = clen; @@ -5499,157 +5514,155 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm add_state = t->state->out; break; - /* - * Character classes like \a for alpha, \d for digit etc. - */ - case NFA_IDENT: /* \i */ + // Character classes like \a for alpha, \d for digit etc. + case NFA_IDENT: // \i result = vim_isIDc(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SIDENT: /* \I */ + case NFA_SIDENT: // \I result = !ascii_isdigit(curc) && vim_isIDc(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_KWORD: /* \k */ + case NFA_KWORD: // \k result = vim_iswordp_buf(reginput, reg_buf); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SKWORD: /* \K */ + case NFA_SKWORD: // \K result = !ascii_isdigit(curc) && vim_iswordp_buf(reginput, reg_buf); ADD_STATE_IF_MATCH(t->state); break; - case NFA_FNAME: /* \f */ + case NFA_FNAME: // \f result = vim_isfilec(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SFNAME: /* \F */ + case NFA_SFNAME: // \F result = !ascii_isdigit(curc) && vim_isfilec(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_PRINT: /* \p */ + case NFA_PRINT: // \p result = vim_isprintc(PTR2CHAR(reginput)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_SPRINT: /* \P */ + case NFA_SPRINT: // \P result = !ascii_isdigit(curc) && vim_isprintc(PTR2CHAR(reginput)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_WHITE: /* \s */ + case NFA_WHITE: // \s result = ascii_iswhite(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NWHITE: /* \S */ + case NFA_NWHITE: // \S result = curc != NUL && !ascii_iswhite(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_DIGIT: /* \d */ + case NFA_DIGIT: // \d result = ri_digit(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NDIGIT: /* \D */ + case NFA_NDIGIT: // \D result = curc != NUL && !ri_digit(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_HEX: /* \x */ + case NFA_HEX: // \x result = ri_hex(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NHEX: /* \X */ + case NFA_NHEX: // \X result = curc != NUL && !ri_hex(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_OCTAL: /* \o */ + case NFA_OCTAL: // \o result = ri_octal(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NOCTAL: /* \O */ + case NFA_NOCTAL: // \O result = curc != NUL && !ri_octal(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_WORD: /* \w */ + case NFA_WORD: // \w result = ri_word(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NWORD: /* \W */ + case NFA_NWORD: // \W result = curc != NUL && !ri_word(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_HEAD: /* \h */ + case NFA_HEAD: // \h result = ri_head(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NHEAD: /* \H */ + case NFA_NHEAD: // \H result = curc != NUL && !ri_head(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_ALPHA: /* \a */ + case NFA_ALPHA: // \a result = ri_alpha(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NALPHA: /* \A */ + case NFA_NALPHA: // \A result = curc != NUL && !ri_alpha(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_LOWER: /* \l */ + case NFA_LOWER: // \l result = ri_lower(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NLOWER: /* \L */ + case NFA_NLOWER: // \L result = curc != NUL && !ri_lower(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_UPPER: /* \u */ + case NFA_UPPER: // \u result = ri_upper(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NUPPER: /* \U */ + case NFA_NUPPER: // \U result = curc != NUL && !ri_upper(curc); ADD_STATE_IF_MATCH(t->state); break; - case NFA_LOWER_IC: /* [a-z] */ + case NFA_LOWER_IC: // [a-z] result = ri_lower(curc) || (ireg_ic && ri_upper(curc)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NLOWER_IC: /* [^a-z] */ + case NFA_NLOWER_IC: // [^a-z] result = curc != NUL && !(ri_lower(curc) || (ireg_ic && ri_upper(curc))); ADD_STATE_IF_MATCH(t->state); break; - case NFA_UPPER_IC: /* [A-Z] */ + case NFA_UPPER_IC: // [A-Z] result = ri_upper(curc) || (ireg_ic && ri_lower(curc)); ADD_STATE_IF_MATCH(t->state); break; - case NFA_NUPPER_IC: /* ^[A-Z] */ + case NFA_NUPPER_IC: // [^A-Z] result = curc != NUL && !(ri_upper(curc) || (ireg_ic && ri_lower(curc))); ADD_STATE_IF_MATCH(t->state); @@ -5673,7 +5686,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_ZREF7: case NFA_ZREF8: case NFA_ZREF9: - /* \1 .. \9 \z1 .. \z9 */ + // \1 .. \9 \z1 .. \z9 { int subidx; int bytelen; @@ -5688,18 +5701,18 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (result) { if (bytelen == 0) { - /* empty match always works, output of NFA_SKIP to be - * used next */ + // empty match always works, output of NFA_SKIP to be + // used next add_here = true; add_state = t->state->out->out; } else if (bytelen <= clen) { - /* match current character, jump ahead to out of - * NFA_SKIP */ + // match current character, jump ahead to out of + // NFA_SKIP add_state = t->state->out->out; add_off = clen; } else { - /* skip over the matched characters, set character - * count in NFA_SKIP */ + // skip over the matched characters, set character + // count in NFA_SKIP add_state = t->state->out; add_off = bytelen; add_count = bytelen - clen; @@ -5708,13 +5721,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; } case NFA_SKIP: - /* character of previous matching \1 .. \9 or \@> */ + // character of previous matching \1 .. \9 or \@> if (t->count - clen <= 0) { - /* end of match, go to what follows */ + // end of match, go to what follows add_state = t->state->out; add_off = clen; } else { - /* add state again with decremented count */ + // add state again with decremented count add_state = t->state; add_off = 0; add_count = t->count - clen; @@ -5766,7 +5779,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm break; } - result = FALSE; + result = false; win_T *wp = reg_win == NULL ? curwin : reg_win; if (op == 1 && col - 1 > t->state->val && col > 100) { long ts = wp->w_buffer->b_p_ts; @@ -5796,9 +5809,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm { pos_T *pos = getmark_buf(reg_buf, t->state->val, FALSE); - /* Compare the mark position to the match position. */ - result = (pos != NULL /* mark doesn't exist */ - && pos->lnum > 0 /* mark isn't set in reg_buf */ + // Compare the mark position to the match position. + result = (pos != NULL // mark doesn't exist + && pos->lnum > 0 // mark isn't set in reg_buf && (pos->lnum == reglnum + reg_firstlnum ? (pos->col == (colnr_T)(reginput - regline) ? t->state->c == NFA_MARK @@ -5855,11 +5868,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_ZOPEN9: case NFA_NOPEN: case NFA_ZSTART: - /* These states are only added to be able to bail out when - * they are added again, nothing is to be done. */ + // These states are only added to be able to bail out when + // they are added again, nothing is to be done. break; - default: /* regular character */ + default: // regular character { int c = t->state->c; @@ -5875,14 +5888,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm // If ireg_icombine is not set only skip over the character // itself. When it is set skip over composing characters. if (result && enc_utf8 && !ireg_icombine) { - clen = utf_char2len(curc); + clen = utf_ptr2len(reginput); } ADD_STATE_IF_MATCH(t->state); break; } - - } /* switch (t->state->c) */ + } // switch (t->state->c) if (add_state != NULL) { nfa_pim_T *pim; @@ -5893,8 +5905,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm else pim = &t->pim; - /* Handle the postponed invisible match if the match might end - * without advancing and before the end of the line. */ + // Handle the postponed invisible match if the match might end + // without advancing and before the end of the line. if (pim != NULL && (clen == 0 || match_follows(add_state, 0))) { if (pim->result == NFA_PIM_TODO) { #ifdef REGEXP_DEBUG @@ -5906,15 +5918,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm result = recursive_regmatch(pim->state, pim, prog, submatch, m, &listids); pim->result = result ? NFA_PIM_MATCH : NFA_PIM_NOMATCH; - /* for \@! and \@<! it is a match when the result is - * FALSE */ + // for \@! and \@<! it is a match when the result is + // FALSE if (result != (pim->state->c == NFA_START_INVISIBLE_NEG || pim->state->c == NFA_START_INVISIBLE_NEG_FIRST || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) { - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&pim->subs.norm, &m->norm); if (nfa_has_zsubexpr) copy_sub_off(&pim->subs.synt, &m->synt); @@ -5927,34 +5939,35 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm log_fd, "Using previous recursive nfa_regmatch() result, result == %d\n", pim->result); - fprintf(log_fd, "MATCH = %s\n", result == TRUE ? "OK" : "FALSE"); + fprintf(log_fd, "MATCH = %s\n", result ? "OK" : "FALSE"); fprintf(log_fd, "\n"); #endif } - /* for \@! and \@<! it is a match when result is FALSE */ + // for \@! and \@<! it is a match when result is FALSE if (result != (pim->state->c == NFA_START_INVISIBLE_NEG || pim->state->c == NFA_START_INVISIBLE_NEG_FIRST || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG || pim->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) { - /* Copy submatch info from the recursive call */ + // Copy submatch info from the recursive call copy_sub_off(&t->subs.norm, &pim->subs.norm); if (nfa_has_zsubexpr) copy_sub_off(&t->subs.synt, &pim->subs.synt); - } else - /* look-behind match failed, don't add the state */ + } else { + // look-behind match failed, don't add the state continue; + } - /* Postponed invisible match was handled, don't add it to - * following states. */ + // Postponed invisible match was handled, don't add it to + // following states. pim = NULL; } - /* If "pim" points into l->t it will become invalid when - * adding the state causes the list to be reallocated. Make a - * local copy to avoid that. */ + // If "pim" points into l->t it will become invalid when + // adding the state causes the list to be reallocated. Make a + // local copy to avoid that. if (pim == &t->pim) { copy_pim(&pim_copy, pim); pim = &pim_copy; @@ -5968,18 +5981,17 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm nextlist->t[nextlist->n - 1].count = add_count; } } - - } /* for (thislist = thislist; thislist->state; thislist++) */ - - /* Look for the start of a match in the current position by adding the - * start state to the list of states. - * The first found match is the leftmost one, thus the order of states - * matters! - * Do not add the start state in recursive calls of nfa_regmatch(), - * because recursive calls should only start in the first position. - * Unless "nfa_endp" is not NULL, then we match the end position. - * Also don't start a match past the first line. */ - if (nfa_match == FALSE + } // for (thislist = thislist; thislist->state; thislist++) + + // Look for the start of a match in the current position by adding the + // start state to the list of states. + // The first found match is the leftmost one, thus the order of states + // matters! + // Do not add the start state in recursive calls of nfa_regmatch(), + // because recursive calls should only start in the first position. + // Unless "nfa_endp" is not NULL, then we match the end position. + // Also don't start a match past the first line. + if (!nfa_match && ((toplevel && reglnum == 0 && clen != 0 @@ -5995,8 +6007,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm #ifdef REGEXP_DEBUG fprintf(log_fd, "(---) STARTSTATE\n"); #endif - /* Inline optimized code for addstate() if we know the state is - * the first MOPEN. */ + // Inline optimized code for addstate() if we know the state is + // the first MOPEN. if (toplevel) { int add = TRUE; int c; @@ -6005,18 +6017,19 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (nextlist->n == 0) { colnr_T col = (colnr_T)(reginput - regline) + clen; - /* Nextlist is empty, we can skip ahead to the - * character that must appear at the start. */ - if (skip_to_start(prog->regstart, &col) == FAIL) + // Nextlist is empty, we can skip ahead to the + // character that must appear at the start. + if (skip_to_start(prog->regstart, &col) == FAIL) { break; + } #ifdef REGEXP_DEBUG fprintf(log_fd, " Skipping ahead %d bytes to regstart\n", col - ((colnr_T)(reginput - regline) + clen)); #endif reginput = regline + col - clen; } else { - /* Checking if the required start character matches is - * cheaper than adding a state that won't match. */ + // Checking if the required start character matches is + // cheaper than adding a state that won't match. c = PTR2CHAR(reginput + clen); if (c != prog->regstart && (!ireg_ic || vim_tolower(c) != vim_tolower(prog->regstart))) { @@ -6053,21 +6066,29 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm #endif nextchar: - /* Advance to the next character, or advance to the next line, or - * finish. */ - if (clen != 0) + // Advance to the next character, or advance to the next line, or + // finish. + if (clen != 0) { reginput += clen; - else if (go_to_nextline || (nfa_endp != NULL && REG_MULTI - && reglnum < nfa_endp->se_u.pos.lnum)) + } else if (go_to_nextline || (nfa_endp != NULL && REG_MULTI + && reglnum < nfa_endp->se_u.pos.lnum)) { reg_nextline(); - else + } else { break; + } // Allow interrupting with CTRL-C. - fast_breakcheck(); + line_breakcheck(); if (got_int) { break; } + // Check for timeout once every twenty times to avoid overhead. + if (nfa_time_limit != NULL && ++nfa_time_count == 20) { + nfa_time_count = 0; + if (profile_passed_limit(*nfa_time_limit)) { + break; + } + } } #ifdef REGEXP_DEBUG @@ -6077,7 +6098,7 @@ nextchar: #endif theend: - /* Free memory */ + // Free memory xfree(list[0].t); xfree(list[1].t); xfree(listids); @@ -6089,11 +6110,9 @@ theend: return nfa_match; } -/* - * Try match of "prog" with at regline["col"]. - * Returns <= 0 for failure, number of lines contained in the match otherwise. - */ -static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) +// Try match of "prog" with at regline["col"]. +// Returns <= 0 for failure, number of lines contained in the match otherwise. +static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm) { int i; regsubs_T subs, m; @@ -6103,6 +6122,8 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) #endif reginput = regline + col; + nfa_time_limit = tm; + nfa_time_count = 0; #ifdef REGEXP_DEBUG f = fopen(NFA_REGEXP_RUN_LOG, "a"); @@ -6127,7 +6148,7 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) clear_sub(&m.synt); int result = nfa_regmatch(prog, start, &subs, &m); - if (result == FALSE) { + if (!result) { return 0; } else if (result == NFA_TOO_EXPENSIVE) { return result; @@ -6173,7 +6194,8 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) if (prog->reghasz == REX_SET) { cleanup_zsubexpr(); re_extmatch_out = make_extmatch(); - for (i = 0; i < subs.synt.in_use; i++) { + // Loop over \z1, \z2, etc. There is no \z0. + for (i = 1; i < subs.synt.in_use; i++) { if (REG_MULTI) { struct multipos *mpos = &subs.synt.list.multi[i]; @@ -6199,17 +6221,16 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) return 1 + reglnum; } -/* - * Match a regexp against a string ("line" points to the string) or multiple - * lines ("line" is NULL, use reg_getline()). - * - * Returns <= 0 for failure, number of lines contained in the match otherwise. - */ -static long -nfa_regexec_both ( - char_u *line, - colnr_T startcol /* column to start looking for match */ -) +/// Match a regexp against a string ("line" points to the string) or multiple +/// lines ("line" is NULL, use reg_getline()). +/// +/// @param line String in which to search or NULL +/// @param startcol Column to start looking for match +/// @param tm Timeout limit or NULL +/// +/// @return <= 0 if there is no match and number of lines contained in the +/// match otherwise. +static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) { nfa_regprog_T *prog; long retval = 0L; @@ -6289,7 +6310,7 @@ nfa_regexec_both ( prog->state[i].lastlist[1] = 0; } - retval = nfa_regtry(prog, col); + retval = nfa_regtry(prog, col, tm); nfa_regengine.expr = NULL; @@ -6441,7 +6462,7 @@ nfa_regexec_nl ( ireg_ic = rmp->rm_ic; ireg_icombine = FALSE; ireg_maxcol = 0; - return nfa_regexec_both(line, col); + return nfa_regexec_both(line, col, NULL); } /// Matches a regexp against multiple lines. @@ -6492,5 +6513,5 @@ static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, ireg_icombine = FALSE; ireg_maxcol = rmp->rmm_maxcol; - return nfa_regexec_both(NULL, col); + return nfa_regexec_both(NULL, col, tm); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 9fdb476748..10b5b6bba4 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -147,6 +147,9 @@ static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */ */ static schar_T *current_ScreenLine; +StlClickDefinition *tab_page_click_defs = NULL; +long tab_page_click_defs_size = 0; + # define SCREEN_LINE(r, o, e, c, rl) screen_line((r), (o), (e), (c), (rl)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" @@ -384,14 +387,15 @@ void update_screen(int type) if (wp->w_buffer->b_mod_set) { win_T *wwp; - /* Check if we already did this buffer. */ - for (wwp = firstwin; wwp != wp; wwp = wwp->w_next) - if (wwp->w_buffer == wp->w_buffer) + // Check if we already did this buffer. + for (wwp = firstwin; wwp != wp; wwp = wwp->w_next) { + if (wwp->w_buffer == wp->w_buffer) { break; - if ( - wwp == wp && - syntax_present(wp)) + } + } + if (wwp == wp && syntax_present(wp)) { syn_stack_apply_changes(wp->w_buffer); + } } } @@ -752,10 +756,11 @@ static void win_update(win_T *wp) lnumt = wp->w_lines[i].wl_lastlnum + 1; if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) { lnumb = wp->w_lines[i].wl_lnum; - /* When there is a fold column it might need updating - * in the next line ("J" just above an open fold). */ - if (wp->w_p_fdc > 0) - ++lnumb; + // When there is a fold column it might need updating + // in the next line ("J" just above an open fold). + if (compute_foldcolumn(wp, 0) > 0) { + lnumb++; + } } } @@ -1227,16 +1232,16 @@ static void win_update(win_T *wp) || did_update == DID_FOLD || (did_update == DID_LINE && syntax_present(wp) - && ( - (foldmethodIsSyntax(wp) - && hasAnyFolding(wp)) || - syntax_check_changed(lnum))) + && ((foldmethodIsSyntax(wp) + && hasAnyFolding(wp)) + || syntax_check_changed(lnum))) // match in fixed position might need redraw // if lines were inserted or deleted - || (wp->w_match_head != NULL && buf->b_mod_xlines != 0) - ))))) { - if (lnum == mod_top) - top_to_mod = FALSE; + || (wp->w_match_head != NULL + && buf->b_mod_xlines != 0)))))) { + if (lnum == mod_top) { + top_to_mod = false; + } /* * When at start of changed lines: May scroll following lines @@ -1567,10 +1572,11 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h { int n = 0; # define FDC_OFF n + int fdc = compute_foldcolumn(wp, 0); if (wp->w_p_rl) { - /* No check for cmdline window: should never be right-left. */ - n = wp->w_p_fdc; + // No check for cmdline window: should never be right-left. + n = fdc; if (n > 0) { /* draw the fold column at the right */ @@ -1610,8 +1616,8 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h wp->w_wincol, wp->w_wincol + n, cmdwin_type, ' ', hl_attr(HLF_AT)); } - if (wp->w_p_fdc > 0) { - int nn = n + wp->w_p_fdc; + if (fdc > 0) { + int nn = n + fdc; /* draw the fold column at the left */ if (nn > wp->w_width) @@ -1654,6 +1660,20 @@ static int advance_color_col(int vcol, int **color_cols) return **color_cols >= 0; } +// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much +// space is available for window "wp", minus "col". +static int compute_foldcolumn(win_T *wp, int col) +{ + int fdc = wp->w_p_fdc; + int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw; + int wwidth = wp->w_width; + + if (fdc > wwidth - (col + wmw)) { + fdc = wwidth - (col + wmw); + } + return fdc; +} + /* * Display one folded line. */ @@ -1692,12 +1712,9 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T ++col; } - /* - * 2. Add the 'foldcolumn' - */ - fdc = wp->w_p_fdc; - if (fdc > wp->w_width - col) - fdc = wp->w_width - col; + // 2. Add the 'foldcolumn' + // Reduce the width when there is not enough space. + fdc = compute_foldcolumn(wp, col); if (fdc > 0) { fill_foldcolumn(buf, wp, TRUE, lnum); if (wp->w_p_rl) { @@ -2018,37 +2035,42 @@ fill_foldcolumn ( int level; int first_level; int empty; + int fdc = compute_foldcolumn(wp, 0); - /* Init to all spaces. */ - memset(p, ' ', (size_t)wp->w_p_fdc); + // Init to all spaces. + memset(p, ' ', (size_t)fdc); level = win_foldinfo.fi_level; if (level > 0) { - /* If there is only one column put more info in it. */ - empty = (wp->w_p_fdc == 1) ? 0 : 1; + // If there is only one column put more info in it. + empty = (fdc == 1) ? 0 : 1; - /* If the column is too narrow, we start at the lowest level that - * fits and use numbers to indicated the depth. */ - first_level = level - wp->w_p_fdc - closed + 1 + empty; - if (first_level < 1) + // If the column is too narrow, we start at the lowest level that + // fits and use numbers to indicated the depth. + first_level = level - fdc - closed + 1 + empty; + if (first_level < 1) { first_level = 1; + } - for (i = 0; i + empty < wp->w_p_fdc; ++i) { + for (i = 0; i + empty < fdc; i++) { if (win_foldinfo.fi_lnum == lnum - && first_level + i >= win_foldinfo.fi_low_level) + && first_level + i >= win_foldinfo.fi_low_level) { p[i] = '-'; - else if (first_level == 1) + } else if (first_level == 1) { p[i] = '|'; - else if (first_level + i <= 9) + } else if (first_level + i <= 9) { p[i] = '0' + first_level + i; - else + } else { p[i] = '>'; - if (first_level + i == level) + } + if (first_level + i == level) { break; + } } } - if (closed) - p[i >= wp->w_p_fdc ? i - 1 : i] = '+'; + if (closed) { + p[i >= fdc ? i - 1 : i] = '+'; + } } /* @@ -2163,6 +2185,10 @@ win_line ( int prev_c1 = 0; /* first composing char for prev_c */ int did_line_attr = 0; + bool has_bufhl = false; // this buffer has highlight matches + int bufhl_attr = 0; // attributes desired by bufhl + bufhl_lineinfo_T bufhl_info; // bufhl data for this line + /* draw_state: items that are drawn in sequence: */ #define WL_START 0 /* nothing done yet */ # define WL_CMDLINE WL_START + 1 /* cmdline window column */ @@ -2178,11 +2204,13 @@ win_line ( int syntax_seqnr = 0; int prev_syntax_id = 0; int conceal_attr = hl_attr(HLF_CONCEAL); - int is_concealing = FALSE; - int boguscols = 0; /* nonexistent columns added to force - wrapping */ - int vcol_off = 0; /* offset for concealed characters */ - int did_wcol = FALSE; + int is_concealing = false; + int boguscols = 0; ///< nonexistent columns added to + ///< force wrapping + int vcol_off = 0; ///< offset for concealed characters + int did_wcol = false; + int match_conc = false; ///< cchar for match functions + int has_match_conc = false; ///< match wants to conceal int old_boguscols = 0; # define VCOL_HLC (vcol - vcol_off) # define FIX_FOR_BOGUSCOLS \ @@ -2221,6 +2249,11 @@ win_line ( } } + if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { + has_bufhl = true; + extra_check = true; + } + /* Check for columns to display for 'colorcolumn'. */ color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; if (color_cols != NULL) @@ -2409,13 +2442,18 @@ win_line ( } } - /* find start of trailing whitespace */ - if (wp->w_p_list && lcs_trail) { - trailcol = (colnr_T)STRLEN(ptr); - while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) - --trailcol; - trailcol += (colnr_T) (ptr - line); - extra_check = TRUE; + if (wp->w_p_list) { + if (lcs_space || lcs_trail) { + extra_check = true; + } + // find start of trailing whitespace + if (lcs_trail) { + trailcol = (colnr_T)STRLEN(ptr); + while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) { + trailcol--; + } + trailcol += (colnr_T) (ptr - line); + } } /* @@ -2435,21 +2473,16 @@ win_line ( mb_ptr_adv(ptr); } - /* When: - * - 'cuc' is set, or - * - 'colorcolumn' is set, or - * - 'virtualedit' is set, or - * - the visual mode is active, - * the end of the line may be before the start of the displayed part. - */ - if (vcol < v && ( - wp->w_p_cuc - || draw_color_col - || - virtual_active() - || - (VIsual_active && wp->w_buffer == curwin->w_buffer) - )) { + // When: + // - 'cuc' is set, or + // - 'colorcolumn' is set, or + // - 'virtualedit' is set, or + // - the visual mode is active, + // the end of the line may be before the start of the displayed part. + if (vcol < v && (wp->w_p_cuc + || draw_color_col + || virtual_active() + || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { vcol = v; } @@ -2612,11 +2645,10 @@ win_line ( extra_check = true; } - /* - * Repeat for the whole displayed line. - */ + // Repeat for the whole displayed line. for (;; ) { - /* Skip this quickly when working on the text. */ + has_match_conc = false; + // Skip this quickly when working on the text. if (draw_state != WL_LINE) { if (draw_state == WL_CMDLINE - 1 && n_extra == 0) { draw_state = WL_CMDLINE; @@ -2629,11 +2661,13 @@ win_line ( } if (draw_state == WL_FOLD - 1 && n_extra == 0) { + int fdc = compute_foldcolumn(wp, 0); + draw_state = WL_FOLD; - if (wp->w_p_fdc > 0) { - /* Draw the 'foldcolumn'. */ - fill_foldcolumn(extra, wp, FALSE, lnum); - n_extra = wp->w_p_fdc; + if (fdc > 0) { + // Draw the 'foldcolumn'. + fill_foldcolumn(extra, wp, false, lnum); + n_extra = fdc; p_extra = extra; p_extra[n_extra] = NUL; c_extra = NUL; @@ -2861,8 +2895,16 @@ win_line ( shl->endcol = tmp_col; } shl->attr_cur = shl->attr; + if (cur != NULL && syn_name2id((char_u *)"Conceal") + == cur->hlg_id) { + has_match_conc = true; + match_conc = cur->conceal_char; + } else { + has_match_conc = match_conc = false; + } } else if (v == (long)shl->endcol) { shl->attr_cur = 0; + prev_syntax_id = 0; next_search_hl(wp, shl, lnum, (colnr_T)v, cur); pos_inprogress = !(cur == NULL || cur->pos.cur == 0); @@ -2934,14 +2976,16 @@ win_line ( } } - /* Decide which of the highlight attributes to use. */ - attr_pri = TRUE; - if (area_attr != 0) - char_attr = area_attr; - else if (search_attr != 0) - char_attr = search_attr; - /* Use line_attr when not in the Visual or 'incsearch' area - * (area_attr may be 0 when "noinvcur" is set). */ + // Decide which of the highlight attributes to use. + attr_pri = true; + + if (area_attr != 0) { + char_attr = hl_combine_attr(line_attr, area_attr); + } else if (search_attr != 0) { + char_attr = hl_combine_attr(line_attr, search_attr); + } + // Use line_attr when not in the Visual or 'incsearch' area + // (area_attr may be 0 when "noinvcur" is set). else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL) || vcol < fromcol || vcol_prev < fromcol_prev || vcol >= tocol)) @@ -3176,26 +3220,7 @@ win_line ( } } - ++ptr; - - // 'list': change char 160 to lcs_nbsp and space to lcs_space. - if (wp->w_p_list - && (((c == 160 || (mb_utf8 && mb_c == 160)) && lcs_nbsp) - || (c == ' ' && lcs_space && ptr - line <= trailcol))) { - c = (c == ' ') ? lcs_space : lcs_nbsp; - if (area_attr == 0 && search_attr == 0) { - n_attr = 1; - extra_attr = hl_attr(HLF_8); - saved_attr2 = char_attr; /* save current attr */ - } - mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; - u8cc[0] = 0; - c = 0xc0; - } else - mb_utf8 = FALSE; - } + ptr++; if (extra_check) { bool can_spell = true; @@ -3244,12 +3269,12 @@ win_line ( * contains the @Spell cluster. */ if (has_spell && v >= word_end && v > cur_checked_col) { spell_attr = 0; - if (!attr_pri) + if (!attr_pri) { char_attr = syntax_attr; - if (c != 0 && ( - !has_syntax || - can_spell)) { - char_u *prev_ptr, *p; + } + if (c != 0 && (!has_syntax || can_spell)) { + char_u *prev_ptr; + char_u *p; int len; hlf_T spell_hlf = HLF_COUNT; if (has_mbyte) { @@ -3315,6 +3340,17 @@ win_line ( char_attr = hl_combine_attr(spell_attr, char_attr); } + if (has_bufhl && v > 0) { + bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); + if (bufhl_attr != 0) { + if (!attr_pri) { + char_attr = hl_combine_attr(char_attr, bufhl_attr); + } else { + char_attr = hl_combine_attr(bufhl_attr, char_attr); + } + } + } + if (wp->w_buffer->terminal) { char_attr = hl_combine_attr(char_attr, term_attrs[vcol]); } @@ -3323,16 +3359,15 @@ win_line ( * Found last space before word: check for line break. */ if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr)) { - char_u *p = ptr - ( - has_mbyte ? mb_l : - 1); + int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; + char_u *p = ptr - (mb_off + 1); // TODO: is passing p for start of the line OK? n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1; if (c == TAB && n_extra + col > wp->w_width) { n_extra = (int)wp->w_buffer->b_p_ts - vcol % (int)wp->w_buffer->b_p_ts - 1; } - c_extra = ' '; + c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; if (ascii_iswhite(c)) { if (c == TAB) /* See "Tab alignment" below. */ @@ -3343,6 +3378,28 @@ win_line ( } } + // 'list': change char 160 to lcs_nbsp and space to lcs_space. + if (wp->w_p_list + && (((c == 160 + || (mb_utf8 && (mb_c == 160 || mb_c == 0x202f))) + && lcs_nbsp) + || (c == ' ' && lcs_space && ptr - line <= trailcol))) { + c = (c == ' ') ? lcs_space : lcs_nbsp; + if (area_attr == 0 && search_attr == 0) { + n_attr = 1; + extra_attr = hl_attr(HLF_8); + saved_attr2 = char_attr; // save current attr + } + mb_c = c; + if (enc_utf8 && (*mb_char2len)(c) > 1) { + mb_utf8 = true; + u8cc[0] = 0; + c = 0xc0; + } else { + mb_utf8 = false; + } + } + if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') { c = lcs_trail; if (!attr_pri) { @@ -3388,7 +3445,7 @@ win_line ( int i; int saved_nextra = n_extra; - if ((is_concealing || boguscols > 0) && vcol_off > 0) { + if (vcol_off > 0) { // there are characters to conceal tab_len += vcol_off; } @@ -3418,25 +3475,31 @@ win_line ( // n_extra will be increased by FIX_FOX_BOGUSCOLS // macro below, so need to adjust for that here - if ((is_concealing || boguscols > 0) && vcol_off > 0) { + if (vcol_off > 0) { n_extra -= vcol_off; } } - /* Tab alignment should be identical regardless of - * 'conceallevel' value. So tab compensates of all - * previous concealed characters, and thus resets vcol_off - * and boguscols accumulated so far in the line. Note that - * the tab can be longer than 'tabstop' when there - * are concealed characters. */ - FIX_FOR_BOGUSCOLS; - // Make sure that the highlighting for the tab char will be correctly - // set further below (effectively reverts the FIX_FOR_BOGSUCOLS - // macro). - if (old_boguscols > 0 && n_extra > tab_len && wp->w_p_list - && lcs_tab1) { - tab_len += n_extra - tab_len; + + { + int vc_saved = vcol_off; + + // Tab alignment should be identical regardless of + // 'conceallevel' value. So tab compensates of all + // previous concealed characters, and thus resets + // vcol_off and boguscols accumulated so far in the + // line. Note that the tab can be longer than + // 'tabstop' when there are concealed characters. + FIX_FOR_BOGUSCOLS; + + // Make sure, the highlighting for the tab char will be + // correctly set further below (effectively reverts the + // FIX_FOR_BOGSUCOLS macro. + if (n_extra == tab_len + vc_saved && wp->w_p_list && lcs_tab1) { + tab_len += vc_saved; + } } - mb_utf8 = FALSE; /* don't draw as UTF-8 */ + + mb_utf8 = (int)false; // don't draw as UTF-8 if (wp->w_p_list) { c = lcs_tab1; if (wp->w_p_lbr) { @@ -3458,7 +3521,7 @@ win_line ( c = ' '; } } else if (c == NUL - && ((wp->w_p_list && lcs_eol > 0) + && (wp->w_p_list || ((fromcol >= 0 || fromcol_prev >= 0) && tocol > vcol && VIsual_mode != Ctrl_V @@ -3468,11 +3531,11 @@ win_line ( && !(noinvcur && lnum == wp->w_cursor.lnum && (colnr_T)vcol == wp->w_virtcol))) - && lcs_eol_one >= 0) { - /* Display a '$' after the line or highlight an extra - * character if the line break is included. */ - /* For a diff line the highlighting continues after the - * "$". */ + && lcs_eol_one > 0) { + // Display a '$' after the line or highlight an extra + // character if the line break is included. + // For a diff line the highlighting continues after the + // "$". if (diff_hlf == (hlf_T)0 && line_attr == 0) { /* In virtualedit, visual selections may extend * beyond end of line. */ @@ -3485,10 +3548,11 @@ win_line ( c_extra = NUL; } } - if (wp->w_p_list) + if (wp->w_p_list && lcs_eol > 0) { c = lcs_eol; - else + } else { c = ' '; + } lcs_eol_one = -1; --ptr; /* put it back at the NUL */ if (!attr_pri) { @@ -3539,25 +3603,22 @@ win_line ( wp->w_p_rl ? (col >= 0) : (col < wp->w_width))) { c = ' '; - --ptr; /* put it back at the NUL */ - } else if (( - diff_hlf != (hlf_T)0 || - line_attr != 0 - ) && ( - wp->w_p_rl ? (col >= 0) : - (col - - boguscols - < wp->w_width))) { - /* Highlight until the right side of the window */ + ptr--; // put it back at the NUL + } else if ((diff_hlf != (hlf_T)0 || line_attr != 0) + && (wp->w_p_rl + ? (col >= 0) + : (col - boguscols < wp->w_width))) { + // Highlight until the right side of the window c = ' '; - --ptr; /* put it back at the NUL */ + ptr--; // put it back at the NUL - /* Remember we do the char for line highlighting. */ - ++did_line_attr; + // Remember we do the char for line highlighting. + did_line_attr++; - /* don't do search HL for the rest of the line */ - if (line_attr != 0 && char_attr == search_attr && col > 0) + // don't do search HL for the rest of the line + if (line_attr != 0 && char_attr == search_attr && col > 0) { char_attr = line_attr; + } if (diff_hlf == HLF_TXD) { diff_hlf = HLF_CHD; if (attr == 0 || char_attr != attr) { @@ -3570,24 +3631,28 @@ win_line ( } } - if ( wp->w_p_cole > 0 - && (wp != curwin || lnum != wp->w_cursor.lnum || - conceal_cursor_line(wp)) - && (syntax_flags & HL_CONCEAL) != 0 - && !(lnum_in_visual_area - && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { + if (wp->w_p_cole > 0 + && (wp != curwin || lnum != wp->w_cursor.lnum + || conceal_cursor_line(wp)) + && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc) + && !(lnum_in_visual_area + && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { char_attr = conceal_attr; if (prev_syntax_id != syntax_seqnr - && (syn_get_sub_char() != NUL || wp->w_p_cole == 1) + && (syn_get_sub_char() != NUL || match_conc + || wp->w_p_cole == 1) && wp->w_p_cole != 3) { - /* First time at this concealed item: display one - * character. */ - if (syn_get_sub_char() != NUL) + // First time at this concealed item: display one + // character. + if (match_conc) { + c = match_conc; + } else if (syn_get_sub_char() != NUL) { c = syn_get_sub_char(); - else if (lcs_conceal != NUL) + } else if (lcs_conceal != NUL) { c = lcs_conceal; - else + } else { c = ' '; + } prev_syntax_id = syntax_seqnr; @@ -3628,9 +3693,13 @@ win_line ( && wp == curwin && lnum == wp->w_cursor.lnum && conceal_cursor_line(wp) && (int)wp->w_virtcol <= vcol + n_skip) { - wp->w_wcol = col - boguscols; + if (wp->w_p_rl) { + wp->w_wcol = wp->w_width - col + boguscols - 1; + } else { + wp->w_wcol = col - boguscols; + } wp->w_wrow = row; - did_wcol = TRUE; + did_wcol = true; } /* Don't override visual selection highlighting. */ @@ -3946,20 +4015,24 @@ win_line ( ScreenAttrs[off] = char_attr; if (has_mbyte && (*mb_char2cells)(mb_c) > 1) { - /* Need to fill two screen columns. */ - ++off; - ++col; - if (enc_utf8) - /* UTF-8: Put a 0 in the second screen char. */ + // Need to fill two screen columns. + off++; + col++; + if (enc_utf8) { + // UTF-8: Put a 0 in the second screen char. ScreenLines[off] = 0; - else - /* DBCS: Put second byte in the second screen char. */ + } else { + // DBCS: Put second byte in the second screen char. ScreenLines[off] = mb_c & 0xff; - ++vcol; - /* When "tocol" is halfway through a character, set it to the end of - * the character, otherwise highlighting won't stop. */ - if (tocol == vcol) - ++tocol; + } + if (draw_state > WL_NR && filler_todo <= 0) { + vcol++; + } + // When "tocol" is halfway through a character, set it to the end of + // the character, otherwise highlighting won't stop. + if (tocol == vcol) { + tocol++; + } if (wp->w_p_rl) { /* now it's time to backup one cell */ --off; @@ -4104,7 +4177,6 @@ win_line ( * xterm/screen: write an extra character beyond the end of * the line. This will work with all terminal types * (regardless of the xn,am settings). - * Only do this on a fast tty. * Only do this if the cursor is on the current line * (something has been written in it). * Don't do this for the GUI. @@ -4977,8 +5049,8 @@ win_redr_custom ( char_u *stl; char_u *p; struct stl_hlrec hltab[STL_MAX_ITEM]; - struct stl_hlrec tabtab[STL_MAX_ITEM]; - int use_sandbox = FALSE; + StlClickRecord tabtab[STL_MAX_ITEM]; + int use_sandbox = false; win_T *ewp; int p_crb_save; @@ -5094,20 +5166,24 @@ win_redr_custom ( screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr); if (wp == NULL) { - /* Fill the TabPageIdxs[] array for clicking in the tab pagesline. */ + // Fill the tab_page_click_defs array for clicking in the tab pages line. col = 0; len = 0; p = buf; - fillchar = 0; + StlClickDefinition cur_click_def = { + .type = kStlClickDisabled, + }; for (n = 0; tabtab[n].start != NULL; n++) { - len += vim_strnsize(p, (int)(tabtab[n].start - p)); - while (col < len) - TabPageIdxs[col++] = fillchar; - p = tabtab[n].start; - fillchar = tabtab[n].userhl; + len += vim_strnsize(p, (int)(tabtab[n].start - (char *) p)); + while (col < len) { + tab_page_click_defs[col++] = cur_click_def; + } + p = (char_u *) tabtab[n].start; + cur_click_def = tabtab[n].def; + } + while (col < Columns) { + tab_page_click_defs[col++] = cur_click_def; } - while (col < Columns) - TabPageIdxs[col++] = fillchar; } theend: @@ -5926,9 +6002,9 @@ void screenalloc(bool doclear) sattr_T *new_ScreenAttrs; unsigned *new_LineOffset; char_u *new_LineWraps; - short *new_TabPageIdxs; - static int entered = FALSE; /* avoid recursiveness */ - static int done_outofmem_msg = FALSE; /* did outofmem message */ + StlClickDefinition *new_tab_page_click_defs; + static bool entered = false; // avoid recursiveness + static bool done_outofmem_msg = false; int retry_count = 0; const bool l_enc_utf8 = enc_utf8; const int l_enc_dbcs = enc_dbcs; @@ -6001,7 +6077,8 @@ retry: new_ScreenAttrs = xmalloc((size_t)((Rows + 1) * Columns * sizeof(sattr_T))); new_LineOffset = xmalloc((size_t)(Rows * sizeof(unsigned))); new_LineWraps = xmalloc((size_t)(Rows * sizeof(char_u))); - new_TabPageIdxs = xmalloc((size_t)(Columns * sizeof(short))); + new_tab_page_click_defs = xcalloc( + (size_t) Columns, sizeof(*new_tab_page_click_defs)); FOR_ALL_TAB_WINDOWS(tp, wp) { win_alloc_lines(wp); @@ -6019,7 +6096,7 @@ retry: || new_ScreenAttrs == NULL || new_LineOffset == NULL || new_LineWraps == NULL - || new_TabPageIdxs == NULL + || new_tab_page_click_defs == NULL || outofmem) { if (ScreenLines != NULL || !done_outofmem_msg) { /* guess the size */ @@ -6045,8 +6122,8 @@ retry: new_LineOffset = NULL; xfree(new_LineWraps); new_LineWraps = NULL; - xfree(new_TabPageIdxs); - new_TabPageIdxs = NULL; + xfree(new_tab_page_click_defs); + new_tab_page_click_defs = NULL; } else { done_outofmem_msg = FALSE; @@ -6125,7 +6202,8 @@ retry: ScreenAttrs = new_ScreenAttrs; LineOffset = new_LineOffset; LineWraps = new_LineWraps; - TabPageIdxs = new_TabPageIdxs; + tab_page_click_defs = new_tab_page_click_defs; + tab_page_click_defs_size = Columns; /* It's important that screen_Rows and screen_Columns reflect the actual * size of ScreenLines[]. Set them before calling anything. */ @@ -6164,7 +6242,25 @@ void free_screenlines(void) xfree(ScreenAttrs); xfree(LineOffset); xfree(LineWraps); - xfree(TabPageIdxs); + clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); + xfree(tab_page_click_defs); +} + +/// Clear tab_page_click_defs table +/// +/// @param[out] tpcd Table to clear. +/// @param[in] tpcd_size Size of the table. +void clear_tab_page_click_defs(StlClickDefinition *const tpcd, + const long tpcd_size) +{ + if (tpcd != NULL) { + for (long i = 0; i < tpcd_size; i++) { + if (i == 0 || tpcd[i].func != tpcd[i - 1].func) { + xfree(tpcd[i].func); + } + } + memset(tpcd, 0, (size_t) tpcd_size * sizeof(tpcd[0])); + } } void screenclear(void) @@ -6681,8 +6777,8 @@ int showmode(void) if (Recording && edit_submode == NULL /* otherwise it gets too long */ ) { - MSG_PUTS_ATTR(_("recording"), attr); - need_clear = TRUE; + recording_mode(attr); + need_clear = true; } mode_displayed = TRUE; @@ -6721,26 +6817,33 @@ static void msg_pos_mode(void) msg_row = Rows - 1; } -/* - * Delete mode message. Used when ESC is typed which is expected to end - * Insert mode (but Insert mode didn't end yet!). - * Caller should check "mode_displayed". - */ -void unshowmode(int force) +/// Delete mode message. Used when ESC is typed which is expected to end +/// Insert mode (but Insert mode didn't end yet!). +/// Caller should check "mode_displayed". +void unshowmode(bool force) { - /* - * Don't delete it right now, when not redrawing or inside a mapping. - */ - if (!redrawing() || (!force && char_avail() && !KeyTyped)) - redraw_cmdline = TRUE; /* delete mode later */ - else { + // Don't delete it right now, when not redrawing or inside a mapping. + if (!redrawing() || (!force && char_avail() && !KeyTyped)) { + redraw_cmdline = true; // delete mode later + } else { msg_pos_mode(); - if (Recording) - MSG_PUTS_ATTR(_("recording"), hl_attr(HLF_CM)); + if (Recording) { + recording_mode(hl_attr(HLF_CM)); + } msg_clr_eos(); } } +static void recording_mode(int attr) +{ + MSG_PUTS_ATTR(_("recording"), attr); + if (!shortmess(SHM_RECORDING)) { + char_u s[4]; + vim_snprintf((char *)s, ARRAY_SIZE(s), " @%c", Recording); + MSG_PUTS_ATTR(s, attr); + } +} + /* * Draw the tab pages line at the top of the Vim window. */ @@ -6772,9 +6875,9 @@ static void draw_tabline(void) return; - /* Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. */ - for (scol = 0; scol < Columns; ++scol) - TabPageIdxs[scol] = 0; + // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. + assert(Columns == tab_page_click_defs_size); + clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); /* Use the 'tabline' option if it's set. */ if (*p_tal != NUL) { @@ -6872,11 +6975,16 @@ static void draw_tabline(void) } screen_putchar(' ', 0, col++, attr); - /* Store the tab page number in TabPageIdxs[], so that - * jump_to_mouse() knows where each one is. */ - ++tabcount; - while (scol < col) - TabPageIdxs[scol++] = tabcount; + // Store the tab page number in tab_page_click_defs[], so that + // jump_to_mouse() knows where each one is. + tabcount++; + while (scol < col) { + tab_page_click_defs[scol++] = (StlClickDefinition) { + .type = kStlClickTabSwitch, + .tabnr = tabcount, + .func = NULL, + }; + } } if (use_sep_chars) @@ -6888,7 +6996,11 @@ static void draw_tabline(void) /* Put an "X" for closing the current tab if there are several. */ if (first_tabpage->tp_next != NULL) { screen_putchar('X', 0, (int)Columns - 1, attr_nosel); - TabPageIdxs[Columns - 1] = -999; + tab_page_click_defs[Columns - 1] = (StlClickDefinition) { + .type = kStlClickTabClose, + .tabnr = 999, + .func = NULL, + }; } } diff --git a/src/nvim/screen.h b/src/nvim/screen.h index debf86ae67..81a8b9ed4c 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -16,6 +16,29 @@ #define NOT_VALID 40 /* buffer needs complete redraw */ #define CLEAR 50 /* screen messed up, clear it */ +/// Status line click definition +typedef struct { + enum { + kStlClickDisabled = 0, ///< Clicks to this area are ignored. + kStlClickTabSwitch, ///< Switch to the given tab. + kStlClickTabClose, ///< Close given tab. + kStlClickFuncRun, ///< Run user function. + } type; ///< Type of the click. + int tabnr; ///< Tab page number. + char *func; ///< Function to run. +} StlClickDefinition; + +/// Used for tabline clicks +typedef struct { + StlClickDefinition def; ///< Click definition. + const char *start; ///< Location where region starts. +} StlClickRecord; + +/// Array defining what should be done when tabline is clicked +extern StlClickDefinition *tab_page_click_defs; + +/// Size of the tab_page_click_defs array +extern long tab_page_click_defs_size; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.h.generated.h" diff --git a/src/nvim/search.c b/src/nvim/search.c index cb461c9ef2..6e2b69fff7 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -453,25 +453,24 @@ void last_pat_prog(regmmatch_T *regmatch) --emsg_off; } -/* - * lowest level search function. - * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. - * Start at position 'pos' and return the found position in 'pos'. - * - * if (options & SEARCH_MSG) == 0 don't give any messages - * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages - * if (options & SEARCH_MSG) == SEARCH_MSG give all messages - * if (options & SEARCH_HIS) put search pattern in history - * if (options & SEARCH_END) return position at end of match - * if (options & SEARCH_START) accept match at pos itself - * if (options & SEARCH_KEEP) keep previous search pattern - * if (options & SEARCH_FOLD) match only once in a closed fold - * if (options & SEARCH_PEEK) check for typed char, cancel search - * - * Return FAIL (zero) for failure, non-zero for success. - * Returns the index of the first matching - * subpattern plus one; one if there was none. - */ +/// lowest level search function. +/// Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. +/// Start at position 'pos' and return the found position in 'pos'. +/// +/// if (options & SEARCH_MSG) == 0 don't give any messages +/// if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages +/// if (options & SEARCH_MSG) == SEARCH_MSG give all messages +/// if (options & SEARCH_HIS) put search pattern in history +/// if (options & SEARCH_END) return position at end of match +/// if (options & SEARCH_START) accept match at pos itself +/// if (options & SEARCH_KEEP) keep previous search pattern +/// if (options & SEARCH_FOLD) match only once in a closed fold +/// if (options & SEARCH_PEEK) check for typed char, cancel search +/// if (options & SEARCH_COL) start at pos->col instead of zero +/// +/// @returns FAIL (zero) for failure, non-zero for success. +/// the index of the first matching +/// subpattern plus one; one if there was none. int searchit( win_T *win, /* window to search in, can be NULL for a buffer without a window! */ @@ -497,6 +496,7 @@ int searchit( pos_T start_pos; int at_first_line; int extra_col; + int start_char_len; int match_ok; long nmatched; int submatch = 0; @@ -519,16 +519,21 @@ int searchit( // When not accepting a match at the start position set "extra_col" to a // non-zero value. Don't do that when starting at MAXCOL, since MAXCOL + 1 // is zero. - if ((options & SEARCH_START) || pos->col == MAXCOL) { - extra_col = 0; - } else if (dir != BACKWARD && has_mbyte - && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count - && pos->col < MAXCOL - 2) { + if (pos->col == MAXCOL) { + start_char_len = 0; + } else if (has_mbyte + && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count + && pos->col < MAXCOL - 2) { // Watch out for the "col" being MAXCOL - 2, used in a closed fold. - ptr = ml_get_buf(buf, pos->lnum, FALSE) + pos->col; - extra_col = *ptr == NUL ? 1 : (*mb_ptr2len)(ptr); + ptr = ml_get_buf(buf, pos->lnum, false) + pos->col; + start_char_len = *ptr == NUL ? 1 : (*mb_ptr2len)(ptr); } else { - extra_col = 1; + start_char_len = 1; + } + if (dir == FORWARD) { + extra_col = (options & SEARCH_START) ? 0 : start_char_len; + } else { + extra_col = (options & SEARCH_START) ? start_char_len : 0; } start_pos = *pos; /* remember start pos for detecting no match */ @@ -565,16 +570,14 @@ int searchit( if (tm != NULL && profile_passed_limit(*tm)) break; - /* - * Look for a match somewhere in line "lnum". - */ + // Look for a match somewhere in line "lnum". + colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; nmatched = vim_regexec_multi(®match, win, buf, - lnum, (colnr_T)0, - tm - ); - /* Abort searching on an error (e.g., out of stack). */ - if (called_emsg) + lnum, col, tm); + // Abort searching on an error (e.g., out of stack). + if (called_emsg) { break; + } if (nmatched > 0) { /* match may actually be in another line when using \zs */ matchpos = regmatch.startpos[0]; @@ -621,43 +624,39 @@ int searchit( break; } matchcol = endpos.col; - /* for empty match: advance one char */ - if (matchcol == matchpos.col - && ptr[matchcol] != NUL) { - if (has_mbyte) - matchcol += - (*mb_ptr2len)(ptr + matchcol); - else - ++matchcol; - } + // for empty match (matchcol == matchpos.col): advance one char } else { + // Prepare to start after first matched character. matchcol = matchpos.col; - if (ptr[matchcol] != NUL) { - if (has_mbyte) - matchcol += (*mb_ptr2len)(ptr - + matchcol); - else - ++matchcol; - } } - if (matchcol == 0 && (options & SEARCH_START)) + + if (matchcol == matchpos.col && ptr[matchcol] != NUL) { + matchcol += MB_PTR2LEN(ptr + matchcol); + } + + if (matchcol == 0 && (options & SEARCH_START)) { break; - if (STRLEN(ptr) <= (size_t)matchcol || ptr[matchcol] == NUL - || (nmatched = vim_regexec_multi(®match, - win, buf, lnum + matchpos.lnum, - matchcol, - tm - )) == 0) { - match_ok = FALSE; + } + + if (ptr[matchcol] == NUL + || (nmatched = vim_regexec_multi(®match, win, buf, lnum, + matchcol, tm)) == 0) { + match_ok = false; break; } matchpos = regmatch.startpos[0]; endpos = regmatch.endpos[0]; submatch = first_submatch(®match); - /* Need to get the line pointer again, a - * multi-line search may have made it invalid. */ - ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE); + // This while-loop only works with matchpos.lnum == 0. + // For bigger values the next line pointer ptr might not be a + // buffer line. + if (matchpos.lnum != 0) { + break; + } + // Need to get the line pointer again, a multi-line search may + // have made it invalid. + ptr = ml_get_buf(buf, lnum, false); } if (!match_ok) continue; @@ -683,16 +682,14 @@ int searchit( || (lnum + regmatch.endpos[0].lnum == start_pos.lnum && (int)regmatch.endpos[0].col - 1 - + extra_col - <= (int)start_pos.col)) + < (int)start_pos.col + extra_col)) : (lnum + regmatch.startpos[0].lnum < start_pos.lnum || (lnum + regmatch.startpos[0].lnum == start_pos.lnum && (int)regmatch.startpos[0].col - + extra_col - <= (int)start_pos.col)))) { - match_ok = TRUE; + < (int)start_pos.col + extra_col)))) { + match_ok = true; matchpos = regmatch.startpos[0]; endpos = regmatch.endpos[0]; submatch = first_submatch(®match); @@ -881,9 +878,8 @@ static void set_vv_searchforward(void) set_vim_var_nr(VV_SEARCHFORWARD, (long)(spats[0].off.dir == '/')); } -/* - * Return the number of the first subpat that matched. - */ +// Return the number of the first subpat that matched. +// Return zero if none of them matched. static int first_submatch(regmmatch_T *rp) { int submatch; @@ -918,7 +914,7 @@ static int first_submatch(regmmatch_T *rp) * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this * makes the movement linewise without moving the match position. * - * return 0 for failure, 1 for found, 2 for found and line offset added + * Return 0 for failure, 1 for found, 2 for found and line offset added. */ int do_search( oparg_T *oap, /* can be NULL */ @@ -1031,19 +1027,18 @@ int do_search( spats[0].off.line = FALSE; spats[0].off.end = FALSE; spats[0].off.off = 0; - /* - * Check for a line offset or a character offset. - * For get_address (echo off) we don't check for a character - * offset, because it is meaningless and the 's' could be a - * substitute command. - */ - if (*p == '+' || *p == '-' || ascii_isdigit(*p)) - spats[0].off.line = TRUE; - else if ((options & SEARCH_OPT) && - (*p == 'e' || *p == 's' || *p == 'b')) { - if (*p == 'e') /* end */ + // Check for a line offset or a character offset. + // For get_address (echo off) we don't check for a character + // offset, because it is meaningless and the 's' could be a + // substitute command. + if (*p == '+' || *p == '-' || ascii_isdigit(*p)) { + spats[0].off.line = true; + } else if ((options & SEARCH_OPT) + && (*p == 'e' || *p == 's' || *p == 'b')) { + if (*p == 'e') { // end spats[0].off.end = true; - ++p; + } + p++; } if (ascii_isdigit(*p) || *p == '+' || *p == '-') { /* got an offset */ /* 'nr' or '+nr' or '-nr' */ @@ -1443,19 +1438,64 @@ static int check_prevcol(char_u *linep, int col, int ch, int *prevcol) } /* + * Raw string start is found at linep[startpos.col - 1]. + * Return true if the matching end can be found between startpos and endpos. + */ +static int find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos) +{ + char_u *p; + char_u *delim_copy; + size_t delim_len; + linenr_T lnum; + int found = false; + + for (p = linep + startpos->col + 1; *p && *p != '('; ++p) {} + + delim_len = (p - linep) - startpos->col - 1; + delim_copy = vim_strnsave(linep + startpos->col + 1, delim_len); + if (delim_copy == NULL) + return false; + for (lnum = startpos->lnum; lnum <= endpos->lnum; ++lnum) + { + char_u *line = ml_get(lnum); + + for (p = line + (lnum == startpos->lnum + ? startpos->col + 1 : 0); *p; ++p) + { + if (lnum == endpos->lnum && (colnr_T)(p - line) >= endpos->col) + break; + if (*p == ')' && p[delim_len + 1] == '"' + && STRNCMP(delim_copy, p + 1, delim_len) == 0) + { + found = true; + break; + } + } + if (found) + break; + } + xfree(delim_copy); + return found; +} + +/* * findmatchlimit -- find the matching paren or brace, if it exists within - * maxtravel lines of here. A maxtravel of 0 means search until falling off - * the edge of the file. + * maxtravel lines of the cursor. A maxtravel of 0 means search until falling + * off the edge of the file. * * "initc" is the character to find a match for. NUL means to find the - * character at or after the cursor. + * character at or after the cursor. Special values: + * '*' look for C-style comment / * + * '/' look for C-style comment / *, ignoring comment-end + * '#' look for preprocessor directives + * 'R' look for raw string start: R"delim(text)delim" (only backwards) * * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#') * FM_FORWARD search forwards (when initc is '/', '*' or '#') * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0) * FM_SKIPCOMM skip comments (not implemented yet!) * - * "oap" is only used to set oap->motion_type for a linewise motion, it be + * "oap" is only used to set oap->motion_type for a linewise motion, it can be * NULL */ @@ -1465,8 +1505,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) int findc = 0; /* matching brace */ int c; int count = 0; /* cumulative number of braces */ - int backwards = FALSE; /* init for gcc */ - int inquote = FALSE; /* TRUE when inside quotes */ + int backwards = false; /* init for gcc */ + int raw_string = false; /* search for raw string */ + int inquote = false; /* true when inside quotes */ char_u *linep; /* pointer to current line */ char_u *ptr; int do_quotes; /* check for quotes in current line */ @@ -1506,22 +1547,22 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) * When '/' is used, we ignore running backwards into a star-slash, for * "[*" command, we just want to find any comment. */ - if (initc == '/' || initc == '*') { + if (initc == '/' || initc == '*' || initc == 'R') { comment_dir = dir; if (initc == '/') - ignore_cend = TRUE; - backwards = (dir == FORWARD) ? FALSE : TRUE; + ignore_cend = true; + backwards = (dir == FORWARD) ? false : true; + raw_string = (initc == 'R'); initc = NUL; } else if (initc != '#' && initc != NUL) { find_mps_values(&initc, &findc, &backwards, TRUE); if (findc == NUL) return NULL; - } - /* - * Either initc is '#', or no initc was given and we need to look under the - * cursor. - */ - else { + } else { + /* + * Either initc is '#', or no initc was given and we need to look + * under the cursor. + */ if (initc == '#') { hash_dir = dir; } else { @@ -1605,8 +1646,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) /* * Look for matching #if, #else, #elif, or #endif */ - if (oap != NULL) - oap->motion_type = MLINE; /* Linewise for this case only */ + if (oap != NULL) { + oap->motion_type = kMTLineWise; // Linewise for this case only + } if (initc != '#') { ptr = skipwhite(skipwhite(linep) + 1); if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) @@ -1741,14 +1783,13 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } } - /* - * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. - */ - if (pos.col == 0 && (flags & FM_BLOCKSTOP) && - (linep[0] == '{' || linep[0] == '}')) { - if (linep[0] == findc && count == 0) /* match! */ + // If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. + if (pos.col == 0 && (flags & FM_BLOCKSTOP) + && (linep[0] == '{' || linep[0] == '}')) { + if (linep[0] == findc && count == 0) { // match! return &pos; - break; /* out of scope */ + } + break; // out of scope } if (comment_dir) { @@ -1766,7 +1807,26 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) */ if (pos.col == 0) continue; - else if ( linep[pos.col - 1] == '/' + else if (raw_string) + { + if (linep[pos.col - 1] == 'R' + && linep[pos.col] == '"' + && vim_strchr(linep + pos.col + 1, '(') != NULL) + { + /* Possible start of raw string. Now that we have the + * delimiter we can check if it ends before where we + * started searching, or before the previously found + * raw string start. */ + if (!find_rawstring_end(linep, &pos, + count > 0 ? &match_pos : &curwin->w_cursor)) + { + count++; + match_pos = pos; + match_pos.col--; + } + linep = ml_get(pos.lnum); /* may have been released */ + } + } else if ( linep[pos.col - 1] == '/' && linep[pos.col] == '*' && (int)pos.col < comment_col) { count++; @@ -1898,15 +1958,15 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) if (linep[pos.col - 2] == '\'') { pos.col -= 2; break; - } else if (linep[pos.col - 2] == '\\' && - pos.col > 2 && linep[pos.col - 3] == '\'') { + } else if (linep[pos.col - 2] == '\\' + && pos.col > 2 && linep[pos.col - 3] == '\'') { pos.col -= 3; break; } } - } else if (linep[pos.col + 1]) { /* forward search */ - if (linep[pos.col + 1] == '\\' && - linep[pos.col + 2] && linep[pos.col + 3] == '\'') { + } else if (linep[pos.col + 1]) { // forward search + if (linep[pos.col + 1] == '\\' + && linep[pos.col + 2] && linep[pos.col + 3] == '\'') { pos.col += 3; break; } else if (linep[pos.col + 2] == '\'') { @@ -2123,30 +2183,32 @@ int findsent(int dir, long count) * if on an empty line, skip up to a non-empty line */ if (gchar_pos(&pos) == NUL) { - do - if ((*func)(&pos) == -1) + do { + if ((*func)(&pos) == -1) { break; - while (gchar_pos(&pos) == NUL); - if (dir == FORWARD) + } + } while (gchar_pos(&pos) == NUL); + if (dir == FORWARD) { goto found; - } - /* - * if on the start of a paragraph or a section and searching forward, - * go to the next line - */ - else if (dir == FORWARD && pos.col == 0 && - startPS(pos.lnum, NUL, FALSE)) { - if (pos.lnum == curbuf->b_ml.ml_line_count) + } + // if on the start of a paragraph or a section and searching forward, + // go to the next line + } else if (dir == FORWARD && pos.col == 0 + && startPS(pos.lnum, NUL, false)) { + if (pos.lnum == curbuf->b_ml.ml_line_count) { return FAIL; - ++pos.lnum; + } + pos.lnum++; goto found; - } else if (dir == BACKWARD) + } else if (dir == BACKWARD) { decl(&pos); + } - /* go back to the previous non-blank char */ - found_dot = FALSE; - while ((c = gchar_pos(&pos)) == ' ' || c == '\t' || - (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) { + // go back to the previous non-blank char + found_dot = false; + while ((c = gchar_pos(&pos)) == ' ' || c == '\t' + || (dir == BACKWARD + && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) { if (vim_strchr((char_u *)".!?", c) != NULL) { /* Only skip over a '.', '!' and '?' once. */ if (found_dot) @@ -2310,12 +2372,14 @@ int startPS(linenr_T lnum, int para, int both) char_u *s; s = ml_get(lnum); - if (*s == para || *s == '\f' || (both && *s == '}')) - return TRUE; - if (*s == '.' && (inmacro(p_sections, s + 1) || - (!para && inmacro(p_para, s + 1)))) - return TRUE; - return FALSE; + if (*s == para || *s == '\f' || (both && *s == '}')) { + return true; + } + if (*s == '.' && (inmacro(p_sections, s + 1) + || (!para && inmacro(p_para, s + 1)))) { + return true; + } + return false; } /* @@ -2732,7 +2796,7 @@ current_word ( redraw_curbuf_later(INVERTED); /* update the inversion */ } else { oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } --count; } @@ -2967,7 +3031,7 @@ extend: else oap->inclusive = false; oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } return OK; } @@ -3015,18 +3079,18 @@ current_block ( } else old_end = VIsual; - /* - * Search backwards for unclosed '(', '{', etc.. - * Put this position in start_pos. - * Ignore quotes here. - */ + // Search backwards for unclosed '(', '{', etc.. + // Put this position in start_pos. + // Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the + // user wants. save_cpo = p_cpo; - p_cpo = (char_u *)"%"; + p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%"); while (count-- > 0) { - if ((pos = findmatch(NULL, what)) == NULL) + if ((pos = findmatch(NULL, what)) == NULL) { break; + } curwin->w_cursor = *pos; - start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */ + start_pos = *pos; // the findmatch for end_pos will overwrite *pos } p_cpo = save_cpo; @@ -3079,17 +3143,19 @@ current_block ( } if (VIsual_active) { - if (*p_sel == 'e') - ++curwin->w_cursor.col; - if (sol && gchar_cursor() != NUL) - inc(&curwin->w_cursor); /* include the line break */ + if (*p_sel == 'e') { + inc(&curwin->w_cursor); + } + if (sol && gchar_cursor() != NUL) { + inc(&curwin->w_cursor); // include the line break + } VIsual = start_pos; VIsual_mode = 'v'; redraw_curbuf_later(INVERTED); /* update the inversion */ showmode(); } else { oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; oap->inclusive = false; if (sol) incl(&curwin->w_cursor); @@ -3193,6 +3259,7 @@ current_tagblock ( int do_include = include; bool save_p_ws = p_ws; int retval = FAIL; + int is_inclusive = true; p_ws = false; @@ -3287,9 +3354,16 @@ again: if (inc_cursor() < 0) break; } else { - /* Exclude the '<' of the end tag. */ - if (*get_cursor_pos_ptr() == '<') + char_u *c = get_cursor_pos_ptr(); + // Exclude the '<' of the end tag. + // If the closing tag is on new line, do not decrement cursor, but make + // operation exclusive, so that the linefeed will be selected + if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) { + // do not decrement cursor + is_inclusive = false; + } else if (*c == '<') { dec_cursor(); + } } end_pos = curwin->w_cursor; @@ -3328,14 +3402,15 @@ again: showmode(); } else { oap->start = start_pos; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; if (lt(end_pos, start_pos)) { /* End is before the start: there is no text between tags; operate * on an empty area. */ curwin->w_cursor = start_pos; oap->inclusive = false; - } else - oap->inclusive = true; + } else { + oap->inclusive = is_inclusive; + } } retval = OK; @@ -3492,7 +3567,7 @@ extend: } else { oap->start.lnum = start_lnum; oap->start.col = 0; - oap->motion_type = MLINE; + oap->motion_type = kMTLineWise; } curwin->w_cursor.lnum = end_lnum; curwin->w_cursor.col = 0; @@ -3734,7 +3809,7 @@ current_quote ( } } else { oap->start = curwin->w_cursor; - oap->motion_type = MCHAR; + oap->motion_type = kMTCharWise; } /* Set end position. */ @@ -4078,19 +4153,20 @@ find_pattern_in_path ( FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL); already_searched = FALSE; if (new_fname != NULL) { - /* Check whether we have already searched in this file */ + // Check whether we have already searched in this file for (i = 0;; i++) { - if (i == depth + 1) + if (i == depth + 1) { i = old_files; - if (i == max_path_depth) + } + if (i == max_path_depth) { break; - if (path_full_compare(new_fname, files[i].name, TRUE) & kEqualFiles) { - if (type != CHECK_PATH && - action == ACTION_SHOW_ALL && files[i].matched) { - msg_putchar('\n'); /* cursor below last one */ - if (!got_int) { /* don't display if 'q' - typed at "--more--" - message */ + } + if (path_full_compare(new_fname, files[i].name, true) & kEqualFiles) { + if (type != CHECK_PATH + && action == ACTION_SHOW_ALL && files[i].matched) { + msg_putchar('\n'); // cursor below last one */ + if (!got_int) { // don't display if 'q' typed at "--more--" + // message msg_home_replace_hl(new_fname); MSG_PUTS(_(" (includes previously listed match)")); prev_fname = NULL; @@ -4105,15 +4181,15 @@ find_pattern_in_path ( } if (type == CHECK_PATH && (action == ACTION_SHOW_ALL - || (new_fname == NULL && - !already_searched))) { - if (did_show) - msg_putchar('\n'); /* cursor below last one */ - else { - gotocmdline(TRUE); /* cursor at status line */ + || (new_fname == NULL && !already_searched))) { + if (did_show) { + msg_putchar('\n'); // cursor below last one + } else { + gotocmdline(true); // cursor at status line MSG_PUTS_TITLE(_("--- Included files ")); - if (action != ACTION_SHOW_ALL) + if (action != ACTION_SHOW_ALL) { MSG_PUTS_TITLE(_("not found ")); + } MSG_PUTS_TITLE(_("in path ---\n")); } did_show = TRUE; @@ -4272,16 +4348,15 @@ search_line: && vim_regexec(®match, line, (colnr_T)(p - line))) { matched = TRUE; startp = regmatch.startp[0]; - /* - * Check if the line is not a comment line (unless we are - * looking for a define). A line starting with "# define" - * is not considered to be a comment line. - */ + // Check if the line is not a comment line (unless we are + // looking for a define). A line starting with "# define" + // is not considered to be a comment line. if (skip_comments) { - if ((*line != '#' || - STRNCMP(skipwhite(line + 1), "define", 6) != 0) - && get_leader_len(line, NULL, FALSE, TRUE)) - matched = FALSE; + if ((*line != '#' + || STRNCMP(skipwhite(line + 1), "define", 6) != 0) + && get_leader_len(line, NULL, false, true)) { + matched = false; + } /* * Also check for a "/ *" or "/ /" before the match. diff --git a/src/nvim/search.h b/src/nvim/search.h index 6947f79d49..d4e40cb287 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -15,19 +15,20 @@ #define ACTION_SHOW_ALL 4 #define ACTION_EXPAND 5 -/* Values for 'options' argument in do_search() and searchit() */ -#define SEARCH_REV 0x01 /* go in reverse of previous dir. */ -#define SEARCH_ECHO 0x02 /* echo the search command and handle options */ -#define SEARCH_MSG 0x0c /* give messages (yes, it's not 0x04) */ -#define SEARCH_NFMSG 0x08 /* give all messages except not found */ -#define SEARCH_OPT 0x10 /* interpret optional flags */ -#define SEARCH_HIS 0x20 /* put search pattern in history */ -#define SEARCH_END 0x40 /* put cursor at end of match */ -#define SEARCH_NOOF 0x80 /* don't add offset to position */ -#define SEARCH_START 0x100 /* start search without col offset */ -#define SEARCH_MARK 0x200 /* set previous context mark */ -#define SEARCH_KEEP 0x400 /* keep previous search pattern */ -#define SEARCH_PEEK 0x800 /* peek for typed char, cancel search */ +// Values for 'options' argument in do_search() and searchit() +#define SEARCH_REV 0x01 ///< go in reverse of previous dir. +#define SEARCH_ECHO 0x02 ///< echo the search command and handle options +#define SEARCH_MSG 0x0c ///< give messages (yes, it's not 0x04) +#define SEARCH_NFMSG 0x08 ///< give all messages except not found +#define SEARCH_OPT 0x10 ///< interpret optional flags +#define SEARCH_HIS 0x20 ///< put search pattern in history +#define SEARCH_END 0x40 ///< put cursor at end of match +#define SEARCH_NOOF 0x80 ///< don't add offset to position +#define SEARCH_START 0x100 ///< start search without col offset +#define SEARCH_MARK 0x200 ///< set previous context mark +#define SEARCH_KEEP 0x400 ///< keep previous search pattern +#define SEARCH_PEEK 0x800 ///< peek for typed char, cancel search +#define SEARCH_COL 0x1000 ///< start at specified column instead of zero /* Values for flags argument for findmatchlimit() */ #define FM_BACKWARD 0x01 /* search backwards */ diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 340c14066a..51c8597d53 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1,8 +1,3 @@ -#ifdef HAVE_BE64TOH -# define _BSD_SOURCE 1 -# define _DEFAULT_SOURCE 1 -# include <endian.h> -#endif #include <stdlib.h> #include <stddef.h> #include <stdbool.h> @@ -10,9 +5,7 @@ #include <stdint.h> #include <inttypes.h> #include <errno.h> -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif +#include <fcntl.h> #include <assert.h> #include <msgpack.h> @@ -38,7 +31,6 @@ #include "nvim/misc2.h" #include "nvim/ex_getln.h" #include "nvim/search.h" -#include "nvim/eval.h" #include "nvim/regexp.h" #include "nvim/eval_defs.h" #include "nvim/version.h" @@ -46,9 +38,17 @@ #include "nvim/fileio.h" #include "nvim/strings.h" #include "nvim/quickfix.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/decode.h" #include "nvim/lib/khash.h" #include "nvim/lib/kvec.h" +#ifdef HAVE_BE64TOH +# define _BSD_SOURCE 1 +# define _DEFAULT_SOURCE 1 +# include ENDIAN_INCLUDE_FILE +#endif + // Note: when using bufset hash pointers are intentionally casted to uintptr_t // and not to khint32_t or khint64_t: this way compiler must give a warning // (-Wconversion) when types change. @@ -66,9 +66,6 @@ KHASH_SET_INIT_STR(strset) ((char *) copy_option_part((char_u **) src, (char_u *) dest, __VA_ARGS__)) #define find_shada_parameter(...) \ ((const char *) find_shada_parameter(__VA_ARGS__)) -#define emsg2(a, b) emsg2((char_u *) a, (char_u *) b) -#define emsg3(a, b, c) emsg3((char_u *) a, (char_u *) b, (char_u *) c) -#define emsgu(a, ...) emsgu((char_u *) a, __VA_ARGS__) #define home_replace_save(a, b) \ ((char *)home_replace_save(a, (char_u *)b)) #define home_replace(a, b, c, d, e) \ @@ -122,7 +119,7 @@ KHASH_SET_INIT_STR(strset) // E576: Missing '>' // E577: Illegal register name // E886: Can't rename viminfo file to %s! -// Now only five of them are used: +// Now only six of them are used: // E137: ShaDa file is not writeable (for pre-open checks) // E138: All %s.tmp.X files exist, cannot write ShaDa file! // RCERR (E576) for critical read errors. @@ -130,6 +127,7 @@ KHASH_SET_INIT_STR(strset) // RERR (E575) for various errors inside read ShaDa file. // SERR (E886) for various “system†errors (always contains output of // strerror) +// WERR (E574) for various ignorable write errors /// Common prefix for all errors inside ShaDa file /// @@ -148,6 +146,9 @@ KHASH_SET_INIT_STR(strset) /// Common prefix for all “rename†errors #define RNERR "E136: " +/// Common prefix for all ignorable “write†errors +#define WERR "E574: " + /// Flags for shada_read_file and children typedef enum { kShaDaWantInfo = 1, ///< Load non-mark information @@ -198,6 +199,9 @@ typedef enum { ///< a ShaDa file. kSDWriteFailed, ///< Writing was not successfull (e.g. because there ///< was no space left on device). + kSDWriteIgnError, ///< Writing resulted in a error which can be ignored + ///< (e.g. when trying to dump a function reference or + ///< self-referencing container in a variable). } ShaDaWriteResult; /// Flags for shada_read_next_item @@ -279,7 +283,7 @@ typedef struct { } history_item; struct reg { char name; - uint8_t type; + MotionType type; char **contents; size_t contents_size; size_t width; @@ -471,7 +475,7 @@ static const ShadaEntry sd_default_values[] = { .additional_elements = NULL), DEF_SDE(Register, reg, .name = NUL, - .type = MCHAR, + .type = kMTCharWise, .contents = NULL, .contents_size = 0, .width = 0, @@ -754,9 +758,9 @@ static void close_sd_writer(ShaDaWriteDef *const sd_writer) FUNC_ATTR_NONNULL_ALL { const int fd = (int)(intptr_t) sd_writer->cookie; - if (fsync(fd) < 0) { - emsg2(_(SERR "System error while synchronizing ShaDa file: %s"), - strerror(errno)); + if (os_fsync(fd) < 0) { + emsgf(_(SERR "System error while synchronizing ShaDa file: %s"), + os_strerror(errno)); errno = 0; } close_file(fd); @@ -805,11 +809,11 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, { if (sd_reader->skip(sd_reader, offset) != OK) { if (sd_reader->error != NULL) { - emsg2(_(SERR "System error while skipping in ShaDa file: %s"), + emsgf(_(SERR "System error while skipping in ShaDa file: %s"), sd_reader->error); return kSDReadStatusReadError; } else if (sd_reader->eof) { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "last entry specified that it occupies %" PRIu64 " bytes, " "but file ended earlier"), (uint64_t) offset); @@ -843,7 +847,7 @@ open_file_start: goto open_file_start; } if (fd != UV_EEXIST) { - emsg3(_(SERR "System error while opening ShaDa file %s: %s"), + emsgf(_(SERR "System error while opening ShaDa file %s: %s"), fname, os_strerror(fd)); } return fd; @@ -891,7 +895,7 @@ close_file_start: errno = 0; goto close_file_start; } else { - emsg2(_(SERR "System error while closing ShaDa file: %s"), + emsgf(_(SERR "System error while closing ShaDa file: %s"), strerror(errno)); errno = 0; } @@ -928,7 +932,7 @@ static int msgpack_sd_writer_write(void *data, const char *buf, size_t len) ShaDaWriteDef *const sd_writer = (ShaDaWriteDef *) data; ptrdiff_t written_bytes = sd_writer->write(sd_writer, buf, len); if (written_bytes == -1) { - emsg2(_(SERR "System error while writing ShaDa file: %s"), + emsgf(_(SERR "System error while writing ShaDa file: %s"), sd_writer->error); return -1; } @@ -975,7 +979,7 @@ static int shada_read_file(const char *const file, const int flags) if (of_ret != 0) { if (of_ret == UV_ENOENT && (flags & kShaDaMissingError)) { - emsg3(_(SERR "System error while opening ShaDa file %s for reading: %s"), + emsgf(_(SERR "System error while opening ShaDa file %s for reading: %s"), fname, os_strerror(of_ret)); } xfree(fname); @@ -1403,9 +1407,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) break; } case kSDItemRegister: { - if (cur_entry.data.reg.type != MCHAR - && cur_entry.data.reg.type != MLINE - && cur_entry.data.reg.type != MBLOCK) { + if (cur_entry.data.reg.type != kMTCharWise + && cur_entry.data.reg.type != kMTLineWise + && cur_entry.data.reg.type != kMTBlockWise) { shada_free_shada_entry(&cur_entry); break; } @@ -1666,25 +1670,30 @@ static char *shada_filename(const char *file) /// @param[in] entry Entry written. /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// restrictions. -static bool shada_pack_entry(msgpack_packer *const packer, - ShadaEntry entry, - const size_t max_kbyte) +/// +/// @return kSDWriteSuccessfull, kSDWriteFailed or kSDWriteIgnError. +static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, + ShadaEntry entry, + const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL { + ShaDaWriteResult ret = kSDWriteFailed; msgpack_sbuffer sbuf; msgpack_sbuffer_init(&sbuf); msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write); -#define DUMP_ADDITIONAL_ELEMENTS(src) \ +#define DUMP_ADDITIONAL_ELEMENTS(src, what) \ do { \ if ((src) != NULL) { \ for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \ - if (vim_to_msgpack(spacker, &li->li_tv) == FAIL) { \ + if (encode_vim_to_msgpack(spacker, &li->li_tv, \ + _("additional elements of ShaDa " what)) \ + == FAIL) { \ goto shada_pack_entry_error; \ } \ } \ } \ } while (0) -#define DUMP_ADDITIONAL_DATA(src) \ +#define DUMP_ADDITIONAL_DATA(src, what) \ do { \ dict_T *const d = (src); \ if (d != NULL) { \ @@ -1696,7 +1705,9 @@ static bool shada_pack_entry(msgpack_packer *const packer, const size_t key_len = strlen((const char *) hi->hi_key); \ msgpack_pack_str(spacker, key_len); \ msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \ - if (vim_to_msgpack(spacker, &di->di_tv) == FAIL) { \ + if (encode_vim_to_msgpack(spacker, &di->di_tv, \ + _("additional data of ShaDa " what)) \ + == FAIL) { \ goto shada_pack_entry_error; \ } \ } \ @@ -1731,7 +1742,8 @@ static bool shada_pack_entry(msgpack_packer *const packer, if (is_hist_search) { msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep); } - DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements); + DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements, + "history entry item"); break; } case kSDItemVariable: { @@ -1740,11 +1752,20 @@ static bool shada_pack_entry(msgpack_packer *const packer, ? 0 : entry.data.global_var.additional_elements->lv_len); msgpack_pack_array(spacker, arr_size); - PACK_BIN(cstr_as_string(entry.data.global_var.name)); - if (vim_to_msgpack(spacker, &entry.data.global_var.value) == FAIL) { + const String varname = cstr_as_string(entry.data.global_var.name); + PACK_BIN(varname); + char vardesc[256] = "variable g:"; + memcpy(&vardesc[sizeof("variable g:") - 1], varname.data, + varname.size + 1); + if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc) + == FAIL) { + ret = kSDWriteIgnError; + EMSG2(_(WERR "Failed to write variable %s"), + entry.data.global_var.name); goto shada_pack_entry_error; } - DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements); + DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements, + "variable item"); break; } case kSDItemSubString: { @@ -1754,7 +1775,8 @@ static bool shada_pack_entry(msgpack_packer *const packer, : entry.data.sub_string.additional_elements->lv_len); msgpack_pack_array(spacker, arr_size); PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); - DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements); + DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, + "sub string item"); break; } case kSDItemSearchPattern: { @@ -1801,7 +1823,8 @@ static bool shada_pack_entry(msgpack_packer *const packer, msgpack_pack_int64(spacker, entry.data.search_pattern.offset); } #undef PACK_BOOL - DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data); + DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data, + "search pattern item"); break; } case kSDItemChange: @@ -1836,7 +1859,8 @@ static bool shada_pack_entry(msgpack_packer *const packer, PACK_STATIC_STR(KEY_NAME_CHAR); msgpack_pack_uint8(spacker, (uint8_t) entry.data.filemark.name); } - DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data); + DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data, + "mark (change, jump, global or local) item"); break; } case kSDItemRegister: { @@ -1858,13 +1882,13 @@ static bool shada_pack_entry(msgpack_packer *const packer, msgpack_pack_char(spacker, entry.data.reg.name); if (!CHECK_DEFAULT(entry, reg.type)) { PACK_STATIC_STR(REG_KEY_TYPE); - msgpack_pack_uint8(spacker, entry.data.reg.type); + msgpack_pack_uint8(spacker, (uint8_t)entry.data.reg.type); } if (!CHECK_DEFAULT(entry, reg.width)) { PACK_STATIC_STR(REG_KEY_WIDTH); msgpack_pack_uint64(spacker, (uint64_t) entry.data.reg.width); } - DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data); + DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data, "register item"); break; } case kSDItemBufferList: { @@ -1895,7 +1919,8 @@ static bool shada_pack_entry(msgpack_packer *const packer, msgpack_pack_uint64( spacker, (uint64_t) entry.data.buffer_list.buffers[i].pos.col); } - DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data); + DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data, + "buffer list subitem"); } break; } @@ -1946,11 +1971,11 @@ static bool shada_pack_entry(msgpack_packer *const packer, } msgpack_packer_free(spacker); msgpack_sbuffer_destroy(&sbuf); - return true; + return kSDWriteSuccessfull; shada_pack_entry_error: msgpack_packer_free(spacker); msgpack_sbuffer_destroy(&sbuf); - return false; + return ret; } #undef PACK_STRING @@ -1965,13 +1990,13 @@ shada_pack_entry_error: /// is assumed that entry was already converted. /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// restrictions. -static bool shada_pack_encoded_entry(msgpack_packer *const packer, - const vimconv_T *const sd_conv, - PossiblyFreedShadaEntry entry, - const size_t max_kbyte) +static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, + const vimconv_T *const sd_conv, + PossiblyFreedShadaEntry entry, + const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL { - bool ret = true; + ShaDaWriteResult ret = kSDWriteSuccessfull; if (entry.can_free_entry) { ret = shada_pack_entry(packer, entry.data, max_kbyte); shada_free_shada_entry(&entry.data); @@ -2134,7 +2159,7 @@ shada_parse_msgpack_read_next: {} break; } case MSGPACK_UNPACK_PARSE_ERROR: { - emsgu(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " + emsgf(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " "at position %" PRIu64), (uint64_t) initial_fpos); ret = kSDReadStatusNotShaDa; @@ -2151,7 +2176,7 @@ shada_parse_msgpack_read_next: {} break; } case MSGPACK_UNPACK_CONTINUE: { - emsgu(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string " + emsgf(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string " "at position %" PRIu64), (uint64_t) initial_fpos); ret = kSDReadStatusNotShaDa; @@ -2159,7 +2184,7 @@ shada_parse_msgpack_read_next: {} } case MSGPACK_UNPACK_EXTRA_BYTES: { shada_parse_msgpack_extra_bytes: - emsgu(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " + emsgf(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " "at position %" PRIu64), (uint64_t) initial_fpos); ret = kSDReadStatusNotShaDa; @@ -2244,9 +2269,7 @@ static inline ShaDaWriteResult shada_read_when_writing( assert(false); } case kSDItemUnknown: { - if (!shada_pack_entry(packer, entry, 0)) { - ret = kSDWriteFailed; - } + ret = shada_pack_entry(packer, entry, 0); shada_free_shada_entry(&entry); break; } @@ -2262,9 +2285,7 @@ static inline ShaDaWriteResult shada_read_when_writing( } case kSDItemHistoryEntry: { if (entry.data.history_item.histtype >= HIST_COUNT) { - if (!shada_pack_entry(packer, entry, 0)) { - ret = kSDWriteFailed; - } + ret = shada_pack_entry(packer, entry, 0); shada_free_shada_entry(&entry); break; } @@ -2275,9 +2296,7 @@ static inline ShaDaWriteResult shada_read_when_writing( case kSDItemRegister: { const int idx = op_reg_index(entry.data.reg.name); if (idx < 0) { - if (!shada_pack_entry(packer, entry, 0)) { - ret = kSDWriteFailed; - } + ret = shada_pack_entry(packer, entry, 0); shada_free_shada_entry(&entry); break; } @@ -2286,9 +2305,7 @@ static inline ShaDaWriteResult shada_read_when_writing( } case kSDItemVariable: { if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) { - if (!shada_pack_entry(packer, entry, 0)) { - ret = kSDWriteFailed; - } + ret = shada_pack_entry(packer, entry, 0); } shada_free_shada_entry(&entry); break; @@ -2296,9 +2313,7 @@ static inline ShaDaWriteResult shada_read_when_writing( case kSDItemGlobalMark: { const int idx = mark_global_index(entry.data.filemark.name); if (idx < 0) { - if (!shada_pack_entry(packer, entry, 0)) { - ret = kSDWriteFailed; - } + ret = shada_pack_entry(packer, entry, 0); shada_free_shada_entry(&entry); break; } @@ -2465,7 +2480,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } // Write header - if (!shada_pack_entry(packer, (ShadaEntry) { + if (shada_pack_entry(packer, (ShadaEntry) { .type = kSDItemHeader, .timestamp = os_time(), .data = { @@ -2486,7 +2501,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, }), } } - }, 0)) { + }, 0) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } @@ -2526,7 +2541,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, }; i++; } - if (!shada_pack_entry(packer, buflist_entry, 0)) { + if (shada_pack_entry(packer, buflist_entry, 0) == kSDWriteFailed) { xfree(buflist_entry.data.buffer_list.buffers); ret = kSDWriteFailed; goto shada_write_exit; @@ -2552,7 +2567,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } else { copy_tv(&vartv, &tgttv); } - if (!shada_pack_entry(packer, (ShadaEntry) { + ShaDaWriteResult spe_ret; + if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { .type = kSDItemVariable, .timestamp = cur_timestamp, .data = { @@ -2562,7 +2578,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .additional_elements = NULL, } } - }, max_kbyte)) { + }, max_kbyte)) == kSDWriteFailed) { clear_tv(&vartv); clear_tv(&tgttv); ret = kSDWriteFailed; @@ -2570,8 +2586,10 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } clear_tv(&vartv); clear_tv(&tgttv); - int kh_ret; - (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); + if (spe_ret == kSDWriteSuccessfull) { + int kh_ret; + (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); + } } while (var_iter != NULL); } @@ -2739,8 +2757,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .reg = { .contents = (char **) reg.y_array, .contents_size = (size_t) reg.y_size, - .type = (uint8_t) reg.y_type, - .width = (size_t) (reg.y_type == MBLOCK ? reg.y_width : 0), + .type = reg.y_type, + .width = (size_t) (reg.y_type == kMTBlockWise ? reg.y_width : 0), .additional_data = reg.additional_data, .name = name, } @@ -2828,9 +2846,9 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, do { \ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ if (wms_array[i_].data.type != kSDItemMissing) { \ - if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \ - wms_array[i_], \ - max_kbyte)) { \ + if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \ + wms_array[i_], \ + max_kbyte) == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ } \ @@ -2840,8 +2858,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, PACK_WMS_ARRAY(wms->global_marks); PACK_WMS_ARRAY(wms->registers); for (size_t i = 0; i < wms->jumps_size; i++) { - if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i], - max_kbyte)) { + if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i], + max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } @@ -2849,8 +2867,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, #define PACK_WMS_ENTRY(wms_entry) \ do { \ if (wms_entry.data.type != kSDItemMissing) { \ - if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \ - max_kbyte)) { \ + if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \ + max_kbyte) == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ } \ @@ -2877,16 +2895,16 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, for (size_t i = 0; i < file_markss_to_dump; i++) { PACK_WMS_ARRAY(all_file_markss[i]->marks); for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) { - if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, - all_file_markss[i]->changes[j], - max_kbyte)) { + if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, + all_file_markss[i]->changes[j], + max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } } for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) { - if (!shada_pack_entry(packer, all_file_markss[i]->additional_marks[j], - 0)) { + if (shada_pack_entry(packer, all_file_markss[i]->additional_marks[j], + 0) == kSDWriteFailed) { shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]); ret = kSDWriteFailed; goto shada_write_exit; @@ -2903,16 +2921,15 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (dump_one_history[i]) { hms_insert_whole_neovim_history(&wms->hms[i]); HMS_ITER(&wms->hms[i], cur_entry, { - if (!shada_pack_encoded_entry( + if (shada_pack_encoded_entry( packer, &sd_writer->sd_conv, (PossiblyFreedShadaEntry) { .data = cur_entry->data, .can_free_entry = cur_entry->can_free_entry, - }, max_kbyte)) { + }, max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; break; } }) - hms_dealloc(&wms->hms[i]); if (ret == kSDWriteFailed) { goto shada_write_exit; } @@ -2921,6 +2938,11 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } shada_write_exit: + for (size_t i = 0; i < HIST_COUNT; i++) { + if (dump_one_history[i]) { + hms_dealloc(&wms->hms[i]); + } + } kh_dealloc(file_marks, &wms->file_marks); kh_dealloc(bufset, &removable_bufs); msgpack_packer_free(packer); @@ -3043,6 +3065,7 @@ shada_write_file_nomerge: {} const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge ? NULL : &sd_reader)); + assert(sw_ret != kSDWriteIgnError); #ifndef UNIX sd_writer.close(&sd_writer); #endif @@ -3242,11 +3265,11 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, (void) read_bytes; if (sd_reader->error != NULL) { - emsg2(_(SERR "System error while reading ShaDa file: %s"), + emsgf(_(SERR "System error while reading ShaDa file: %s"), sd_reader->error); return kSDReadStatusReadError; } else if (sd_reader->eof) { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "last entry specified that it occupies %" PRIu64 " bytes, " "but file ended earlier"), (uint64_t) length); @@ -3281,11 +3304,11 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, if (first_char == EOF) { if (sd_reader->error) { - emsg2(_(SERR "System error while reading integer from ShaDa file: %s"), + emsgf(_(SERR "System error while reading integer from ShaDa file: %s"), sd_reader->error); return kSDReadStatusReadError; } else if (sd_reader->eof) { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "expected positive integer at position %" PRIu64 ", but got nothing"), (uint64_t) fpos); @@ -3316,7 +3339,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, break; } default: { - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "expected positive integer at position %" PRIu64), (uint64_t) fpos); return kSDReadStatusNotShaDa; @@ -3380,18 +3403,18 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, proc) \ do { \ if (!(condition)) { \ - emsgu(_(READERR(entry_name, error_desc)), initial_fpos); \ + emsgf(_(READERR(entry_name, error_desc)), initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } \ tgt = proc(obj.via.attr); \ } while (0) #define CHECK_KEY_IS_STR(entry_name) \ if (unpacked.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \ - emsgu(_(READERR(entry_name, "has key which is not a string")), \ + emsgf(_(READERR(entry_name, "has key which is not a string")), \ initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } else if (unpacked.data.via.map.ptr[i].key.via.str.size == 0) { \ - emsgu(_(READERR(entry_name, "has empty key")), initial_fpos); \ + emsgf(_(READERR(entry_name, "has empty key")), initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } #define CHECKED_KEY(entry_name, name, error_desc, tgt, condition, attr, proc) \ @@ -3454,7 +3477,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, typval_T adtv; \ if (msgpack_to_vim(obj, &adtv) == FAIL \ || adtv.v_type != VAR_DICT) { \ - emsgu(_(READERR(name, \ + emsgf(_(READERR(name, \ "cannot be converted to a VimL dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ @@ -3479,7 +3502,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, }; \ typval_T aetv; \ if (msgpack_to_vim(obj, &aetv) == FAIL) { \ - emsgu(_(READERR(name, "cannot be converted to a VimL list")), \ + emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ initial_fpos); \ clear_tv(&aetv); \ goto shada_read_next_item_error; \ @@ -3547,7 +3570,7 @@ shada_read_next_item_start: // kSDItemUnknown cannot possibly pass that far because it is -1 and that // will fail in msgpack_read_uint64. But kSDItemMissing may and it will // otherwise be skipped because (1 << 0) will never appear in flags. - emsgu(_(RCERR "Error while reading ShaDa file: " + emsgf(_(RCERR "Error while reading ShaDa file: " "there is an item at position %" PRIu64 " " "that must not be there: Missing items are " "for internal uses only"), @@ -3617,14 +3640,14 @@ shada_read_next_item_start: switch ((ShadaEntryType) type_u64) { case kSDItemHeader: { if (!msgpack_rpc_to_dictionary(&(unpacked.data), &(entry->data.header))) { - emsgu(_(READERR("header", "is not a dictionary")), initial_fpos); + emsgf(_(READERR("header", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } break; } case kSDItemSearchPattern: { if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(READERR("search pattern", "is not a dictionary")), + emsgf(_(READERR("search pattern", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } @@ -3655,7 +3678,7 @@ shada_read_next_item_start: ADDITIONAL_KEY } if (entry->data.search_pattern.pat == NULL) { - emsgu(_(READERR("search pattern", "has no pattern")), initial_fpos); + emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } SET_ADDITIONAL_DATA(entry->data.search_pattern.additional_data, @@ -3667,7 +3690,7 @@ shada_read_next_item_start: case kSDItemGlobalMark: case kSDItemLocalMark: { if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(READERR("mark", "is not a dictionary")), initial_fpos); + emsgf(_(READERR("mark", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } garray_T ad_ga; @@ -3676,7 +3699,7 @@ shada_read_next_item_start: CHECK_KEY_IS_STR("mark") if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) { if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { - emsgu(_(READERR("mark", "has n key which is only valid for " + emsgf(_(READERR("mark", "has n key which is only valid for " "local and global mark entries")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } @@ -3693,15 +3716,15 @@ shada_read_next_item_start: ADDITIONAL_KEY } if (entry->data.filemark.fname == NULL) { - emsgu(_(READERR("mark", "is missing file name")), initial_fpos); + emsgf(_(READERR("mark", "is missing file name")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.filemark.mark.lnum <= 0) { - emsgu(_(READERR("mark", "has invalid line number")), initial_fpos); + emsgf(_(READERR("mark", "has invalid line number")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.filemark.mark.col < 0) { - emsgu(_(READERR("mark", "has invalid column number")), initial_fpos); + emsgf(_(READERR("mark", "has invalid column number")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } SET_ADDITIONAL_DATA(entry->data.filemark.additional_data, "mark"); @@ -3709,7 +3732,7 @@ shada_read_next_item_start: } case kSDItemRegister: { if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(READERR("register", "is not a dictionary")), initial_fpos); + emsgf(_(READERR("register", "is not a dictionary")), initial_fpos); goto shada_read_next_item_error; } garray_T ad_ga; @@ -3719,14 +3742,14 @@ shada_read_next_item_start: if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, REG_KEY_CONTENTS)) { if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("register", + emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " key with non-array value")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (unpacked.data.via.map.ptr[i].val.via.array.size == 0) { - emsgu(_(READERR("register", + emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " key with empty array")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); @@ -3735,7 +3758,7 @@ shada_read_next_item_start: unpacked.data.via.map.ptr[i].val.via.array; for (size_t i = 0; i < arr.size; i++) { if (arr.ptr[i].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("register", "has " REG_KEY_CONTENTS " array " + emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array " "with non-binary value")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } @@ -3755,7 +3778,7 @@ shada_read_next_item_start: ADDITIONAL_KEY } if (entry->data.reg.contents == NULL) { - emsgu(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), + emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } @@ -3764,29 +3787,29 @@ shada_read_next_item_start: } case kSDItemHistoryEntry: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("history", "is not an array")), initial_fpos); + emsgf(_(READERR("history", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size < 2) { - emsgu(_(READERR("history", "does not have enough elements")), + emsgf(_(READERR("history", "does not have enough elements")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - emsgu(_(READERR("history", "has wrong history type type")), + emsgf(_(READERR("history", "has wrong history type type")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[1].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("history", "has wrong history string type")), + emsgf(_(READERR("history", "has wrong history string type")), initial_fpos); goto shada_read_next_item_error; } if (memchr(unpacked.data.via.array.ptr[1].via.bin.ptr, 0, unpacked.data.via.array.ptr[1].via.bin.size) != NULL) { - emsgu(_(READERR("history", "contains string with zero byte inside")), + emsgf(_(READERR("history", "contains string with zero byte inside")), initial_fpos); goto shada_read_next_item_error; } @@ -3796,13 +3819,13 @@ shada_read_next_item_start: entry->data.history_item.histtype == HIST_SEARCH; if (is_hist_search) { if (unpacked.data.via.array.size < 3) { - emsgu(_(READERR("search history", + emsgf(_(READERR("search history", "does not have separator character")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - emsgu(_(READERR("search history", + emsgf(_(READERR("search history", "has wrong history separator type")), initial_fpos); goto shada_read_next_item_error; } @@ -3844,22 +3867,16 @@ shada_read_next_item_hist_no_conv: } case kSDItemVariable: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("variable", "is not an array")), initial_fpos); + emsgf(_(READERR("variable", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size < 2) { - emsgu(_(READERR("variable", "does not have enough elements")), + emsgf(_(READERR("variable", "does not have enough elements")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("variable", "has wrong variable name type")), - initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_NIL - || unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_EXT) { - emsgu(_(READERR("variable", "has wrong variable value type")), + emsgf(_(READERR("variable", "has wrong variable name type")), initial_fpos); goto shada_read_next_item_error; } @@ -3868,7 +3885,7 @@ shada_read_next_item_hist_no_conv: unpacked.data.via.array.ptr[0].via.bin.size); if (msgpack_to_vim(unpacked.data.via.array.ptr[1], &(entry->data.global_var.value)) == FAIL) { - emsgu(_(READERR("variable", "has value that cannot " + emsgf(_(READERR("variable", "has value that cannot " "be converted to the VimL value")), initial_fpos); goto shada_read_next_item_error; } @@ -3889,16 +3906,16 @@ shada_read_next_item_hist_no_conv: } case kSDItemSubString: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("sub string", "is not an array")), initial_fpos); + emsgf(_(READERR("sub string", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size < 1) { - emsgu(_(READERR("sub string", "does not have enough elements")), + emsgf(_(READERR("sub string", "does not have enough elements")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - emsgu(_(READERR("sub string", "has wrong sub string type")), + emsgf(_(READERR("sub string", "has wrong sub string type")), initial_fpos); goto shada_read_next_item_error; } @@ -3911,7 +3928,7 @@ shada_read_next_item_hist_no_conv: } case kSDItemBufferList: { if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgu(_(READERR("buffer list", "is not an array")), initial_fpos); + emsgf(_(READERR("buffer list", "is not an array")), initial_fpos); goto shada_read_next_item_error; } if (unpacked.data.via.array.size == 0) { @@ -3928,7 +3945,7 @@ shada_read_next_item_hist_no_conv: { msgpack_unpacked unpacked = unpacked_2; if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry that is not a dictionary"), initial_fpos); @@ -3953,21 +3970,21 @@ shada_read_next_item_hist_no_conv: } } if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry with invalid line number"), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.buffer_list.buffers[i].pos.col < 0) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry with invalid column number"), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } if (entry->data.buffer_list.buffers[i].fname == NULL) { - emsgu(_(RERR "Error while reading ShaDa file: " + emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry that does not have a file name"), initial_fpos); diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 420e8e2b70..0acaa9ae2b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -319,7 +319,6 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/ui.h" -#include "nvim/tempfile.h" #include "nvim/undo.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -1063,8 +1062,7 @@ static char_u *repl_to = NULL; // // Returns the length of the word in bytes, also when it's OK, so that the // caller can skip over the word. -size_t -spell_check ( +size_t spell_check( win_T *wp, // current window char_u *ptr, hlf_T *attrp, @@ -1082,12 +1080,14 @@ spell_check ( // A word never starts at a space or a control character. Return quickly // then, skipping over the character. - if (*ptr <= ' ') + if (*ptr <= ' ') { return 1; + } // Return here when loading language files failed. - if (GA_EMPTY(&wp->w_s->b_langp)) + if (GA_EMPTY(&wp->w_s->b_langp)) { return 1; + } memset(&mi, 0, sizeof(matchinf_T)); @@ -1095,10 +1095,13 @@ spell_check ( // 0X99FF. But always do check spelling to find "3GPP" and "11 // julifeest". if (*ptr >= '0' && *ptr <= '9') { - if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) + if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B')) { + mi.mi_end = (char_u*) skipbin((char*) ptr + 2); + } else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) { mi.mi_end = skiphex(ptr + 2); - else + } else { mi.mi_end = skipdigits(ptr); + } nrlen = (size_t)(mi.mi_end - ptr); } @@ -1113,12 +1116,14 @@ spell_check ( if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) { // Check word starting with capital letter. c = PTR2CHAR(ptr); - if (!SPELL_ISUPPER(c)) + if (!SPELL_ISUPPER(c)) { wrongcaplen = (size_t)(mi.mi_fend - ptr); + } } } - if (capcol != NULL) + if (capcol != NULL) { *capcol = -1; + } // We always use the characters up to the next non-word character, // also for bad words. @@ -1131,8 +1136,9 @@ spell_check ( // case-fold the word with one non-word character, so that we can check // for the word end. - if (*mi.mi_fend != NUL) + if (*mi.mi_fend != NUL) { mb_ptr_adv(mi.mi_fend); + } (void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1); mi.mi_fwordlen = (int)STRLEN(mi.mi_fword); @@ -1149,8 +1155,9 @@ spell_check ( // If reloading fails the language is still in the list but everything // has been cleared. - if (mi.mi_lp->lp_slang->sl_fidxs == NULL) + if (mi.mi_lp->lp_slang->sl_fidxs == NULL) { continue; + } // Check for a matching word in case-folded words. find_word(&mi, FIND_FOLDWORD); @@ -1181,18 +1188,18 @@ spell_check ( // If we found a number skip over it. Allows for "42nd". Do flag // rare and local words, e.g., "3GPP". if (nrlen > 0) { - if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) + if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) { return nrlen; - } + } + } else if (!spell_iswordp_nmw(ptr, wp)) { // When we are at a non-word character there is no error, just // skip over the character (try looking for a word after it). - else if (!spell_iswordp_nmw(ptr, wp)) { if (capcol != NULL && wp->w_s->b_cap_prog != NULL) { regmatch_T regmatch; // Check for end of sentence. regmatch.regprog = wp->w_s->b_cap_prog; - regmatch.rm_ic = FALSE; + regmatch.rm_ic = false; int r = vim_regexec(®match, ptr, 0); wp->w_s->b_cap_prog = regmatch.regprog; if (r) { @@ -1204,12 +1211,12 @@ spell_check ( return (size_t)(*mb_ptr2len)(ptr); } return 1; - } else if (mi.mi_end == ptr) + } else if (mi.mi_end == ptr) { // Always include at least one character. Required for when there // is a mixup in "midword". mb_ptr_adv(mi.mi_end); - else if (mi.mi_result == SP_BAD - && LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) { + } else if (mi.mi_result == SP_BAD + && LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) { char_u *p, *fp; int save_result = mi.mi_result; @@ -1219,11 +1226,12 @@ spell_check ( if (mi.mi_lp->lp_slang->sl_fidxs != NULL) { p = mi.mi_word; fp = mi.mi_fword; - for (;; ) { + for (;;) { mb_ptr_adv(p); mb_ptr_adv(fp); - if (p >= mi.mi_end) + if (p >= mi.mi_end) { break; + } mi.mi_compoff = (int)(fp - mi.mi_fword); find_word(&mi, FIND_COMPOUND); if (mi.mi_result != SP_BAD) { @@ -1235,12 +1243,13 @@ spell_check ( } } - if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) + if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) { *attrp = HLF_SPB; - else if (mi.mi_result == SP_RARE) + } else if (mi.mi_result == SP_RARE) { *attrp = HLF_SPR; - else + } else { *attrp = HLF_SPL; + } } if (wrongcaplen > 0 && (mi.mi_result == SP_OK || mi.mi_result == SP_RARE)) { @@ -2322,14 +2331,17 @@ static void spell_load_lang(char_u *lang) if (r == FAIL) { if (starting) { - // Some startup file sets &spell, but the necessary files don't exist: - // try to prompt the user at VimEnter. Also set spell again. #3027 - do_cmdline_cmd( - "autocmd VimEnter * call spellfile#LoadFile(&spelllang)|set spell"); + // Prompt the user at VimEnter if spell files are missing. #3027 + // Plugins aren't loaded yet, so spellfile.vim cannot handle this case. + char autocmd_buf[128] = { 0 }; + snprintf(autocmd_buf, sizeof(autocmd_buf), + "autocmd VimEnter * call spellfile#LoadFile('%s')|set spell", + lang); + do_cmdline_cmd(autocmd_buf); } else { smsg( _("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), - lang, spell_enc(), lang); + lang, spell_enc(), lang); } } else if (sl.sl_slang != NULL) { // At least one file was loaded, now load ALL the additions. @@ -12900,8 +12912,8 @@ void ex_spelldump(exarg_T *eap) do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value((char_u*)"spell", TRUE, (char_u*)"", OPT_LOCAL); - set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); + set_option_value((char_u*)"spell", true, (char_u*)"", OPT_LOCAL); + set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); xfree(spl); if (!bufempty() || !buf_valid(curbuf)) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 00dcf3cf46..37a0fb82da 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -119,6 +119,54 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, return escaped_string; } +/// Save a copy of an unquoted string +/// +/// Turns string like `a\bc"def\"ghi\\\n"jkl` into `a\bcdef"ghi\\njkl`, for use +/// in shell_build_argv: the only purpose of backslash is making next character +/// be treated literally inside the double quotes, if this character is +/// backslash or quote. +/// +/// @param[in] string String to copy. +/// @param[in] length Length of the string to copy. +/// +/// @return [allocated] Copy of the string. +char *vim_strnsave_unquoted(const char *const string, const size_t length) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET +{ +#define ESCAPE_COND(p, inquote, string_end) \ + (*p == '\\' && inquote && p + 1 < string_end && (p[1] == '\\' || p[1] == '"')) + size_t ret_length = 0; + bool inquote = false; + const char *const string_end = string + length; + for (const char *p = string; p < string_end; p++) { + if (*p == '"') { + inquote = !inquote; + } else if (ESCAPE_COND(p, inquote, string_end)) { + ret_length++; + p++; + } else { + ret_length++; + } + } + + char *const ret = xmallocz(ret_length); + char *rp = ret; + inquote = false; + for (const char *p = string; p < string_end; p++) { + if (*p == '"') { + inquote = !inquote; + } else if (ESCAPE_COND(p, inquote, string_end)) { + *rp++ = *(++p); + } else { + *rp++ = *p; + } + } +#undef ESCAPE_COND + + return ret; +} + /* * Escape "string" for use as a shell argument with system(). * This uses single quotes, except when we know we need to use double quotes @@ -377,9 +425,13 @@ char_u *vim_strchr(const char_u *string, int c) const char_u *p = string; if (enc_utf8 && c >= 0x80) { while (*p != NUL) { - if (utf_ptr2char(p) == c) + int l = (*mb_ptr2len)(p); + + // Avoid matching an illegal byte here. + if (l > 1 && utf_ptr2char(p) == c) { return (char_u *) p; - p += (*mb_ptr2len)(p); + } + p += l; } return NULL; } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 24422c71fb..b04180ad1c 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -41,6 +41,8 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" +static bool did_syntax_onoff = false; + // Structure that stores information about a highlight group. // The ID of a highlight group is also called group ID. It is the index in // the highlight_ga array PLUS ONE. @@ -2613,33 +2615,37 @@ find_endpos ( IF_SYN_TIME(&spp_skip->sp_time)); spp_skip->sp_prog = regmatch.regprog; if (r && regmatch.startpos[0].col <= best_regmatch.startpos[0].col) { - /* Add offset to skip pattern match */ + // Add offset to skip pattern match syn_add_end_off(&pos, ®match, spp_skip, SPO_ME_OFF, 1); - /* If the skip pattern goes on to the next line, there is no - * match with an end pattern in this line. */ - if (pos.lnum > startpos->lnum) + // If the skip pattern goes on to the next line, there is no + // match with an end pattern in this line. + if (pos.lnum > startpos->lnum) { break; + } - line = ml_get_buf(syn_buf, startpos->lnum, FALSE); + line = ml_get_buf(syn_buf, startpos->lnum, false); + int line_len = (int)STRLEN(line); - /* take care of an empty match or negative offset */ - if (pos.col <= matchcol) - ++matchcol; - else if (pos.col <= regmatch.endpos[0].col) + // take care of an empty match or negative offset + if (pos.col <= matchcol) { + matchcol++; + } else if (pos.col <= regmatch.endpos[0].col) { matchcol = pos.col; - else - /* Be careful not to jump over the NUL at the end-of-line */ + } else { + // Be careful not to jump over the NUL at the end-of-line for (matchcol = regmatch.endpos[0].col; - line[matchcol] != NUL && matchcol < pos.col; - ++matchcol) - ; + matchcol < line_len && matchcol < pos.col; + matchcol++) { + } + } - /* if the skip pattern includes end-of-line, break here */ - if (line[matchcol] == NUL) + // if the skip pattern includes end-of-line, break here + if (matchcol >= line_len) { break; + } - continue; /* start with first end pattern again */ + continue; // start with first end pattern again } } @@ -3004,14 +3010,19 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) return; next = skiptowhite(arg); - if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) + if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) { curwin->w_s->b_syn_spell = SYNSPL_TOP; - else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) + } else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) { curwin->w_s->b_syn_spell = SYNSPL_NOTOP; - else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) + } else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) { curwin->w_s->b_syn_spell = SYNSPL_DEFAULT; - else + } else { EMSG2(_("E390: Illegal argument: %s"), arg); + return; + } + + // assume spell checking changed, force a redraw + redraw_win_later(curwin, NOT_VALID); } /* @@ -3281,17 +3292,28 @@ static void syn_cmd_off(exarg_T *eap, int syncing) } static void syn_cmd_onoff(exarg_T *eap, char *name) + FUNC_ATTR_NONNULL_ALL { - char buf[100]; - + did_syntax_onoff = true; eap->nextcmd = check_nextcmd(eap->arg); if (!eap->skip) { - strcpy(buf, "so "); + char buf[100]; + strncpy(buf, "so ", 4); vim_snprintf(buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name); do_cmdline_cmd(buf); } } +void syn_maybe_on(void) +{ + if (!did_syntax_onoff) { + exarg_T ea; + ea.arg = (char_u *)""; + ea.skip = false; + syn_cmd_onoff(&ea, "syntax"); + } +} + /* * Handle ":syntax [list]" command: list current syntax words. */ @@ -4183,12 +4205,16 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) break; if (p[1] == NUL) { EMSG2(_("E789: Missing ']': %s"), kw); - kw = p + 2; /* skip over the NUL */ - break; + goto error; } if (p[1] == ']') { - kw = p + 1; /* skip over the "]" */ - break; + if (p[2] != NUL) { + EMSG3(_("E890: trailing char after ']': %s]%s"), + kw, &p[2]); + goto error; + } + kw = p + 1; + break; // skip over the "]" } if (has_mbyte) { int l = (*mb_ptr2len)(p + 1); @@ -4203,6 +4229,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) } } +error: xfree(keyword_copy); xfree(syn_opt_arg.cont_in_list); xfree(syn_opt_arg.next_list); @@ -4455,12 +4482,10 @@ syn_cmd_region ( if (illegal || not_enough) rest = NULL; - /* - * Must have a "start" and "end" pattern. - */ - if (rest != NULL && (pat_ptrs[ITEM_START] == NULL || - pat_ptrs[ITEM_END] == NULL)) { - not_enough = TRUE; + // Must have a "start" and "end" pattern. + if (rest != NULL && (pat_ptrs[ITEM_START] == NULL + || pat_ptrs[ITEM_END] == NULL)) { + not_enough = true; rest = NULL; } @@ -4843,9 +4868,10 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) int idx; char_u *cpo_save; - /* need at least three chars */ - if (arg == NULL || arg[1] == NUL || arg[2] == NUL) + // need at least three chars + if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) { return NULL; + } end = skip_regexp(arg + 1, *arg, TRUE, NULL); if (*end != *arg) { /* end delimiter not found */ @@ -4982,6 +5008,10 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) curwin->w_s->b_syn_sync_maxlines = 0; } } else if (STRCMP(key, "LINECONT") == 0) { + if (*next_arg == NUL) { // missing pattern + illegal = true; + break; + } if (curwin->w_s->b_syn_linecont_pat != NULL) { EMSG(_("E403: syntax sync: line continuations pattern specified twice")); finished = TRUE; @@ -5395,8 +5425,10 @@ void ex_ownsyntax(exarg_T *eap) if (curwin->w_s == &curwin->w_buffer->b_s) { curwin->w_s = xmalloc(sizeof(synblock_T)); memset(curwin->w_s, 0, sizeof(synblock_T)); - // TODO: Keep the spell checking as it was. - curwin->w_p_spell = FALSE; /* No spell checking */ + hash_init(&curwin->w_s->b_keywtab); + hash_init(&curwin->w_s->b_keywtab_ic); + // TODO: Keep the spell checking as it was. NOLINT(readability/todo) + curwin->w_p_spell = false; // No spell checking clear_string_option(&curwin->w_s->b_p_spc); clear_string_option(&curwin->w_s->b_p_spf); clear_string_option(&curwin->w_s->b_p_spl); @@ -5507,25 +5539,29 @@ char_u *get_syntax_name(expand_T *xp, int idx) } -/* - * Function called for expression evaluation: get syntax ID at file position. - */ -int -syn_get_id ( +// Function called for expression evaluation: get syntax ID at file position. +int syn_get_id( win_T *wp, long lnum, colnr_T col, - int trans, /* remove transparency */ - bool *spellp, /* return: can do spell checking */ - int keep_state /* keep state of char at "col" */ + int trans, // remove transparency + bool *spellp, // return: can do spell checking + int keep_state // keep state of char at "col" ) { - /* When the position is not after the current position and in the same - * line of the same buffer, need to restart parsing. */ + // When the position is not after the current position and in the same + // line of the same buffer, need to restart parsing. if (wp->w_buffer != syn_buf || lnum != current_lnum - || col < current_col) + || col < current_col) { syntax_start(wp, lnum); + } else if (wp->w_buffer == syn_buf + && lnum == current_lnum + && col > current_col) { + // next_match may not be correct when moving around, e.g. with the + // "skip" expression in searchpair() + next_match_idx = -1; + } (void)get_syntax_attr(col, spellp, keep_state); @@ -6365,7 +6401,7 @@ do_highlight ( HL_TABLE()[idx].sg_cterm_bg = color + 1; if (is_normal_group) { cterm_normal_bg_color = color + 1; - { + if (!ui_rgb_attached()) { must_redraw = CLEAR; if (color >= 0) { if (t_colors < 16) @@ -6903,7 +6939,12 @@ set_hl_attr ( // before setting attr_entry->{f,g}g_color to a other than -1 at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; - sgp->sg_attr = get_attr_entry(&at_en); + + if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0 + || at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1 + || at_en.cterm_ae_attr != 0 || at_en.rgb_ae_attr != 0) { + sgp->sg_attr = get_attr_entry(&at_en); + } } /* diff --git a/src/nvim/tag.c b/src/nvim/tag.c index d832924efd..7885d467d8 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -445,17 +445,10 @@ do_tag ( tagmatchname = vim_strsave(name); } - /* - * If a count is supplied to the ":tag <name>" command, then - * jump to count'th matching tag. - */ - if (type == DT_TAG && *tag != NUL && count > 0) - cur_match = count - 1; - - if (type == DT_SELECT || type == DT_JUMP - || type == DT_LTAG - ) + if (type == DT_TAG || type == DT_SELECT || type == DT_JUMP + || type == DT_LTAG) { cur_match = MAXCOL - 1; + } max_num_matches = cur_match + 1; /* when the argument starts with '/', use it as a regexp */ @@ -506,18 +499,19 @@ do_tag ( EMSG2(_("E426: tag not found: %s"), name); g_do_tagpreview = 0; } else { - int ask_for_selection = FALSE; + bool ask_for_selection = false; if (type == DT_CSCOPE && num_matches > 1) { cs_print_tags(); - ask_for_selection = TRUE; - } else if (type == DT_SELECT || - (type == DT_JUMP && num_matches > 1)) { - /* - * List all the matching tags. - * Assume that the first match indicates how long the tags can - * be, and align the file names to that. - */ + ask_for_selection = true; + } else if (type == DT_TAG) { + // If a count is supplied to the ":tag <name>" command, then + // jump to count'th matching tag. + cur_match = count > 0 ? count - 1 : 0; + } else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) { + // List all the matching tags. + // Assume that the first match indicates how long the tags can + // be, and align the file names to that. parse_match(matches[0], &tagp); taglen = (int)(tagp.tagname_end - tagp.tagname + 2); if (taglen < 18) @@ -532,19 +526,17 @@ do_tag ( taglen_advance(taglen); MSG_PUTS_ATTR(_("file\n"), hl_attr(HLF_T)); - for (i = 0; i < num_matches && !got_int; ++i) { + for (i = 0; i < num_matches && !got_int; i++) { parse_match(matches[i], &tagp); - if (!new_tag && ( - (g_do_tagpreview != 0 - && i == ptag_entry.cur_match) || - (use_tagstack - && i == tagstack[tagstackidx].cur_match))) + if (!new_tag && ((g_do_tagpreview != 0 && i == ptag_entry.cur_match) + || (use_tagstack + && i == tagstack[tagstackidx].cur_match))) { *IObuff = '>'; - else + } else { *IObuff = ' '; - vim_snprintf((char *)IObuff + 1, IOSIZE - 1, - "%2d %s ", i + 1, - mt_names[matches[i][0] & MT_MASK]); + } + vim_snprintf((char *)IObuff + 1, IOSIZE - 1, "%2d %s ", i + 1, + mt_names[matches[i][0] & MT_MASK]); msg_puts(IObuff); if (tagp.tagkind != NULL) msg_outtrans_len(tagp.tagkind, @@ -666,9 +658,10 @@ do_tag ( msg_putchar('\n'); os_breakcheck(); } - if (got_int) - got_int = FALSE; /* only stop the listing */ - ask_for_selection = TRUE; + if (got_int) { + got_int = false; // only stop the listing + } + ask_for_selection = true; } else if (type == DT_LTAG) { list_T *list; char_u tag_name[128 + 1]; @@ -801,10 +794,8 @@ do_tag ( cur_match = 0; /* Jump to the first tag */ } - if (ask_for_selection == TRUE) { - /* - * Ask to select a tag from the list. - */ + if (ask_for_selection) { + // Ask to select a tag from the list. i = prompt_for_number(NULL); if (i <= 0 || i > num_matches || got_int) { /* no valid choice: don't change anything */ @@ -851,7 +842,7 @@ do_tag ( ic = (matches[cur_match][0] & MT_IC_OFF); - if (type != DT_SELECT && type != DT_JUMP + if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP && type != DT_CSCOPE && (num_matches > 1 || ic) && !skip_msg) { @@ -879,7 +870,7 @@ do_tag ( /* Let the SwapExists event know what tag we are jumping to. */ vim_snprintf((char *)IObuff, IOSIZE, ":ta %s\r", name); - set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1); + set_vim_var_string(VV_SWAPCOMMAND, (char *) IObuff, -1); /* * Jump to the desired match. @@ -1154,6 +1145,22 @@ find_tags ( int get_it_again = FALSE; int use_cscope = (flags & TAG_CSCOPE); int verbose = (flags & TAG_VERBOSE); + int save_p_ic = p_ic; + + // Change the value of 'ignorecase' according to 'tagcase' for the + // duration of this function. + switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) { + case TC_FOLLOWIC: + break; + case TC_IGNORE: + p_ic = true; + break; + case TC_MATCH: + p_ic = false; + break; + default: + assert(false); + } help_save = curbuf->b_help; orgpat.pat = pat; @@ -1217,20 +1224,15 @@ find_tags ( for (round = 1; round <= 2; ++round) { linear = (orgpat.headlen == 0 || !p_tbs || round == 2); - /* - * Try tag file names from tags option one by one. - */ - for (first_file = TRUE; - use_cscope || - get_tagfname(&tn, first_file, tag_fname) == OK; - first_file = FALSE) { - /* - * A file that doesn't exist is silently ignored. Only when not a - * single file is found, an error message is given (further on). - */ - if (use_cscope) - fp = NULL; /* avoid GCC warning */ - else { + // Try tag file names from tags option one by one. + for (first_file = true; + use_cscope || get_tagfname(&tn, first_file, tag_fname) == OK; + first_file = false) { + // A file that doesn't exist is silently ignored. Only when not a + // single file is found, an error message is given (further on). + if (use_cscope) { + fp = NULL; // avoid GCC warning + } else { if (curbuf->b_help) { /* Prefer help tags according to 'helplang'. Put the * two-letter language name in help_lang[]. */ @@ -1962,6 +1964,8 @@ findtag_end: curbuf->b_help = help_save; xfree(saved_pat); + p_ic = save_p_ic; + return retval; } diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c deleted file mode 100644 index a218c03fdb..0000000000 --- a/src/nvim/tempfile.c +++ /dev/null @@ -1,138 +0,0 @@ -#include <inttypes.h> -#include <stdbool.h> -#include <stdlib.h> -#include <stdio.h> - -#include "nvim/ascii.h" -#include "nvim/memory.h" -#include "nvim/misc1.h" -#include "nvim/os/os.h" -#include "nvim/os/os_defs.h" -#include "nvim/path.h" -#include "nvim/strings.h" -#include "nvim/tempfile.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "tempfile.c.generated.h" -#endif - -/// Name of Vim's own temp dir. Ends in a slash. -static char_u *vim_tempdir = NULL; - -/// Create a directory for private use by this instance of Neovim. -/// This is done once, and the same directory is used for all temp files. -/// This method avoids security problems because of symlink attacks et al. -/// It's also a bit faster, because we only need to check for an existing -/// file when creating the directory and not for each temp file. -static void vim_maketempdir(void) -{ - static const char *temp_dirs[] = TEMP_DIR_NAMES; - // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. - char_u template[TEMP_FILE_PATH_MAXLEN]; - char_u path[TEMP_FILE_PATH_MAXLEN]; - for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) { - // Expand environment variables, leave room for "/nvimXXXXXX/999999999" - expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); - if (!os_isdir(template)) { // directory doesn't exist - continue; - } - - add_pathsep((char *)template); - // Concatenate with temporary directory name pattern - STRCAT(template, "nvimXXXXXX"); - - if (os_mkdtemp((const char *)template, (char *)path) != 0) { - continue; - } - - if (vim_settempdir((char *)path)) { - // Successfully created and set temporary directory so stop trying. - break; - } else { - // Couldn't set `vim_tempdir` to `path` so remove created directory. - os_rmdir((char *)path); - } - } -} - -/// Delete the temp directory and all files it contains. -void vim_deltempdir(void) -{ - if (vim_tempdir != NULL) { - snprintf((char *)NameBuff, MAXPATHL, "%s*", vim_tempdir); - - char_u **files; - int file_count; - - // Note: We cannot just do `&NameBuff` because it is a statically - // sized array so `NameBuff == &NameBuff` according to C semantics. - char_u *buff_list[1] = {NameBuff}; - if (gen_expand_wildcards(1, buff_list, &file_count, &files, - EW_DIR|EW_FILE|EW_SILENT) == OK) { - for (int i = 0; i < file_count; ++i) { - os_remove((char *)files[i]); - } - FreeWild(file_count, files); - } - path_tail(NameBuff)[-1] = NUL; - os_rmdir((char *)NameBuff); - - xfree(vim_tempdir); - vim_tempdir = NULL; - } -} - -/// Get the name of temp directory. This directory would be created on the first -/// call to this function. -char_u *vim_gettempdir(void) -{ - if (vim_tempdir == NULL) { - vim_maketempdir(); - } - - return vim_tempdir; -} - -/// Set Neovim own temporary directory name to `tempdir`. This directory should -/// be already created. Expand this name to a full path and put it in -/// `vim_tempdir`. This avoids that using `:cd` would confuse us. -/// -/// @param tempdir must be no longer than MAXPATHL. -/// -/// @return false if we run out of memory. -static bool vim_settempdir(char *tempdir) -{ - char *buf = verbose_try_malloc(MAXPATHL + 2); - if (!buf) { - return false; - } - vim_FullName(tempdir, buf, MAXPATHL, false); - add_pathsep(buf); - vim_tempdir = (char_u *)xstrdup(buf); - xfree(buf); - return true; -} - -/// Return a unique name that can be used for a temp file. -/// -/// @note The temp file is NOT created. -/// -/// @return pointer to the temp file name or NULL if Neovim can't create -/// temporary directory for its own temporary files. -char_u *vim_tempname(void) -{ - // Temp filename counter. - static uint32_t temp_count; - - char_u *tempdir = vim_gettempdir(); - if (!tempdir) { - return NULL; - } - - // There is no need to check if the file exists, because we own the directory - // and nobody else creates a file in it. - char_u template[TEMP_FILE_PATH_MAXLEN]; - snprintf((char *)template, TEMP_FILE_PATH_MAXLEN, - "%s%" PRIu32, tempdir, temp_count++); - return vim_strsave(template); -} diff --git a/src/nvim/tempfile.h b/src/nvim/tempfile.h deleted file mode 100644 index c030a70eeb..0000000000 --- a/src/nvim/tempfile.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef NVIM_TEMPFILE_H -#define NVIM_TEMPFILE_H - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "tempfile.h.generated.h" -#endif - -#endif // NVIM_TEMPFILE_H diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index adf3f725a2..104cc47cda 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1,5 +1,5 @@ -// VT220/xterm-like terminal emulator implementation for Neovim. Powered by -// libvterm(http://www.leonerd.org.uk/code/libvterm/). +// VT220/xterm-like terminal emulator implementation for nvim. Powered by +// libvterm (http://www.leonerd.org.uk/code/libvterm/). // // libvterm is a pure C99 terminal emulation library with abstract input and // display. This means that the library needs to read data from the master fd @@ -10,31 +10,31 @@ // vterm_keyboard_key/vterm_keyboard_unichar, which generates byte streams that // must be fed back to the master fd. // -// This implementation uses Neovim buffers as the display mechanism for both +// This implementation uses nvim buffers as the display mechanism for both // the visible screen and the scrollback buffer. When focused, the window // "pins" to the bottom of the buffer and mirrors libvterm screen state. // // When a line becomes invisible due to a decrease in screen height or because // a line was pushed up during normal terminal output, we store the line -// information in the scrollback buffer, which is mirrored in the Neovim buffer +// information in the scrollback buffer, which is mirrored in the nvim buffer // by appending lines just above the visible part of the buffer. // // When the screen height increases, libvterm will ask for a row in the -// scrollback buffer, which is mirrored in the Neovim buffer displaying lines +// scrollback buffer, which is mirrored in the nvim buffer displaying lines // that were previously invisible. // -// The vterm->Neovim synchronization is performed in intervals of 10 +// The vterm->nvim synchronization is performed in intervals of 10 // milliseconds. This is done to minimize screen updates when receiving large // bursts of data. // // This module is decoupled from the processes that normally feed it data, so -// it's possible to use it as a general purpose console buffer(possibly as a -// log/display mechanism for Neovim in the future) +// it's possible to use it as a general purpose console buffer (possibly as a +// log/display mechanism for nvim in the future) // -// Inspired by vimshell(http://www.wana.at/vimshell/) and -// Conque(https://code.google.com/p/conque/). Libvterm usage instructions (plus +// Inspired by vimshell (http://www.wana.at/vimshell/) and +// Conque (https://code.google.com/p/conque/). Libvterm usage instructions (plus // some extra code) were taken from -// pangoterm(http://www.leonerd.org.uk/code/pangoterm/) +// pangoterm (http://www.leonerd.org.uk/code/pangoterm/) #include <assert.h> #include <stdio.h> #include <stdint.h> @@ -77,9 +77,7 @@ typedef struct terminal_state { VimState state; Terminal *term; - int save_state; // saved value of State int save_rd; // saved value of RedrawingDisabled - bool save_mapped_ctrl_c; // saved value of mapped_ctrl_c; bool close; bool got_bs; // if the last input was <C-\> } TerminalState; @@ -143,10 +141,6 @@ struct terminal { int pressed_button; // pending width/height bool pending_resize; - // color palette. this isn't set directly in the vterm instance because - // the default values are used to obtain the color numbers passed to cterm - // colors - RgbValue colors[256]; // With a reference count of 0 the terminal can be freed. size_t refcount; }; @@ -181,7 +175,16 @@ void terminal_init(void) for (int color_index = 0; color_index < 256; color_index++) { VTermColor color; - vterm_state_get_palette_color(state, color_index, &color); + // Some of the default 16 colors has the same color as the later + // 240 colors. To avoid collisions, we will use the custom colors + // below in non true color mode. + if (color_index < 16) { + color.red = 0; + color.green = 0; + color.blue = (uint8_t)(color_index + 1); + } else { + vterm_state_get_palette_color(state, color_index, &color); + } map_put(int, int)(color_indexes, RGB(color.red, color.green, color.blue), color_index + 1); } @@ -207,6 +210,7 @@ void terminal_teardown(void) Terminal *terminal_open(TerminalOptions opts) { + bool true_color = ui_rgb_attached(); // Create a new terminal instance and configure it Terminal *rv = xcalloc(1, sizeof(Terminal)); rv->opts = opts; @@ -222,7 +226,7 @@ Terminal *terminal_open(TerminalOptions opts) // Set up screen rv->vts = vterm_obtain_screen(rv->vt); vterm_screen_enable_altscreen(rv->vts, true); - // delete empty lines at the end of the buffer + // delete empty lines at the end of the buffer vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv); vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL); vterm_screen_reset(rv->vts, 1); @@ -238,7 +242,7 @@ Terminal *terminal_open(TerminalOptions opts) set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); RESET_BINDING(curwin); // Apply TermOpen autocmds so the user can configure the terminal - apply_autocmds(EVENT_TERMOPEN, NULL, NULL, true, curbuf); + apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf); // Configure the scrollback buffer. Try to get the size from: // @@ -252,12 +256,27 @@ Terminal *terminal_open(TerminalOptions opts) rv->sb_size = MIN(rv->sb_size, 100000); rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); + if (!true_color) { + // Change the first 16 colors so we can easily get the correct color + // index from them. + for (int i = 0; i < 16; i++) { + VTermColor color; + color.red = 0; + color.green = 0; + color.blue = (uint8_t)(i + 1); + vterm_state_set_palette_color(state, i, &color); + } + return rv; + } + + vterm_state_set_bold_highbright(state, true); + // Configure the color palette. Try to get the color from: // // - b:terminal_color_{NUM} // - g:terminal_color_{NUM} // - the VTerm instance - for (int i = 0; i < (int)ARRAY_SIZE(rv->colors); i++) { + for (int i = 0; i < 16; i++) { RgbValue color_val = -1; char var[64]; snprintf(var, sizeof(var), "terminal_color_%d", i); @@ -267,16 +286,13 @@ Terminal *terminal_open(TerminalOptions opts) xfree(name); if (color_val != -1) { - rv->colors[i] = color_val; + VTermColor color; + color.red = (uint8_t)((color_val >> 16) & 0xFF); + color.green = (uint8_t)((color_val >> 8) & 0xFF); + color.blue = (uint8_t)((color_val >> 0) & 0xFF); + vterm_state_set_palette_color(state, i, &color); } } - - if (color_val == -1) { - // the default is taken from vterm - VTermColor color; - vterm_state_get_palette_color(state, i, &color); - rv->colors[i] = RGB(color.red, color.green, color.blue); - } } return rv; @@ -290,8 +306,9 @@ void terminal_close(Terminal *term, char *msg) term->forward_mouse = false; term->closed = true; + buf_T *buf = handle_get_buffer(term->buf_handle); + if (!msg || exiting) { - buf_T *buf = handle_get_buffer(term->buf_handle); // If no msg was given, this was called by close_buffer(buffer.c). Or if // exiting, we must inform the buffer the terminal no longer exists so that // close_buffer() doesn't call this again. @@ -306,6 +323,10 @@ void terminal_close(Terminal *term, char *msg) } else { terminal_receive(term, msg, strlen(msg)); } + + if (buf) { + apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); + } } void terminal_resize(Terminal *term, uint16_t width, uint16_t height) @@ -362,12 +383,11 @@ void terminal_enter(void) checkpcmark(); setpcmark(); - s->save_state = State; + int save_state = State; s->save_rd = RedrawingDisabled; State = TERM_FOCUS; + mapped_ctrl_c |= TERM_FOCUS; // Always map CTRL-C to avoid interrupt. RedrawingDisabled = false; - s->save_mapped_ctrl_c = mapped_ctrl_c; - mapped_ctrl_c = true; // go to the bottom when the terminal is focused adjust_topline(s->term, buf, false); // erase the unfocused cursor @@ -380,11 +400,10 @@ void terminal_enter(void) state_enter(&s->state); restart_edit = 0; - State = s->save_state; + State = save_state; RedrawingDisabled = s->save_rd; // draw the unfocused cursor invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1); - mapped_ctrl_c = s->save_mapped_ctrl_c; unshowmode(true); redraw(curbuf->handle != s->term->buf_handle); ui_busy_stop(); @@ -402,14 +421,18 @@ static int terminal_execute(VimState *state, int key) TerminalState *s = (TerminalState *)state; switch (key) { - case K_FOCUSGAINED: // Neovim has been given focus + case K_FOCUSGAINED: // nvim has been given focus apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf); break; - case K_FOCUSLOST: // Neovim has lost focus + case K_FOCUSLOST: // nvim has lost focus apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf); break; + // Temporary fix until paste events gets implemented + case K_PASTE: + break; + case K_LEFTMOUSE: case K_LEFTDRAG: case K_LEFTRELEASE: @@ -543,14 +566,10 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, // Since libvterm does not expose the color index used by the program, we // use the rgb value to find the appropriate index in the cache computed by // `terminal_init`. - int vt_fg_idx = vt_fg != default_vt_fg ? + int vt_fg_idx = vt_fg != -1 ? map_get(int, int)(color_indexes, vt_fg) : 0; - int vt_bg_idx = vt_bg != default_vt_bg ? + int vt_bg_idx = vt_bg != -1 ? map_get(int, int)(color_indexes, vt_bg) : 0; - // The index is now used to get the final rgb value from the - // user-customizable palette. - int vt_fg_rgb = vt_fg_idx != 0 ? term->colors[vt_fg_idx - 1] : -1; - int vt_bg_rgb = vt_bg_idx != 0 ? term->colors[vt_bg_idx - 1] : -1; int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0) | (cell.attrs.italic ? HL_ITALIC : 0) @@ -565,8 +584,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, .cterm_fg_color = vt_fg_idx, .cterm_bg_color = vt_bg_idx, .rgb_ae_attr = (int16_t)hl_attrs, - .rgb_fg_color = vt_fg_rgb, - .rgb_bg_color = vt_bg_rgb, + .rgb_fg_color = vt_fg, + .rgb_bg_color = vt_bg, }); } @@ -626,6 +645,7 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data) api_free_object(dict_set_value(buf->b_vars, cstr_as_string("term_title"), STRING_OBJ(cstr_as_string(val->string)), + false, &err)); break; } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 8c3e99c624..b763a67347 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -7,28 +7,41 @@ export SHELL := sh VIMPROG := ../../../build/bin/nvim SCRIPTSOURCE := ../../../runtime -SCRIPTS := test_eval.out \ - test8.out test10.out \ - test11.out test12.out test13.out test14.out \ - test17.out \ - test24.out \ - test30.out \ - test32.out test34.out \ - test36.out test37.out test39.out test40.out \ - test42.out test45.out \ - test47.out test48.out test49.out \ - test52.out test53.out test55.out \ - test64.out \ - test68.out test69.out \ - test73.out \ - test79.out \ - test83.out \ - test88.out \ - test_listlbr.out \ - test_breakindent.out \ - test_charsearch.out \ - test_close_count.out \ - test_command_count.out \ +SCRIPTS := \ + test8.out \ + test10.out \ + test12.out \ + test13.out \ + test14.out \ + test17.out \ + test24.out \ + test30.out \ + test32.out \ + test34.out \ + test37.out \ + test40.out \ + test42.out \ + test47.out \ + test48.out \ + test49.out \ + test52.out \ + test53.out \ + test55.out \ + test64.out \ + test69.out \ + test73.out \ + test79.out \ + test_listlbr.out \ + test_breakindent.out \ + test_close_count.out \ + test_marks.out \ + +# Tests using runtest.vim.vim. +# Keep test_alot*.res as the last one, sort the others. +NEW_TESTS = \ + test_viml.res \ + test_cursor_func.res \ + test_alot.res SCRIPTS_GUI := test16.out @@ -61,9 +74,9 @@ ifdef TESTNUM SCRIPTS := test$(TESTNUM).out endif -nongui: nolog $(SCRIPTS) report +nongui: nolog $(SCRIPTS) newtests report -gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) report +gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) newtests report .gdbinit: echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit @@ -90,9 +103,11 @@ RUN_VIM := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(VIMPROG) clean: -rm -rf *.out \ *.failed \ + *.res \ *.rej \ *.orig \ test.log \ + messages \ $(RM_ON_RUN) \ $(RM_ON_START) \ valgrind.* \ @@ -128,7 +143,7 @@ test1.out: .gdbinit test1.in # Check if the test.out file matches test.ok. @/bin/sh -c "if test -f test.out; then \ - if diff test.out $*.ok; then \ + if diff -u test.out $*.ok; then \ mv -f test.out $*.out; \ else \ echo $* FAILED >> test.log; \ @@ -145,4 +160,20 @@ test1.out: .gdbinit test1.in test49.out: test49.vim nolog: - -rm -f test.log + -rm -f test.log messages + + +# New style of tests uses Vim script with assert calls. These are easier +# to write and a lot easier to read and debug. +# Limitation: Only works with the +eval feature. +RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -u unix.vim -U NONE --noplugin + +newtests: newtestssilent + @/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \ + cat messages && cat test.log; \ + fi" + +newtestssilent: $(NEW_TESTS) + +%.res: %.vim .gdbinit + $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim new file mode 100644 index 0000000000..6601dcf52f --- /dev/null +++ b/src/nvim/testdir/runtest.vim @@ -0,0 +1,128 @@ +" This script is sourced while editing the .vim file with the tests. +" When the script is successful the .res file will be created. +" Errors are appended to the test.log file. +" +" The test script may contain anything, only functions that start with +" "Test_" are special. These will be invoked and should contain assert +" functions. See test_assert.vim for an example. +" +" It is possible to source other files that contain "Test_" functions. This +" can speed up testing, since Vim does not need to restart. But be careful +" that the tests do not interfere with each other. +" +" If an error cannot be detected properly with an assert function add the +" error to the v:errors list: +" call add(v:errors, 'test foo failed: Cannot find xyz') +" +" If preparation for each Test_ function is needed, define a SetUp function. +" It will be called before each Test_ function. +" +" If cleanup after each Test_ function is needed, define a TearDown function. +" It will be called after each Test_ function. + +" Without the +eval feature we can't run these tests, bail out. +so small.vim + +" Check that the screen size is at least 24 x 80 characters. +if &lines < 24 || &columns < 80 + let error = 'Screen size too small! Tests require at least 24 lines with 80 characters' + echoerr error + split test.log + $put =error + w + cquit +endif + +" This also enables use of line continuation. +set viminfo+=nviminfo + +" Avoid stopping at the "hit enter" prompt +set nomore + +" Output all messages in English. +lang mess C + +" Source the test script. First grab the file name, in case the script +" navigates away. +let testname = expand('%') +let done = 0 +let fail = 0 +let errors = [] +let messages = [] +if expand('%') =~ 'test_viml.vim' + " this test has intentional errors, don't use try/catch. + source % +else + try + source % + catch + let fail += 1 + call add(errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) + endtry +endif + +" Locate Test_ functions and execute them. +set nomore +redir @q +function /^Test_ +redir END +let tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) + +for test in tests + if exists("*SetUp") + call SetUp() + endif + + call add(messages, 'Executing ' . test) + let done += 1 + try + exe 'call ' . test + catch + let fail += 1 + call add(v:errors, 'Caught exception in ' . test . ': ' . v:exception . ' @ ' . v:throwpoint) + endtry + + if len(v:errors) > 0 + let fail += 1 + call add(errors, 'Found errors in ' . test . ':') + call extend(errors, v:errors) + let v:errors = [] + endif + + if exists("*TearDown") + call TearDown() + endif +endfor + +if fail == 0 + " Success, create the .res file so that make knows it's done. + exe 'split ' . fnamemodify(testname, ':r') . '.res' + write +endif + +if len(errors) > 0 + " Append errors to test.log + split test.log + call append(line('$'), '') + call append(line('$'), 'From ' . testname . ':') + call append(line('$'), errors) + write +endif + +let message = 'Executed ' . done . (done > 1 ? ' tests': ' test') +echo message +call add(messages, message) +if fail > 0 + let message = fail . ' FAILED' + echo message + call add(messages, message) +endif + +" Append messages to "messages" +split messages +call append(line('$'), '') +call append(line('$'), 'From ' . testname . ':') +call append(line('$'), messages) +write + +qall! diff --git a/src/nvim/testdir/test11.in b/src/nvim/testdir/test11.in deleted file mode 100644 index 9e9e257c1d..0000000000 --- a/src/nvim/testdir/test11.in +++ /dev/null @@ -1,84 +0,0 @@ -Tests for autocommands: -- FileWritePre writing a compressed file -- FileReadPost reading a compressed file -- BufNewFile reading a file template -- BufReadPre decompressing the file to be read -- FilterReadPre substituting characters in the temp file -- FilterReadPost substituting characters after filtering -- FileReadPre set options for decompression -- FileReadPost decompress the file - -Note: This test is skipped if "gzip" is not available. -$GZIP is made empty, "-v" would cause trouble. -Use a FileChangedShell autocommand to avoid a prompt for "Xtestfile.gz" being -modified outside of Vim (noticed on Solaris). - -STARTTEST -:so small.vim -:" drop out when there is no gzip program -:if !executable("gzip") -: e! test.ok -: w! test.out -: qa! -:endif -:let $GZIP = "" -:au FileChangedShell * echo "caught FileChangedShell" -:set bin -:au FileWritePre *.gz '[,']!gzip -:au FileWritePost *.gz undo -:/^start of testfile/,/^end of testfile/w! Xtestfile.gz -:au FileReadPost *.gz '[,']!gzip -d -:$r Xtestfile.gz " Read and decompress the testfile -:?startstart?,$w! test.out " Write contents of this file -:au BufNewFile *.c read Xtest.c -:/^start of test.c/+1,/^end of test.c/-1w! Xtest.c -:e! foo.c " Will load Xtest.c -:au FileAppendPre *.out '[,']s/new/NEW/ -:au FileAppendPost *.out !cat Xtest.c >>test.out -:w>>test.out " Append it to the output file -:au! FileAppendPre -:" setup autocommands to decompress before reading and re-compress afterwards -:au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand("<afile>")) -:au BufReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>")) -:au BufReadPost *.gz call rename(expand("<afile>"), expand("<afile>:r")) -:au BufReadPost *.gz exe '!gzip ' . shellescape(expand("<afile>:r")) -:e! Xtestfile.gz " Edit compressed file -:w>>test.out " Append it to the output file -:set shelltemp " need temp files here -:au FilterReadPre *.out call rename(expand("<afile>"), expand("<afile>") . ".t") -:au FilterReadPre *.out exe 'silent !sed s/e/E/ ' . shellescape(expand("<afile>")) . ".t >" . shellescape(expand("<afile>")) -:au FilterReadPre *.out exe 'silent !rm ' . shellescape(expand("<afile>")) . '.t' -:au FilterReadPost *.out '[,']s/x/X/g -:e! test.out " Edit the output file -:23,$!cat -:23,$s/\r$// " remove CR for when sed adds them -:au! FileReadPre *.gz exe 'silent !gzip -d ' . shellescape(expand("<afile>")) -:au FileReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>")) -:au! FileReadPost *.gz '[,']s/l/L/ -:$r Xtestfile.gz " Read compressed file -:w " write it, after filtering -:au! " remove all autocommands -:e " Edit test.out again -:set nobin ff& " use the default fileformat for writing -:w -:qa! -ENDTEST - -startstart -start of testfile -line 2 Abcdefghijklmnopqrstuvwxyz -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 Abcdefghijklmnopqrstuvwxyz -line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 6 Abcdefghijklmnopqrstuvwxyz -line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 8 Abcdefghijklmnopqrstuvwxyz -line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 10 Abcdefghijklmnopqrstuvwxyz -end of testfile - -start of test.c -/* - * Here is a new .c file - */ -end of test.c diff --git a/src/nvim/testdir/test11.ok b/src/nvim/testdir/test11.ok deleted file mode 100644 index af8c5ce261..0000000000 --- a/src/nvim/testdir/test11.ok +++ /dev/null @@ -1,61 +0,0 @@ -startstart -start of testfile -line 2 Abcdefghijklmnopqrstuvwxyz -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 Abcdefghijklmnopqrstuvwxyz -line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 6 Abcdefghijklmnopqrstuvwxyz -line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 8 Abcdefghijklmnopqrstuvwxyz -line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 10 Abcdefghijklmnopqrstuvwxyz -end of testfile - -start of test.c -/* - * Here is a new .c file - */ -end of test.c -start of testfile -line 2 Abcdefghijklmnopqrstuvwxyz -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 Abcdefghijklmnopqrstuvwxyz -linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 6 AbcdefghijklmnopqrstuvwXyz -linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 8 AbcdefghijklmnopqrstuvwXyz -linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 10 AbcdefghijklmnopqrstuvwXyz -End of testfile - -/* - * HEre is a NEW .c file - */ -/* - * HEre is a new .c file - */ -start of tEstfile -linE 2 AbcdefghijklmnopqrstuvwXyz -linE 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 4 AbcdefghijklmnopqrstuvwXyz -linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 6 AbcdefghijklmnopqrstuvwXyz -linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 8 AbcdefghijklmnopqrstuvwXyz -linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -linE 10 AbcdefghijklmnopqrstuvwXyz -End of testfile -/* - * HEre is a new .c file - */ -start of testfiLe -Line 2 Abcdefghijklmnopqrstuvwxyz -Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 4 Abcdefghijklmnopqrstuvwxyz -Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 6 Abcdefghijklmnopqrstuvwxyz -Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 8 Abcdefghijklmnopqrstuvwxyz -Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Line 10 Abcdefghijklmnopqrstuvwxyz -end of testfiLe diff --git a/src/nvim/testdir/test13.in b/src/nvim/testdir/test13.in index cb8a6fff89..6713f80e88 100644 --- a/src/nvim/testdir/test13.in +++ b/src/nvim/testdir/test13.in @@ -48,6 +48,12 @@ otestje3 :au BufWipeout Xtestje1 buf Xtestje1 :bwipe :w >>test.out +:only +:new|set buftype=help +:wincmd w +:1quit +:$put ='Final line' +:$w >>test.out :qa! ENDTEST diff --git a/src/nvim/testdir/test13.ok b/src/nvim/testdir/test13.ok index 0f1fc347a4..66ebce63f7 100644 --- a/src/nvim/testdir/test13.ok +++ b/src/nvim/testdir/test13.ok @@ -28,3 +28,4 @@ testje1 contents contents end of testfile +Final line diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in index 7fef87d383..a8c81b832d 100644 --- a/src/nvim/testdir/test17.in +++ b/src/nvim/testdir/test17.in @@ -41,17 +41,17 @@ STARTTEST :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :e! Xdir1/dir2/foo.a -i#include "bar.a" +i#include "bar.a": :w :e Xdir1/dir2/bar.a -i#include "baz.a" +i#include "baz.a": :w :e Xdir1/dir2/baz.a -i#include "foo.a" +i#include "foo.a": :w :e Xbase.a :set path=Xdir1/dir2 -i#include <foo.a> +i#include <foo.a>: :w :redir! >>test.out :checkpath! @@ -71,17 +71,17 @@ STARTTEST :endfunction :let &includeexpr='DotsToSlashes()' :e! Xdir1/dir2/foo.b -i%inc /bar/ +i%inc /bar/: :w :e Xdir1/dir2/bar.b -i%inc /baz/ +i%inc /baz/: :w :e Xdir1/dir2/baz.b -i%inc /foo/ +i%inc /foo/: :w :e Xbase.b :set path=Xdir1/dir2 -i%inc /foo/ +i%inc /foo/: :w :redir! >>test.out :checkpath! @@ -104,20 +104,20 @@ STARTTEST :endfunction :let &includeexpr='StripNewlineChar()' :e! Xdir1/dir2/foo.c -i%inc bar.c +i%inc bar.c: :w :e Xdir1/dir2/bar.c -i%inc baz.c +i%inc baz.c: :w :e Xdir1/dir2/baz.c -i%inc foo.c +i%inc foo.c: :w :e Xdir1/dir2/FALSE.c -i%inc foo.c +i%inc foo.c: :w :e Xbase.c :set path=Xdir1/dir2 -i%inc FALSE.c foo.c +i%inc FALSE.c foo.c: :w :redir! >>test.out :checkpath! diff --git a/src/nvim/testdir/test30.in b/src/nvim/testdir/test30.in index 3f7b9eb472..2a89eac73d 100644 --- a/src/nvim/testdir/test30.in +++ b/src/nvim/testdir/test30.in @@ -7,32 +7,27 @@ STARTTEST :" first write three test files, one in each format :set fileformat=unix :set fileformats= -:/^1/w! XX1 -:/^2/w! XX2 -:/^3/w! XX3 -:/^4/w! XX4 -:/^5/w! XX5 -:/^6/w! XX6 -:/^7/w! XX7 -:/^8/w! XX8 -:/^9/w! XX9 -:/^10/w! XX10 :/^unix/;/eof/-1w! XXUnix :/^dos/;/eof/-1w! XXDos :set bin noeol :$w! XXMac +Gonoeol +:$w! XXEol :set nobin eol +:enew! :bwipe XXUnix XXDos XXMac :" create mixed format files :if has("win32") : !copy /b XXUnix+XXDos XXUxDs : !copy /b XXUnix+XXMac XXUxMac : !copy /b XXDos+XXMac XXDosMac +: !copy /b XXMac+XXEol XXMacEol : !copy /b XXUnix+XXDos+XXMac XXUxDsMc :else : !cat XXUnix XXDos >XXUxDs : !cat XXUnix XXMac >XXUxMac : !cat XXDos XXMac >XXDosMac +: !cat XXMac XXEol >XXMacEol : !cat XXUnix XXDos XXMac >XXUxDsMc :endif :" @@ -97,26 +92,48 @@ STARTTEST :e! XXDosMac :w! XXtt53 :bwipe XXDosMac +:e! XXEol +ggO=&ffs +:=&ff +:w! XXtt54 +:bwipe XXEol :set fileformats=dos,mac :e! XXUxDs :w! XXtt61 :bwipe XXUxDs :e! XXUxMac -:w! XXtt62 +ggO=&ffs +:=&ff +:w! XXtt62 :bwipe XXUxMac :e! XXUxDsMc :w! XXtt63 :bwipe XXUxDsMc +:e! XXMacEol +ggO=&ffs +:=&ff +:w! XXtt64 +:bwipe XXMacEol :" :" try reading and writing with 'fileformats' set to three formats :set fileformats=unix,dos,mac :e! XXUxDsMc :w! XXtt71 :bwipe XXUxDsMc +:e! XXEol +ggO=&ffs +:=&ff +:w! XXtt72 +:bwipe XXEol :set fileformats=mac,dos,unix :e! XXUxDsMc :w! XXtt81 :bwipe XXUxDsMc +:e! XXEol +ggO=&ffs +:=&ff +:w! XXtt82 +:bwipe XXEol :" try with 'binary' set :set fileformats=mac,unix,dos :set binary @@ -150,11 +167,15 @@ ggdGaEND:w >>XXtt01 :w >>XXtt51 :w >>XXtt52 :w >>XXtt53 +:w >>XXtt54 :w >>XXtt61 :w >>XXtt62 :w >>XXtt63 +:w >>XXtt64 :w >>XXtt71 +:w >>XXtt72 :w >>XXtt81 +:w >>XXtt82 :w >>XXtt91 :w >>XXtt92 :w >>XXtt93 @@ -181,11 +202,15 @@ Go4:$r XXtt41 Go5:$r XXtt51 :$r XXtt52 :$r XXtt53 +:$r XXtt54 Go6:$r XXtt61 :$r XXtt62 :$r XXtt63 +:$r XXtt64 Go7:$r XXtt71 +:$r XXtt72 Go8:$r XXtt81 +:$r XXtt82 Go9:$r XXtt91 :$r XXtt92 :$r XXtt93 @@ -195,17 +220,6 @@ Go10:$r XXUnix :qa! ENDTEST -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 - unix unix eof diff --git a/src/nvim/testdir/test30.ok b/src/nvim/testdir/test30.ok index 380ce67061..b35f4f5904 100644 --- a/src/nvim/testdir/test30.ok +++ b/src/nvim/testdir/test30.ok @@ -70,12 +70,16 @@ END dos
dos
mac
mac
END +unix,mac:unix +noeol +END 6 unix
unix
dos
dos
END +dos,mac:dos
unix
unix
mac
mac
@@ -86,6 +90,7 @@ dos dos
mac
mac
END +dos,mac:mac
mac
mac
noeol
END 7 unix unix @@ -93,6 +98,9 @@ dos dos
mac
mac
END +unix,dos,mac:unix +noeol +END 8 unix unix @@ -100,6 +108,7 @@ dos dos
mac
mac
END +mac,dos,unix:mac
noeol
END 9 unix unix diff --git a/src/nvim/testdir/test36.in b/src/nvim/testdir/test36.in deleted file mode 100644 index 8cdb5262bd..0000000000 --- a/src/nvim/testdir/test36.in +++ /dev/null @@ -1,105 +0,0 @@ -Test character classes in regexp using regexpengine 0, 1, 2. - -STARTTEST -/^start-here/+1 -Y:s/\%#=0\d//g -p:s/\%#=1\d//g -p:s/\%#=2\d//g -p:s/\%#=0[0-9]//g -p:s/\%#=1[0-9]//g -p:s/\%#=2[0-9]//g -p:s/\%#=0\D//g -p:s/\%#=1\D//g -p:s/\%#=2\D//g -p:s/\%#=0[^0-9]//g -p:s/\%#=1[^0-9]//g -p:s/\%#=2[^0-9]//g -p:s/\%#=0\o//g -p:s/\%#=1\o//g -p:s/\%#=2\o//g -p:s/\%#=0[0-7]//g -p:s/\%#=1[0-7]//g -p:s/\%#=2[0-7]//g -p:s/\%#=0\O//g -p:s/\%#=1\O//g -p:s/\%#=2\O//g -p:s/\%#=0[^0-7]//g -p:s/\%#=1[^0-7]//g -p:s/\%#=2[^0-7]//g -p:s/\%#=0\x//g -p:s/\%#=1\x//g -p:s/\%#=2\x//g -p:s/\%#=0[0-9A-Fa-f]//g -p:s/\%#=1[0-9A-Fa-f]//g -p:s/\%#=2[0-9A-Fa-f]//g -p:s/\%#=0\X//g -p:s/\%#=1\X//g -p:s/\%#=2\X//g -p:s/\%#=0[^0-9A-Fa-f]//g -p:s/\%#=1[^0-9A-Fa-f]//g -p:s/\%#=2[^0-9A-Fa-f]//g -p:s/\%#=0\w//g -p:s/\%#=1\w//g -p:s/\%#=2\w//g -p:s/\%#=0[0-9A-Za-z_]//g -p:s/\%#=1[0-9A-Za-z_]//g -p:s/\%#=2[0-9A-Za-z_]//g -p:s/\%#=0\W//g -p:s/\%#=1\W//g -p:s/\%#=2\W//g -p:s/\%#=0[^0-9A-Za-z_]//g -p:s/\%#=1[^0-9A-Za-z_]//g -p:s/\%#=2[^0-9A-Za-z_]//g -p:s/\%#=0\h//g -p:s/\%#=1\h//g -p:s/\%#=2\h//g -p:s/\%#=0[A-Za-z_]//g -p:s/\%#=1[A-Za-z_]//g -p:s/\%#=2[A-Za-z_]//g -p:s/\%#=0\H//g -p:s/\%#=1\H//g -p:s/\%#=2\H//g -p:s/\%#=0[^A-Za-z_]//g -p:s/\%#=1[^A-Za-z_]//g -p:s/\%#=2[^A-Za-z_]//g -p:s/\%#=0\a//g -p:s/\%#=1\a//g -p:s/\%#=2\a//g -p:s/\%#=0[A-Za-z]//g -p:s/\%#=1[A-Za-z]//g -p:s/\%#=2[A-Za-z]//g -p:s/\%#=0\A//g -p:s/\%#=1\A//g -p:s/\%#=2\A//g -p:s/\%#=0[^A-Za-z]//g -p:s/\%#=1[^A-Za-z]//g -p:s/\%#=2[^A-Za-z]//g -p:s/\%#=0\l//g -p:s/\%#=1\l//g -p:s/\%#=2\l//g -p:s/\%#=0[a-z]//g -p:s/\%#=1[a-z]//g -p:s/\%#=2[a-z]//g -p:s/\%#=0\L//g -p:s/\%#=1\L//g -p:s/\%#=2\L//g -p:s/\%#=0[^a-z]//g -p:s/\%#=1[^a-z]//g -p:s/\%#=2[^a-z]//g -p:s/\%#=0\u//g -p:s/\%#=1\u//g -p:s/\%#=2\u//g -p:s/\%#=0[A-Z]//g -p:s/\%#=1[A-Z]//g -p:s/\%#=2[A-Z]//g -p:s/\%#=0\U//g -p:s/\%#=1\U//g -p:s/\%#=2\U//g -p:s/\%#=0[^A-Z]//g -p:s/\%#=1[^A-Z]//g -p:s/\%#=2[^A-Z]//g -:/^start-here/+1,$wq! test.out -ENDTEST - -start-here -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé diff --git a/src/nvim/testdir/test36.ok b/src/nvim/testdir/test36.ok deleted file mode 100644 index f72a74b2b7..0000000000 --- a/src/nvim/testdir/test36.ok +++ /dev/null @@ -1,96 +0,0 @@ -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -0123456789 -0123456789 -0123456789 -0123456789 -0123456789 -0123456789 -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -01234567 -01234567 -01234567 -01234567 -01234567 -01234567 -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -0123456789ABCDEFabcdef -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -0123456789ABCDEFGHIXYZ_abcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -ABCDEFGHIXYZ_abcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -ABCDEFGHIXYZabcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -abcdefghiwxyz -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -
!"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ -ABCDEFGHIXYZ diff --git a/src/nvim/testdir/test39.in b/src/nvim/testdir/test39.in deleted file mode 100644 index ebbcbd6d0d..0000000000 --- a/src/nvim/testdir/test39.in +++ /dev/null @@ -1,106 +0,0 @@ - -Test Visual block mode commands -And test "U" in Visual mode, also on German sharp S. - -STARTTEST -:so small.vim -:so mbyte.vim -/^abcde -:" Test shift-right of a block -jlllljj>wlljlll> -:" Test shift-left of a block -G$hhhhkk< -:" Test block-insert -GklkkkIxyz -:" Test block-replace -Gllllkkklllrq -:" Test block-change -G$khhhhhkkcmno -:$-4,$w! test.out -:" Test block-insert using cursor keys for movement -/^aaaa/ -:exe ":norm! l\<C-V>jjjlllI\<Right>\<Right> \<Esc>" -:/^aa/,/^$/w >> test.out -/xaaa$/ -:exe ":norm! \<C-V>jjjI<>\<Left>p\<Esc>" -:/xaaa$/,/^$/w >> test.out -:" Test for Visual block was created with the last <C-v>$ -/^A23$/ -:exe ":norm! l\<C-V>j$Aab\<Esc>" -:.,/^$/w >> test.out -:" Test for Visual block was created with the middle <C-v>$ (1) -/^B23$/ -:exe ":norm! l\<C-V>j$hAab\<Esc>" -:.,/^$/w >> test.out -:" Test for Visual block was created with the middle <C-v>$ (2) -/^C23$/ -:exe ":norm! l\<C-V>j$hhAab\<Esc>" -:.,/^$/w >> test.out -:" Test for Visual block insert when virtualedit=all and utf-8 encoding -:set ve=all -:/\t\tline -:exe ":norm! 07l\<C-V>jjIx\<Esc>" -:.,/^$/w >> test.out -:" Test for Visual block append when virtualedit=all -:exe ":norm! 012l\<C-v>jjAx\<Esc>" -:set ve= -:.,/^$/w >> test.out -:" gUe must uppercase a whole word, also when ß changes to SS -Gothe youtußeuu endYpk0wgUe -:" gUfx must uppercase until x, inclusive. -O- youßtußexu -0fogUfx -:" VU must uppercase a whole line -YpkVU -:" same, when it's the last line in the buffer -YPGi111VUddP -:" Uppercase two lines -Oblah di -doh dutVkUj -:" Uppercase part of two lines -ddppi333k0i222fyllvjfuUk -:" visual replace using Enter or NL -G3o1234567892k05l2jr
G3o987652k02l2jr
-G3o1234567892k05l2jr -G3o987652k02l2jr -:" -:" Test cursor position. When ve=block and Visual block mode and $gj -:set ve=block -:exe ":norm! 2k\<C-V>$gj\<Esc>" -:let cpos=getpos("'>") -:$put ='col:'.cpos[2].' off:'.cpos[3] -:/^the/,$w >> test.out -:qa! -ENDTEST - - line1 - line2 - line3 - -aaaaaa -bbbbbb -cccccc -dddddd - -xaaa -bbbb -cccc -dddd - -yaaa -¿¿¿ -bbb - -A23 -4567 - -B23 -4567 - -C23 -4567 - -abcdefghijklm -abcdefghijklm -abcdefghijklm -abcdefghijklm -abcdefghijklm diff --git a/src/nvim/testdir/test39.ok b/src/nvim/testdir/test39.ok Binary files differdeleted file mode 100644 index 198e5b14dc..0000000000 --- a/src/nvim/testdir/test39.ok +++ /dev/null diff --git a/src/nvim/testdir/test45.in b/src/nvim/testdir/test45.in deleted file mode 100644 index e5af5073d9..0000000000 --- a/src/nvim/testdir/test45.in +++ /dev/null @@ -1,80 +0,0 @@ -Tests for folding. vim: set ft=vim : - -STARTTEST -:so small.vim -:" We also need the +syntax feature here. -:if !has("syntax") - e! test.ok - w! test.out - qa! -:endif -:" basic test if a fold can be created, opened, moving to the end and closed -/^1 -zf2j:call append("$", "manual " . getline(foldclosed("."))) -zo:call append("$", foldclosed(".")) -]z:call append("$", getline(".")) -zc:call append("$", getline(foldclosed("."))) -:" test folding with markers. -:set fdm=marker fdl=1 fdc=3 -/^5 -:call append("$", "marker " . foldlevel(".")) -[z:call append("$", foldlevel(".")) -jo{{ r{jj:call append("$", foldlevel(".")) -kYpj:call append("$", foldlevel(".")) -:" test folding with indent -:set fdm=indent sw=2 -/^2 b -i jI :call append("$", "indent " . foldlevel(".")) -k:call append("$", foldlevel(".")) -:" test syntax folding -:set fdm=syntax fdl=0 -:syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3 -:syn region Fd1 start="ee" end="ff" fold contained -:syn region Fd2 start="gg" end="hh" fold contained -:syn region Fd3 start="commentstart" end="commentend" fold contained -Gzk:call append("$", "folding " . getline(".")) -k:call append("$", getline(".")) -jAcommentstart Acommentend:set fdl=1 -3j:call append("$", getline(".")) -:set fdl=0 -zOj:call append("$", getline(".")) -:" test expression folding -:fun Flvl() - let l = getline(v:lnum) - if l =~ "bb$" - return 2 - elseif l =~ "gg$" - return "s1" - elseif l =~ "ii$" - return ">2" - elseif l =~ "kk$" - return "0" - endif - return "=" -endfun -:set fdm=expr fde=Flvl() -/bb$ -:call append("$", "expr " . foldlevel(".")) -/hh$ -:call append("$", foldlevel(".")) -/ii$ -:call append("$", foldlevel(".")) -/kk$ -:call append("$", foldlevel(".")) -:/^last/+1,$w! test.out -:delfun Flvl -:qa! -ENDTEST - -1 aa -2 bb -3 cc -4 dd {{{ -5 ee {{{ }}} -6 ff }}} -7 gg -8 hh -9 ii -a jj -b kk -last diff --git a/src/nvim/testdir/test45.ok b/src/nvim/testdir/test45.ok deleted file mode 100644 index f04996e337..0000000000 --- a/src/nvim/testdir/test45.ok +++ /dev/null @@ -1,18 +0,0 @@ -manual 1 aa --1 -3 cc -1 aa -marker 2 -1 -1 -0 -indent 2 -1 -folding 9 ii - 3 cc -7 gg -8 hh -expr 2 -1 -2 -0 diff --git a/src/nvim/testdir/test47.in b/src/nvim/testdir/test47.in index 13ad82462f..f15eaf0f8f 100644 --- a/src/nvim/testdir/test47.in +++ b/src/nvim/testdir/test47.in @@ -1,5 +1,7 @@ Tests for vertical splits and filler lines in diff mode +Also tests restoration of saved options by :diffoff. + STARTTEST :so small.vim :" Disable the title to avoid xterm keeping the wrong one. @@ -10,8 +12,19 @@ pkdd:w! Xtest ddGpkkrXoxxx:w! Xtest2 :file Nop ggoyyyjjjozzzz +:set foldmethod=marker foldcolumn=4 +:redir => nodiffsettings +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END :vert diffsplit Xtest :vert diffsplit Xtest2 +:redir => diffsettings +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END +:let diff_fdm = &fdm +:let diff_fdc = &fdc +:" repeat entering diff mode here to see if this saves the wrong settings +:diffthis :" jump to second window for a moment to have filler line appear at start of :" first window ggpgg:let one = winline() @@ -36,8 +49,36 @@ j:let three = three . "-" . winline() :call append("$", two) :call append("$", three) :$-2,$w! test.out -:" Test that diffing shows correct filler lines +:" +:" Test diffoff :diffoff! +1 +:let &diff = 1 +:let &fdm = diff_fdm +:let &fdc = diff_fdc +4 +:diffoff! +:$put =nodiffsettings +:$put =diffsettings +1 +:redir => nd1 +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END + +:redir => nd2 +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END + +:redir => nd3 +:silent! :set diff? fdm? fdc? scb? crb? wrap? +:redir END + +:$put =nd1 +:$put =nd2 +:$put =nd3 +:$-39,$w >> test.out +:" +:" Test that diffing shows correct filler lines :windo :bw! :enew :put =range(4,10) @@ -51,7 +92,7 @@ j:let three = three . "-" . winline() :enew :put =w0 :.w >> test.out -:unlet! one two three w0 +:unlet! one two three nodiffsettings diffsettings diff_fdm diff_fdc nd1 nd2 nd3 w0 :qa! ENDTEST diff --git a/src/nvim/testdir/test47.ok b/src/nvim/testdir/test47.ok index b1cba92b1c..83e96571ad 100644 --- a/src/nvim/testdir/test47.ok +++ b/src/nvim/testdir/test47.ok @@ -1,4 +1,44 @@ 2-4-5-6-8-9 1-2-4-5-8 2-3-4-5-6-7-8 + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap + + + diff + foldmethod=diff + foldcolumn=2 + scrollbind + cursorbind +nowrap + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap + + +nodiff + foldmethod=marker + foldcolumn=4 +noscrollbind +nocursorbind + wrap 1 diff --git a/src/nvim/testdir/test49.in b/src/nvim/testdir/test49.in index 1ce57246ee..d95052e14c 100644 --- a/src/nvim/testdir/test49.in +++ b/src/nvim/testdir/test49.in @@ -8,7 +8,9 @@ STARTTEST :se nomore :lang mess C :so test49.vim -GGGGGGGGGGGGGG"rp:.-,$w! test.out +:" Go back to this file and append the results from register r. +:buf test49.in +G"rp:/^Results/,$w! test.out :" :" make valgrind happy :redir => funclist diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok index bf1ceed9af..50fc5d2cef 100644 --- a/src/nvim/testdir/test49.ok +++ b/src/nvim/testdir/test49.ok @@ -1,19 +1,4 @@ Results of test49.vim: -*** Test 1: OK (34695) -*** Test 2: OK (34695) -*** Test 3: OK (1384648195) -*** Test 4: OK (32883) -*** Test 5: OK (32883) -*** Test 6: OK (603978947) -*** Test 7: OK (90563) -*** Test 8: OK (562493431) -*** Test 9: OK (363) -*** Test 10: OK (559615) -*** Test 11: OK (2049) -*** Test 12: OK (352256) -*** Test 13: OK (145) -*** Test 14: OK (42413) -*** Test 15: OK (42413) *** Test 16: OK (8722) *** Test 17: OK (285127993) *** Test 18: OK (67224583) diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim index afee9d882c..edd49a2b63 100644 --- a/src/nvim/testdir/test49.vim +++ b/src/nvim/testdir/test49.vim @@ -1,6 +1,6 @@ " Vim script language tests " Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com> -" Last Change: 2013 Jun 06 +" Last Change: 2016 Feb 07 "------------------------------------------------------------------------------- " Test environment {{{1 @@ -608,850 +608,8 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) " END_OF_TEST_ENVIRONMENT - do not change or remove this line. -"------------------------------------------------------------------------------- -" Test 1: :endwhile in function {{{1 -" -" Detect if a broken loop is (incorrectly) reactivated by the -" :endwhile. Use a :return to prevent an endless loop, and make -" this test first to get a meaningful result on an error before other -" tests will hang. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - Xpath 1 " X: 1 - let first = 1 - XloopINIT 2 8 - while 1 - Xloop 1 " X: 2 + 0 * 16 - if first - Xloop 2 " X: 4 + 0 * 32 - let first = 0 - XloopNEXT - break - else - Xloop 4 " X: 0 + 0 * 64 - return - endif - endwhile -endfunction - -call F() -Xpath 128 " X: 128 - -function! G() - Xpath 256 " X: 256 + 0 * 2048 - let first = 1 - XloopINIT 512 8 - while 1 - Xloop 1 " X: 512 + 0 * 4096 - if first - Xloop 2 " X: 1024 + 0 * 8192 - let first = 0 - XloopNEXT - break - else - Xloop 4 " X: 0 + 0 * 16384 - return - endif - if 1 " unmatched :if - endwhile -endfunction - -call G() -Xpath 32768 " X: 32768 - -Xcheck 34695 - -" Leave F and G for execution as scripts in the next test. - - -"------------------------------------------------------------------------------- -" Test 2: :endwhile in script {{{1 -" -" Detect if a broken loop is (incorrectly) reactivated by the -" :endwhile. Use a :finish to prevent an endless loop, and place -" this test before others that might hang to get a meaningful result -" on an error. -" -" This test executes the bodies of the functions F and G from the -" previous test as script files (:return replaced by :finish). -"------------------------------------------------------------------------------- - -XpathINIT - -ExecAsScript F " X: 1 + 2 + 4 -Xpath 128 " X: 128 - -ExecAsScript G " X: 256 + 512 + 1024 -Xpath 32768 " X: 32768 - -unlet first -delfunction F -delfunction G - -Xcheck 34695 - - -"------------------------------------------------------------------------------- -" Test 3: :if, :elseif, :while, :continue, :break {{{1 -"------------------------------------------------------------------------------- - -XpathINIT -if 1 - Xpath 1 " X: 1 - let loops = 3 - XloopINIT 2 512 - while loops > -1 " main loop: loops == 3, 2, 1 (which breaks) - if loops <= 0 - let break_err = 1 - let loops = -1 - else " 3: 2: 1: - Xloop 1 " X: 2 + 2*512 + 2*512*512 - endif - if (loops == 2) - while loops == 2 " dummy loop - Xloop 2 " X: 4*512 - let loops = loops - 1 - continue " stop dummy loop - Xloop 4 " X: 0 - endwhile - XloopNEXT - continue " continue main loop - Xloop 8 " X: 0 - elseif (loops == 1) - let p = 1 - while p " dummy loop - Xloop 16 " X: 32*512*512 - let p = 0 - break " break dummy loop - Xloop 32 " X: 0 - endwhile - Xloop 64 " X: 128*512*512 - unlet p - break " break main loop - Xloop 128 " X: 0 - endif - if (loops > 0) - Xloop 256 " X: 512 - endif - while loops == 3 " dummy loop - let loops = loops - 1 - endwhile " end dummy loop - XloopNEXT - endwhile " end main loop - Xpath 268435456 " X: 1024*512*512 -else - Xpath 536870912 " X: 0 -endif -Xpath 1073741824 " X: 4096*512*512 -if exists("break_err") - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - unlet break_err -endif - -unlet loops - -Xcheck 1384648195 - - -"------------------------------------------------------------------------------- -" Test 4: :return {{{1 -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - if 1 - Xpath 1 " X: 1 - let loops = 3 - XloopINIT 2 16 - while loops > 0 " 3: 2: 1: - Xloop 1 " X: 2 + 2*16 + 0*16*16 - if (loops == 2) - Xloop 2 " X: 4*16 - return - Xloop 4 " X: 0 - endif - Xloop 8 " X: 16 - let loops = loops - 1 - XloopNEXT - endwhile - Xpath 8192 " X: 0 - else - Xpath 16384 " X: 0 - endif -endfunction - -call F() -Xpath 32768 " X: 8*16*16*16 - -Xcheck 32883 - -" Leave F for execution as a script in the next test. - - -"------------------------------------------------------------------------------- -" Test 5: :finish {{{1 -" -" This test executes the body of the function F from the previous test -" as a script file (:return replaced by :finish). -"------------------------------------------------------------------------------- - -XpathINIT - -ExecAsScript F " X: 1 + 2 + 2*16 + 4*16 + 16 -Xpath 32768 " X: 32768 - -unlet loops -delfunction F - -Xcheck 32883 - - -"------------------------------------------------------------------------------- -" Test 6: Defining functions in :while loops {{{1 -" -" Functions can be defined inside other functions. An inner function -" gets defined when the outer function is executed. Functions may -" also be defined inside while loops. Expressions in braces for -" defining the function name are allowed. -"------------------------------------------------------------------------------- - -XpathINIT - -if ExtraVim() - - " The command CALL collects the argument of all its invocations in "calls" - " when used from a function (that is, when the global variable "calls" needs - " the "g:" prefix). This is to check that the function code is skipped when - " the function is defined. For inner functions, do so only if the outer - " function is not being executed. - " - let calls = "" - com! -nargs=1 CALL - \ if !exists("calls") && !exists("outer") | - \ let g:calls = g:calls . <args> | - \ endif - - - XloopINIT! 1 16 - - let i = 0 - while i < 3 - - XloopNEXT - let i = i + 1 - - if i == 1 - Xloop 1 " X: 1 - function! F1(arg) - CALL a:arg - let outer = 1 - - XloopINIT! 4096 4 - let j = 0 - while j < 1 - XloopNEXT - Xloop 1 " X: 4096 - let j = j + 1 - function! G1(arg) - CALL a:arg - endfunction - Xloop 2 " X: 8192 - endwhile - endfunction - Xloop 2 " X: 2 - - continue - endif - - Xloop 4 " X: 4 * (16 + 256) - function! F{i}(i, arg) - CALL a:arg - let outer = 1 - - XloopINIT! 16384 4 - if a:i == 3 - XloopNEXT - XloopNEXT - XloopNEXT - endif - let k = 0 - while k < 3 - XloopNEXT - Xloop 1 " X: 16384*(1+4+16+64+256+1024) - let k = k + 1 - function! G{a:i}{k}(arg) - CALL a:arg - endfunction - Xloop 2 " X: 32768*(1+4+16+64+256+1024) - endwhile - endfunction - Xloop 8 " X: 8 * (16 + 256) - - endwhile - - if exists("*G1") - Xpath 67108864 " X: 0 - endif - if exists("*F1") - call F1("F1") - if exists("*G1") - call G1("G1") - endif - endif - - if exists("G21") || exists("G21") || exists("G21") - Xpath 134217728 " X: 0 - endif - if exists("*F2") - call F2(2, "F2") - if exists("*G21") - call G21("G21") - endif - if exists("*G22") - call G22("G22") - endif - if exists("*G23") - call G23("G23") - endif - endif - - if exists("G31") || exists("G31") || exists("G31") - Xpath 268435456 " X: 0 - endif - if exists("*F3") - call F3(3, "F3") - if exists("*G31") - call G31("G31") - endif - if exists("*G32") - call G32("G32") - endif - if exists("*G33") - call G33("G33") - endif - endif - - Xpath 536870912 " X: 536870912 - - if calls != "F1G1F2G21G22G23F3G31G32G33" - Xpath 1073741824 " X: 0 - Xout "calls is" calls - endif - - delfunction F1 - delfunction G1 - delfunction F2 - delfunction G21 - delfunction G22 - delfunction G23 - delfunction G31 - delfunction G32 - delfunction G33 - -endif - -Xcheck 603978947 - - -"------------------------------------------------------------------------------- -" Test 7: Continuing on errors outside functions {{{1 -" -" On an error outside a function, the script processing continues -" at the line following the outermost :endif or :endwhile. When not -" inside an :if or :while, the script processing continues at the next -" line. -"------------------------------------------------------------------------------- - -XpathINIT - -if 1 - Xpath 1 " X: 1 - while 1 - Xpath 2 " X: 2 - asdf - Xpath 4 " X: 0 - break - endwhile | Xpath 8 " X: 0 - Xpath 16 " X: 0 -endif | Xpath 32 " X: 0 -Xpath 64 " X: 64 - -while 1 - Xpath 128 " X: 128 - if 1 - Xpath 256 " X: 256 - asdf - Xpath 512 " X: 0 - endif | Xpath 1024 " X: 0 - Xpath 2048 " X: 0 - break -endwhile | Xpath 4096 " X: 0 -Xpath 8192 " X: 8192 - -asdf -Xpath 16384 " X: 16384 - -asdf | Xpath 32768 " X: 0 -Xpath 65536 " X: 65536 - -Xcheck 90563 - - -"------------------------------------------------------------------------------- -" Test 8: Aborting and continuing on errors inside functions {{{1 -" -" On an error inside a function without the "abort" attribute, the -" script processing continues at the next line (unless the error was -" in a :return command). On an error inside a function with the -" "abort" attribute, the function is aborted and the script processing -" continues after the function call; the value -1 is returned then. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - if 1 - Xpath 1 " X: 1 - while 1 - Xpath 2 " X: 2 - asdf - Xpath 4 " X: 4 - asdf | Xpath 8 " X: 0 - Xpath 16 " X: 16 - break - endwhile - Xpath 32 " X: 32 - endif | Xpath 64 " X: 64 - Xpath 128 " X: 128 - - while 1 - Xpath 256 " X: 256 - if 1 - Xpath 512 " X: 512 - asdf - Xpath 1024 " X: 1024 - asdf | Xpath 2048 " X: 0 - Xpath 4096 " X: 4096 - endif - Xpath 8192 " X: 8192 - break - endwhile | Xpath 16384 " X: 16384 - Xpath 32768 " X: 32768 - - return novar " returns (default return value 0) - Xpath 65536 " X: 0 - return 1 " not reached -endfunction - -function! G() abort - if 1 - Xpath 131072 " X: 131072 - while 1 - Xpath 262144 " X: 262144 - asdf " returns -1 - Xpath 524288 " X: 0 - break - endwhile - Xpath 1048576 " X: 0 - endif | Xpath 2097152 " X: 0 - Xpath Xpath 4194304 " X: 0 - - return -4 " not reached -endfunction - -function! H() abort - while 1 - Xpath 8388608 " X: 8388608 - if 1 - Xpath 16777216 " X: 16777216 - asdf " returns -1 - Xpath 33554432 " X: 0 - endif - Xpath 67108864 " X: 0 - break - endwhile | Xpath 134217728 " X: 0 - Xpath 268435456 " X: 0 - - return -4 " not reached -endfunction - -" Aborted functions (G and H) return -1. -let sum = (F() + 1) - 4*G() - 8*H() -Xpath 536870912 " X: 536870912 -if sum != 13 - Xpath 1073741824 " X: 0 - Xout "sum is" sum -endif - -unlet sum -delfunction F -delfunction G -delfunction H - -Xcheck 562493431 - - -"------------------------------------------------------------------------------- -" Test 9: Continuing after aborted functions {{{1 -" -" When a function with the "abort" attribute is aborted due to an -" error, the next function back in the call hierarchy without an -" "abort" attribute continues; the value -1 is returned then. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() abort - Xpath 1 " X: 1 - let result = G() " not aborted - Xpath 2 " X: 2 - if result != 2 - Xpath 4 " X: 0 - endif - return 1 -endfunction - -function! G() " no abort attribute - Xpath 8 " X: 8 - if H() != -1 " aborted - Xpath 16 " X: 0 - endif - Xpath 32 " X: 32 - return 2 -endfunction - -function! H() abort - Xpath 64 " X: 64 - call I() " aborted - Xpath 128 " X: 0 - return 4 -endfunction - -function! I() abort - Xpath 256 " X: 256 - asdf " error - Xpath 512 " X: 0 - return 8 -endfunction - -if F() != 1 - Xpath 1024 " X: 0 -endif - -delfunction F -delfunction G -delfunction H -delfunction I - -Xcheck 363 - - -"------------------------------------------------------------------------------- -" Test 10: :if, :elseif, :while argument parsing {{{1 -" -" A '"' or '|' in an argument expression must not be mixed up with -" a comment or a next command after a bar. Parsing errors should -" be recognized. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -if 1 || strlen("\"") | Xpath 1 " X: 1 - Xpath 2 " X: 2 -endif -Xpath 4 " X: 4 - -if 0 -elseif 1 || strlen("\"") | Xpath 8 " X: 8 - Xpath 16 " X: 16 -endif -Xpath 32 " X: 32 - -while 1 || strlen("\"") | Xpath 64 " X: 64 - Xpath 128 " X: 128 - break -endwhile -Xpath 256 " X: 256 - -let v:errmsg = "" -if 1 ||| strlen("\"") | Xpath 512 " X: 0 - Xpath 1024 " X: 0 -endif -Xpath 2048 " X: 2048 -if !MSG('E15', "Invalid expression") - Xpath 4096 " X: 0 -endif - -let v:errmsg = "" -if 0 -elseif 1 ||| strlen("\"") | Xpath 8192 " X: 0 - Xpath 16384 " X: 0 -endif -Xpath 32768 " X: 32768 -if !MSG('E15', "Invalid expression") - Xpath 65536 " X: 0 -endif - -let v:errmsg = "" -while 1 ||| strlen("\"") | Xpath 131072 " X: 0 - Xpath 262144 " X: 0 - break -endwhile -Xpath 524288 " X: 524288 -if !MSG('E15', "Invalid expression") - Xpath 1048576 " X: 0 -endif - -delfunction MSG - -Xcheck 559615 - - -"------------------------------------------------------------------------------- -" Test 11: :if, :elseif, :while argument evaluation after abort {{{1 -" -" When code is skipped over due to an error, the boolean argument to -" an :if, :elseif, or :while must not be evaluated. -"------------------------------------------------------------------------------- - -XpathINIT - -let calls = 0 - -function! P(num) - let g:calls = g:calls + a:num " side effect on call - return 0 -endfunction - -if 1 - Xpath 1 " X: 1 - asdf " error - Xpath 2 " X: 0 - if P(1) " should not be called - Xpath 4 " X: 0 - elseif !P(2) " should not be called - Xpath 8 " X: 0 - else - Xpath 16 " X: 0 - endif - Xpath 32 " X: 0 - while P(4) " should not be called - Xpath 64 " X: 0 - endwhile - Xpath 128 " X: 0 -endif - -if calls % 2 - Xpath 256 " X: 0 -endif -if (calls/2) % 2 - Xpath 512 " X: 0 -endif -if (calls/4) % 2 - Xpath 1024 " X: 0 -endif -Xpath 2048 " X: 2048 - -unlet calls -delfunction P - -Xcheck 2049 - - -"------------------------------------------------------------------------------- -" Test 12: Expressions in braces in skipped code {{{1 -" -" In code skipped over due to an error or inactive conditional, -" an expression in braces as part of a variable or function name -" should not be evaluated. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 8 - -function! NULL() - Xloop 1 " X: 0 - return 0 -endfunction - -function! ZERO() - Xloop 2 " X: 0 - return 0 -endfunction - -function! F0() - Xloop 4 " X: 0 -endfunction - -function! F1(arg) - Xpath 4096 " X: 0 -endfunction - -let V0 = 1 - -Xpath 8192 " X: 8192 -echo 0 ? F{NULL() + V{ZERO()}}() : 1 -XloopNEXT - -Xpath 16384 " X: 16384 -if 0 - Xpath 32768 " X: 0 - call F{NULL() + V{ZERO()}}() -endif -XloopNEXT - -Xpath 65536 " X: 65536 -if 1 - asdf " error - Xpath 131072 " X: 0 - call F1(F{NULL() + V{ZERO()}}()) -endif -XloopNEXT - -Xpath 262144 " X: 262144 -if 1 - asdf " error - Xpath 524288 " X: 0 - call F{NULL() + V{ZERO()}}() -endif - -Xcheck 352256 - - -"------------------------------------------------------------------------------- -" Test 13: Failure in argument evaluation for :while {{{1 -" -" A failure in the expression evaluation for the condition of a :while -" causes the whole :while loop until the matching :endwhile being -" ignored. Continuation is at the next following line. -"------------------------------------------------------------------------------- - -XpathINIT - -Xpath 1 " X: 1 -while asdf - Xpath 2 " X: 0 - while 1 - Xpath 4 " X: 0 - break - endwhile - Xpath 8 " X: 0 - break -endwhile -Xpath 16 " X: 16 - -while asdf | Xpath 32 | endwhile | Xpath 64 " X: 0 -Xpath 128 " X: 128 - -Xcheck 145 - - -"------------------------------------------------------------------------------- -" Test 14: Failure in argument evaluation for :if {{{1 -" -" A failure in the expression evaluation for the condition of an :if -" does not cause the corresponding :else or :endif being matched to -" a previous :if/:elseif. Neither of both branches of the failed :if -" are executed. -"------------------------------------------------------------------------------- - -XpathINIT -XloopINIT 1 256 - -function! F() - Xloop 1 " X: 1 + 256 * 1 - let x = 0 - if x " false - Xloop 2 " X: 0 + 256 * 0 - elseif !x " always true - Xloop 4 " X: 4 + 256 * 4 - let x = 1 - if g:boolvar " possibly undefined - Xloop 8 " X: 8 + 256 * 0 - else - Xloop 16 " X: 0 + 256 * 0 - endif - Xloop 32 " X: 32 + 256 * 32 - elseif x " never executed - Xloop 64 " X: 0 + 256 * 0 - endif - Xloop 128 " X: 128 + 256 * 128 -endfunction - -let boolvar = 1 -call F() - -XloopNEXT -unlet boolvar -call F() - -delfunction F - -Xcheck 42413 - - -"------------------------------------------------------------------------------- -" Test 15: Failure in argument evaluation for :if (bar) {{{1 -" -" Like previous test, except that the failing :if ... | ... | :endif -" is in a single line. -"------------------------------------------------------------------------------- - -XpathINIT -XloopINIT 1 256 - -function! F() - Xloop 1 " X: 1 + 256 * 1 - let x = 0 - if x " false - Xloop 2 " X: 0 + 256 * 0 - elseif !x " always true - Xloop 4 " X: 4 + 256 * 4 - let x = 1 - if g:boolvar | Xloop 8 | else | Xloop 16 | endif " X: 8 - Xloop 32 " X: 32 + 256 * 32 - elseif x " never executed - Xloop 64 " X: 0 + 256 * 0 - endif - Xloop 128 " X: 128 + 256 * 128 -endfunction - -let boolvar = 1 -call F() - -XloopNEXT -unlet boolvar -call F() - -delfunction F - -Xcheck 42413 - +" Tests 1 to 15 were moved to test_viml.vim +let Xtest = 16 "------------------------------------------------------------------------------- " Test 16: Double :else or :elseif after :else {{{1 @@ -5188,19 +4346,19 @@ catch /.*/ Xpath 65536 " X: 65536 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(1, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(1, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') exec "let exception = v:exception" exec "let throwpoint = v:throwpoint" - call CHECK(2, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(2, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') CmdException CmdThrowpoint - call CHECK(3, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(3, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') call FuncException() call FuncThrowpoint() - call CHECK(4, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(4, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') exec "source" scriptException exec "source" scriptThrowPoint - call CHECK(5, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(5, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') try Xpath 131072 " X: 131072 call G("arrgh", 4) @@ -5208,7 +4366,7 @@ catch /.*/ Xpath 262144 " X: 262144 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(6, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(6, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') try Xpath 524288 " X: 524288 let g:arg = "autsch" @@ -5226,7 +4384,7 @@ catch /.*/ Xpath 2097152 " X: 2097152 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(8, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(8, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') try Xpath 4194304 " X: 4194304 let g:arg = "brrrr" @@ -5242,27 +4400,27 @@ catch /.*/ Xpath 16777216 " X: 16777216 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(10, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(10, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') endtry Xpath 33554432 " X: 33554432 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(11, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(11, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') endtry Xpath 67108864 " X: 67108864 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(12, "arrgh", '\<G\.\.T\>', '\<4\>') + call CHECK(12, "arrgh", '\<G\[1]\.\.T\>', '\<4\>') finally Xpath 134217728 " X: 134217728 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(13, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(13, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') endtry Xpath 268435456 " X: 268435456 let exception = v:exception let throwpoint = v:throwpoint - call CHECK(14, "oops", '\<F\.\.G\.\.T\>', '\<2\>') + call CHECK(14, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>') finally Xpath 536870912 " X: 536870912 let exception = v:exception @@ -6591,8 +5749,7 @@ function! F() if !caught && !$VIMNOERRTHROW Xpath 8192 " X: 0 endif - if caught ? !MSG('E55', 'Unmatched \\)') - \ : !MSG('E475', "Invalid argument") + if !MSG('E475', "Invalid argument") Xpath 16384 " X: 0 endif if !caught diff --git a/src/nvim/testdir/test53.in b/src/nvim/testdir/test53.in index 8ca9c9ed29..7c35b2e853 100644 --- a/src/nvim/testdir/test53.in +++ b/src/nvim/testdir/test53.in @@ -23,6 +23,7 @@ jfXdit 0fXdit fXdat 0fXdat +dit :" :put =matchstr(\"abcd\", \".\", 0, 2) " b :put =matchstr(\"abcd\", \"..\", 0, 2) " bc @@ -97,6 +98,9 @@ voo "nah" sdf " asdf" sdf " sdf" sd -<b>asdX<i>a<i />sdf</i>asdf</b>- -<b>asdf<i>Xasdf</i>asdf</b>- -<b>asdX<i>as<b />df</i>asdf</b>- +-<b> +innertext object +</b> </begin> SEARCH: foobar diff --git a/src/nvim/testdir/test53.ok b/src/nvim/testdir/test53.ok index 0c0b9ded16..05206972a4 100644 --- a/src/nvim/testdir/test53.ok +++ b/src/nvim/testdir/test53.ok @@ -11,6 +11,7 @@ voo "zzzzzzzzzzzzzzzzzzzzzzzzzzzzsd -<b></b>- -<b>asdfasdf</b>- -- +-<b></b> </begin> b bc diff --git a/src/nvim/testdir/test55.in b/src/nvim/testdir/test55.in index c4e82d429c..9e3c1168cc 100644 --- a/src/nvim/testdir/test55.in +++ b/src/nvim/testdir/test55.in @@ -282,6 +282,177 @@ let l = [0, 1, 2, 3] : $put =ps : endfor :endfor +:" +:" Unletting locked variables +:$put ='Unletting:' +:for depth in range(5) +: $put ='depth is ' . depth +: for u in range(3) +: unlet l +: let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] +: exe "lockvar " . depth . " l" +: if u == 1 +: exe "unlockvar l" +: elseif u == 2 +: exe "unlockvar " . depth . " l" +: endif +: let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") +: $put =ps +: let ps = '' +: try +: unlet l[2]['6'][7] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[2][6] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[2] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[1][1][0] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[1][1] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[1] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: $put =ps +: endfor +:endfor +:" +:" Locked variables and :unlet or list / dict functions +:$put ='Locks and commands or functions:' +:" +:$put ='No :unlet after lock on dict:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar 1 d +:try +: unlet d.a +: $put ='did :unlet' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put =':unlet after lock on dict item:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar d.a +:try +: unlet d.a +: $put ='did :unlet' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put ='filter() after lock on dict item:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar d.a +:try +: call filter(d, 'v:key != "a"') +: $put ='did filter()' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put ='map() after lock on dict:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar 1 d +:try +: call map(d, 'v:val + 200') +: $put ='did map()' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put ='No extend() after lock on dict item:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar d.a +:try +: $put =string(extend(d, {'a': 123})) +: $put ='did extend()' +:catch +: $put =v:exception[:14] +:endtry +:$put =string(d) +:" +:$put ='No remove() of write-protected scope-level variable:' +:fun! Tfunc(this_is_a_loooooooooong_parameter_name) +: try +: $put =string(remove(a:, 'this_is_a_loooooooooong_parameter_name')) +: $put ='did remove()' +: catch +: $put =v:exception[:14] +: endtry +:endfun +:call Tfunc('testval') +:" +:$put ='No extend() of write-protected scope-level variable:' +:fun! Tfunc(this_is_a_loooooooooong_parameter_name) +: try +: $put =string(extend(a:, {'this_is_a_loooooooooong_parameter_name': 1234})) +: $put ='did extend()' +: catch +: $put =v:exception[:14] +: endtry +:endfun +:call Tfunc('testval') +:" +:$put ='No :unlet of variable in locked scope:' +:let b:testvar = 123 +:lockvar 1 b: +:try +: unlet b:testvar +: $put ='b:testvar was :unlet: '. (!exists('b:testvar')) +:catch +: $put =v:exception[:16] +:endtry +:unlockvar 1 b: +:unlet! b:testvar +:" +:$put ='No :let += of locked list variable:' +:let l = ['a', 'b', 3] +:lockvar 1 l +:try +: let l += ['x'] +: $put ='did :let +=' +:catch +: $put =v:exception[:14] +:endtry +:$put =string(l) +:" :unlet l :let l = [1, 2, 3, 4] :lockvar! l diff --git a/src/nvim/testdir/test55.ok b/src/nvim/testdir/test55.ok index ba029b2898..607a95ead9 100644 --- a/src/nvim/testdir/test55.ok +++ b/src/nvim/testdir/test55.ok @@ -86,6 +86,67 @@ FFFFFFF FFpFFpp 0000-000 ppppppp +Unletting: +depth is 0 +0000-000 +ppppppp +0000-000 +ppppppp +0000-000 +ppppppp +depth is 1 +1000-000 +ppFppFp +0000-000 +ppppppp +0000-000 +ppppppp +depth is 2 +1100-100 +pFFpFFp +0000-000 +ppppppp +0000-000 +ppppppp +depth is 3 +1110-110 +FFFFFFp +0010-010 +FppFppp +0000-000 +ppppppp +depth is 4 +1111-111 +FFFFFFp +0011-011 +FppFppp +0000-000 +ppppppp +Locks and commands or functions: +No :unlet after lock on dict: +Vim(unlet):E741: +{'a': 99, 'b': 100} +:unlet after lock on dict item: +did :unlet +{'b': 100} +filter() after lock on dict item: +did filter() +{'b': 100} +map() after lock on dict: +did map() +{'a': 299, 'b': 300} +No extend() after lock on dict item: +Vim(put):E741: +{'a': 99, 'b': 100} +No remove() of write-protected scope-level variable: +Vim(put):E795: +No extend() of write-protected scope-level variable: +Vim(put):E742: +No :unlet of variable in locked scope: +Vim(unlet):E741: +No :let += of locked list variable: +Vim(let):E741: +['a', 'b', 3] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] diff --git a/src/nvim/testdir/test68.in b/src/nvim/testdir/test68.in deleted file mode 100644 index ca54e942b5..0000000000 --- a/src/nvim/testdir/test68.in +++ /dev/null @@ -1,131 +0,0 @@ -Test for text formatting. - -Results of test68: - -STARTTEST -:so small.vim -/^{/+1 -:set noai tw=2 fo=t -gRa b -ENDTEST - -{ - - -} - -STARTTEST -/^{/+1 -:set ai tw=2 fo=tw -gqgqjjllab -ENDTEST - -{ -a b - -a -} - -STARTTEST -/^{/+1 -:set tw=3 fo=t -gqgqo -a -ENDTEST - -{ -a -} - -STARTTEST -/^{/+1 -:set tw=2 fo=tcq1 comments=:# -gqgqjgqgqo -a b -#a b -ENDTEST - -{ -a b -#a b -} - -STARTTEST -/^{/+1 -:set tw=5 fo=tcn comments=:# -A bjA b -ENDTEST - -{ - 1 a -# 1 a -} - -STARTTEST -/^{/+3 -:set tw=5 fo=t2a si -i A_ -ENDTEST - -{ - - x a - b - c - -} - -STARTTEST -/^{/+1 -:set tw=5 fo=qn comments=:# -gwap -ENDTEST - -{ -# 1 a b -} - -STARTTEST -/^{/+1 -:set tw=5 fo=q2 comments=:# -gwap -ENDTEST - -{ -# x -# a b -} - -STARTTEST -/^{/+2 -:set tw& fo=a -I^^ -ENDTEST - -{ - 1aa - 2bb -} - -STARTTEST -/mno pqr/ -:setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/ -A vwx yz -ENDTEST - -/* abc def ghi jkl - * mno pqr stu - */ - -STARTTEST -/^#/ -:setl tw=12 fo=tqnc comments=:# -A foobar -ENDTEST - -# 1 xxxxx - -STARTTEST -:g/^STARTTEST/.,/^ENDTEST/d -:1;/^Results/,$wq! test.out -ENDTEST diff --git a/src/nvim/testdir/test68.ok b/src/nvim/testdir/test68.ok deleted file mode 100644 index b3726a0a27..0000000000 --- a/src/nvim/testdir/test68.ok +++ /dev/null @@ -1,77 +0,0 @@ -Results of test68: - - -{ -a -b -} - - -{ -a -b - -a -b -} - - -{ -a - - -a - -} - - -{ -a b -#a b - -a b -#a b -} - - -{ - 1 a - b -# 1 a -# b -} - - -{ - - x a - b_ - c - -} - - -{ -# 1 a -# b -} - - -{ -# x a -# b -} - - -{ 1aa ^^2bb } - - -/* abc def ghi jkl - * mno pqr stu - * vwx yz - */ - - -# 1 xxxxx -# foobar - diff --git a/src/nvim/testdir/test83-tags2 b/src/nvim/testdir/test83-tags2 deleted file mode 100644 index 7f9f21b0eb..0000000000 --- a/src/nvim/testdir/test83-tags2 +++ /dev/null @@ -1,2 +0,0 @@ -!_TAG_FILE_ENCODING cp932 // -‚`‚a‚b Xtags2.txt /‚`‚a‚b diff --git a/src/nvim/testdir/test83-tags3 b/src/nvim/testdir/test83-tags3 deleted file mode 100644 index 0cb6591562..0000000000 --- a/src/nvim/testdir/test83-tags3 +++ /dev/null @@ -1,102 +0,0 @@ -!_TAG_FILE_SORTED 1 // -!_TAG_FILE_ENCODING cp932 // -abc1 Xtags3.txt /‚`‚a‚b -abc2 Xtags3.txt /‚`‚a‚b -abc3 Xtags3.txt /‚`‚a‚b -abc4 Xtags3.txt /‚`‚a‚b -abc5 Xtags3.txt /‚`‚a‚b -abc6 Xtags3.txt /‚`‚a‚b -abc7 Xtags3.txt /‚`‚a‚b -abc8 Xtags3.txt /‚`‚a‚b -abc9 Xtags3.txt /‚`‚a‚b -abc10 Xtags3.txt /‚`‚a‚b -abc11 Xtags3.txt /‚`‚a‚b -abc12 Xtags3.txt /‚`‚a‚b -abc13 Xtags3.txt /‚`‚a‚b -abc14 Xtags3.txt /‚`‚a‚b -abc15 Xtags3.txt /‚`‚a‚b -abc16 Xtags3.txt /‚`‚a‚b -abc17 Xtags3.txt /‚`‚a‚b -abc18 Xtags3.txt /‚`‚a‚b -abc19 Xtags3.txt /‚`‚a‚b -abc20 Xtags3.txt /‚`‚a‚b -abc21 Xtags3.txt /‚`‚a‚b -abc22 Xtags3.txt /‚`‚a‚b -abc23 Xtags3.txt /‚`‚a‚b -abc24 Xtags3.txt /‚`‚a‚b -abc25 Xtags3.txt /‚`‚a‚b -abc26 Xtags3.txt /‚`‚a‚b -abc27 Xtags3.txt /‚`‚a‚b -abc28 Xtags3.txt /‚`‚a‚b -abc29 Xtags3.txt /‚`‚a‚b -abc30 Xtags3.txt /‚`‚a‚b -abc31 Xtags3.txt /‚`‚a‚b -abc32 Xtags3.txt /‚`‚a‚b -abc33 Xtags3.txt /‚`‚a‚b -abc34 Xtags3.txt /‚`‚a‚b -abc35 Xtags3.txt /‚`‚a‚b -abc36 Xtags3.txt /‚`‚a‚b -abc37 Xtags3.txt /‚`‚a‚b -abc38 Xtags3.txt /‚`‚a‚b -abc39 Xtags3.txt /‚`‚a‚b -abc40 Xtags3.txt /‚`‚a‚b -abc41 Xtags3.txt /‚`‚a‚b -abc42 Xtags3.txt /‚`‚a‚b -abc43 Xtags3.txt /‚`‚a‚b -abc44 Xtags3.txt /‚`‚a‚b -abc45 Xtags3.txt /‚`‚a‚b -abc46 Xtags3.txt /‚`‚a‚b -abc47 Xtags3.txt /‚`‚a‚b -abc48 Xtags3.txt /‚`‚a‚b -abc49 Xtags3.txt /‚`‚a‚b -abc50 Xtags3.txt /‚`‚a‚b -abc51 Xtags3.txt /‚`‚a‚b -abc52 Xtags3.txt /‚`‚a‚b -abc53 Xtags3.txt /‚`‚a‚b -abc54 Xtags3.txt /‚`‚a‚b -abc55 Xtags3.txt /‚`‚a‚b -abc56 Xtags3.txt /‚`‚a‚b -abc57 Xtags3.txt /‚`‚a‚b -abc58 Xtags3.txt /‚`‚a‚b -abc59 Xtags3.txt /‚`‚a‚b -abc60 Xtags3.txt /‚`‚a‚b -abc61 Xtags3.txt /‚`‚a‚b -abc62 Xtags3.txt /‚`‚a‚b -abc63 Xtags3.txt /‚`‚a‚b -abc64 Xtags3.txt /‚`‚a‚b -abc65 Xtags3.txt /‚`‚a‚b -abc66 Xtags3.txt /‚`‚a‚b -abc67 Xtags3.txt /‚`‚a‚b -abc68 Xtags3.txt /‚`‚a‚b -abc69 Xtags3.txt /‚`‚a‚b -abc70 Xtags3.txt /‚`‚a‚b -abc71 Xtags3.txt /‚`‚a‚b -abc72 Xtags3.txt /‚`‚a‚b -abc73 Xtags3.txt /‚`‚a‚b -abc74 Xtags3.txt /‚`‚a‚b -abc75 Xtags3.txt /‚`‚a‚b -abc76 Xtags3.txt /‚`‚a‚b -abc77 Xtags3.txt /‚`‚a‚b -abc78 Xtags3.txt /‚`‚a‚b -abc79 Xtags3.txt /‚`‚a‚b -abc80 Xtags3.txt /‚`‚a‚b -abc81 Xtags3.txt /‚`‚a‚b -abc82 Xtags3.txt /‚`‚a‚b -abc83 Xtags3.txt /‚`‚a‚b -abc84 Xtags3.txt /‚`‚a‚b -abc85 Xtags3.txt /‚`‚a‚b -abc86 Xtags3.txt /‚`‚a‚b -abc87 Xtags3.txt /‚`‚a‚b -abc88 Xtags3.txt /‚`‚a‚b -abc89 Xtags3.txt /‚`‚a‚b -abc90 Xtags3.txt /‚`‚a‚b -abc91 Xtags3.txt /‚`‚a‚b -abc92 Xtags3.txt /‚`‚a‚b -abc93 Xtags3.txt /‚`‚a‚b -abc94 Xtags3.txt /‚`‚a‚b -abc95 Xtags3.txt /‚`‚a‚b -abc96 Xtags3.txt /‚`‚a‚b -abc97 Xtags3.txt /‚`‚a‚b -abc98 Xtags3.txt /‚`‚a‚b -abc99 Xtags3.txt /‚`‚a‚b -abc100 Xtags3.txt /‚`‚a‚b diff --git a/src/nvim/testdir/test83.in b/src/nvim/testdir/test83.in deleted file mode 100644 index d54b1bcddd..0000000000 --- a/src/nvim/testdir/test83.in +++ /dev/null @@ -1,75 +0,0 @@ -Tests for tag search with !_TAG_FILE_ENCODING. - -STARTTEST -:so mbyte.vim -:if !has('iconv') || iconv("\x82\x60", "cp932", "utf-8") != "\uff21" -: e! test.ok -: w! test.out -: qa! -:endif - -:/^text for tags1$/,/^text for tags1$/+1w! Xtags1.txt -:/^text for tags2$/,/^text for tags2$/+1w! Xtags2.txt -:/^text for tags3$/,/^text for tags3$/+1w! Xtags3.txt -:/^tags1$/+1,/^tags1-end$/-1w! Xtags1 - -ggdG - -:call setline('.', 'Results of test83') - -:" case1: -:new -:set tags=Xtags1 -:let v:errmsg = '' -:tag abcdefghijklmnopqrs -:if v:errmsg =~ 'E426:' || getline('.') != 'abcdefghijklmnopqrs' -: close -: put ='case1: failed' -:else -: close -: put ='case1: ok' -:endif - -:" case2: -:new -:set tags=test83-tags2 -:let v:errmsg = '' -:tag /.BC -:if v:errmsg =~ 'E426:' || getline('.') != 'ABC' -: close -: put ='case2: failed' -:else -: close -: put ='case2: ok' -:endif - -:" case3: -:new -:set tags=test83-tags3 -:let v:errmsg = '' -:tag abc50 -:if v:errmsg =~ 'E426:' || getline('.') != 'ABC' -: close -: put ='case3: failed' -:else -: close -: put ='case3: ok' -:endif -:close - -:wq! test.out -ENDTEST - -text for tags1 -abcdefghijklmnopqrs - -text for tags2 -ABC - -text for tags3 -ABC - -tags1 -!_TAG_FILE_ENCODING utf-8 // -abcdefghijklmnopqrs Xtags1.txt /abcdefghijklmnopqrs -tags1-end diff --git a/src/nvim/testdir/test83.ok b/src/nvim/testdir/test83.ok deleted file mode 100644 index 61a1a04a18..0000000000 --- a/src/nvim/testdir/test83.ok +++ /dev/null @@ -1,4 +0,0 @@ -Results of test83 -case1: ok -case2: ok -case3: ok diff --git a/src/nvim/testdir/test88.in b/src/nvim/testdir/test88.in deleted file mode 100644 index c2e6a752fa..0000000000 --- a/src/nvim/testdir/test88.in +++ /dev/null @@ -1,88 +0,0 @@ -vim: set ft=vim - -Tests for correct display (cursor column position) with +conceal and -tabulators. - -STARTTEST -:so small.vim -:if !has('conceal') - e! test.ok - wq! test.out -:endif -:" Conceal settings. -:set conceallevel=2 -:set concealcursor=nc -:syntax match test /|/ conceal -:" Save current cursor position. Only works in <expr> mode, can't be used -:" with :normal because it moves the cursor to the command line. Thanks to ZyX -:" <zyx.vim@gmail.com> for the idea to use an <expr> mapping. -:let positions = [] -:nnoremap <expr> GG ":let positions += ['".screenrow().":".screencol()."']\n" -:" Start test. -/^start: -:normal ztj -GGk -:" We should end up in the same column when running these commands on the two -:" lines. -:normal ft -GGk -:normal $ -GGk -:normal 0j -GGk -:normal ft -GGk -:normal $ -GGk -:normal 0j0j -GGk -:" Same for next test block. -:normal ft -GGk -:normal $ -GGk -:normal 0j -GGk -:normal ft -GGk -:normal $ -GGk -:normal 0j0j -GGk -:" And check W with multiple tabs and conceals in a line. -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:normal 0j -GGk -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:set lbr -:normal $ -GGk -:" Display result. -:call append('$', 'end:') -:call append('$', positions) -:/^end/,$wq! test.out -ENDTEST - -start: -.concealed. text -|concealed| text - - .concealed. text - |concealed| text - -.a. .b. .c. .d. -|a| |b| |c| |d| diff --git a/src/nvim/testdir/test88.ok b/src/nvim/testdir/test88.ok deleted file mode 100644 index e29698b7bd..0000000000 --- a/src/nvim/testdir/test88.ok +++ /dev/null @@ -1,24 +0,0 @@ -end: -2:1 -2:17 -2:20 -3:1 -3:17 -3:20 -5:8 -5:25 -5:28 -6:8 -6:25 -6:28 -8:1 -8:9 -8:17 -8:25 -8:27 -9:1 -9:9 -9:17 -9:25 -9:26 -9:26 diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim new file mode 100644 index 0000000000..1d1da94bac --- /dev/null +++ b/src/nvim/testdir/test_alot.vim @@ -0,0 +1,3 @@ +" A series of tests that can run in one Vim invocation. +" This makes testing go faster, since Vim doesn't need to restart. + diff --git a/src/nvim/testdir/test_breakindent.in b/src/nvim/testdir/test_breakindent.in index b05aef7f8d..5a8e580c4a 100644 --- a/src/nvim/testdir/test_breakindent.in +++ b/src/nvim/testdir/test_breakindent.in @@ -101,7 +101,7 @@ fygjyl:let line2 = @0 :$put =line2 :" :let g:test="Test 14: breakindent + visual blockwise delete #1" -:set all& breakindent +:set all& breakindent shada+=nX-test-breakindent.shada :30vnew :normal! 3a1234567890 :normal! a abcde diff --git a/src/nvim/testdir/test_charsearch.in b/src/nvim/testdir/test_charsearch.in deleted file mode 100644 index 5085cb39bc..0000000000 --- a/src/nvim/testdir/test_charsearch.in +++ /dev/null @@ -1,25 +0,0 @@ -Test for character searches - -STARTTEST -:so small.vim -:" check that "fe" and ";" work -/^X -ylfep;;p,,p: -:" check that save/restore works -/^Y -ylfep:let csave = getcharsearch() -fip:call setcharsearch(csave) -;p;p: -:" check that setcharsearch() changes the settins. -/^Z -ylfep:call setcharsearch({'char': 'k'}) -;p:call setcharsearch({'forward': 0}) -$;p:call setcharseearch({'until'}: 1}) -;;p: -:/^X/,$w! test.out -:qa! -ENDTEST - -Xabcdefghijkemnopqretuvwxyz -Yabcdefghijkemnopqretuvwxyz -Zabcdefghijkemnokqretkvwxyz diff --git a/src/nvim/testdir/test_charsearch.ok b/src/nvim/testdir/test_charsearch.ok deleted file mode 100644 index a0c90e24f9..0000000000 --- a/src/nvim/testdir/test_charsearch.ok +++ /dev/null @@ -1,3 +0,0 @@ -XabcdeXfghijkeXmnopqreXtuvwxyz -YabcdeYfghiYjkeYmnopqreYtuvwxyz -ZabcdeZfghijkZemnokZqretkZvwxyz diff --git a/src/nvim/testdir/test_command_count.in b/src/nvim/testdir/test_command_count.in deleted file mode 100644 index 170c810923..0000000000 --- a/src/nvim/testdir/test_command_count.in +++ /dev/null @@ -1,158 +0,0 @@ -Test for user command counts vim: set ft=vim : - -STARTTEST -:so tiny.vim -:lang C -:let g:lines = [] -:com -range=% RangeLines :call add(g:lines, 'RangeLines '.<line1>.' '.<line2>) -:com -range -addr=arguments RangeArguments :call add(g:lines, 'RangeArguments '.<line1>.' '.<line2>) -:com -range=% -addr=arguments RangeArgumentsAll :call add(g:lines, 'RangeArgumentsAll '.<line1>.' '.<line2>) -:com -range -addr=loaded_buffers RangeLoadedBuffers :call add(g:lines, 'RangeLoadedBuffers '.<line1>.' '.<line2>) -:com -range=% -addr=loaded_buffers RangeLoadedBuffersAll :call add(g:lines, 'RangeLoadedBuffersAll '.<line1>.' '.<line2>) -:com -range -addr=buffers RangeBuffers :call add(g:lines, 'RangeBuffers '.<line1>.' '.<line2>) -:com -range=% -addr=buffers RangeBuffersAll :call add(g:lines, 'RangeBuffersAll '.<line1>.' '.<line2>) -:com -range -addr=windows RangeWindows :call add(g:lines, 'RangeWindows '.<line1>.' '.<line2>) -:com -range=% -addr=windows RangeWindowsAll :call add(g:lines, 'RangeWindowsAll '.<line1>.' '.<line2>) -:com -range -addr=tabs RangeTabs :call add(g:lines, 'RangeTabs '.<line1>.' '.<line2>) -:com -range=% -addr=tabs RangeTabsAll :call add(g:lines, 'RangeTabsAll '.<line1>.' '.<line2>) -:set hidden -:arga a b c d -:argdo echo "loading buffers" -:argu 3 -:.-,$-RangeArguments -:%RangeArguments -:RangeArgumentsAll -:N -:.RangeArguments -:split|split|split|split -:3wincmd w -:.,$RangeWindows -:%RangeWindows -:RangeWindowsAll -:only -:blast|bd -:.,$RangeLoadedBuffers -:%RangeLoadedBuffers -:RangeLoadedBuffersAll -:.,$RangeBuffers -:%RangeBuffers -:RangeBuffersAll -:tabe|tabe|tabe|tabe -:normal 2gt -:.,$RangeTabs -:%RangeTabs -:RangeTabsAll -:1tabonly -:s/\n/\r\r\r\r\r/ -:2ma< -:$-ma> -:'<,'>RangeLines -:com -range=% -buffer LocalRangeLines :call add(g:lines, 'LocalRangeLines '.<line1>.' '.<line2>) -:'<,'>LocalRangeLines -:b1 -ENDTEST - -STARTTEST -:call add(g:lines, '') -:%argd -:arga a b c d -:let v:errmsg = '' -:5argu -:call add(g:lines, '5argu ' . v:errmsg) -:$argu -:call add(g:lines, '4argu ' . expand('%:t')) -:let v:errmsg = '' -:1argu -:call add(g:lines, '1argu ' . expand('%:t')) -:let v:errmsg = '' -:100b -:call add(g:lines, '100b ' . v:errmsg) -:split|split|split|split -:let v:errmsg = '' -:0close -:call add(g:lines, '0close ' . v:errmsg) -:$wincmd w -:$close -:call add(g:lines, '$close ' . winnr()) -:let v:errmsg = '' -:$+close -:call add(g:lines, '$+close ' . v:errmsg) -:$tabe -:call add(g:lines, '$tabe ' . tabpagenr()) -:let v:errmsg = '' -:$+tabe -:call add(g:lines, '$+tabe ' . v:errmsg) -:only! -:e x -:0tabm -:normal 1gt -:call add(g:lines, '0tabm ' . expand('%:t')) -:tabonly! -:only! -:e! test.out -:call append(0, g:lines) -:unlet g:lines -:w|bd -:b1 -ENDTEST - -STARTTEST -:let g:lines = [] -:func BufStatus() -: call add(g:lines, 'aaa: ' . buflisted(g:buf_aaa) . ' bbb: ' . buflisted(g:buf_bbb) . ' ccc: ' . buflisted(g:buf_ccc)) -:endfunc -:se nohidden -:e aaa -:let buf_aaa = bufnr('%') -:e bbb -:let buf_bbb = bufnr('%') -:e ccc -:let buf_ccc = bufnr('%') -:b1 -:call BufStatus() -:exe buf_bbb . "," . buf_ccc . "bdelete" -:call BufStatus() -:exe buf_aaa . "bdelete" -:call BufStatus() -:e! test.out -:call append('$', g:lines) -:unlet g:lines -:delfunc BufStatus -:w|bd -:b1 -ENDTEST - -STARTTEST -:se hidden -:only! -:let g:lines = [] -:%argd -:arga a b c d e f -:3argu -:let args = '' -:.,$-argdo let args .= ' '.expand('%') -:call add(g:lines, 'argdo:' . args) -:split|split|split|split -:2wincmd w -:let windows = '' -:.,$-windo let windows .= ' '.winnr() -:call add(g:lines, 'windo:'. windows) -:b2 -:let buffers = '' -:.,$-bufdo let buffers .= ' '.bufnr('%') -:call add(g:lines, 'bufdo:' . buffers) -:3bd -:let buffers = '' -:3,7bufdo let buffers .= ' '.bufnr('%') -:call add(g:lines, 'bufdo:' . buffers) -:tabe|tabe|tabe|tabe -:normal! 2gt -:let tabpages = '' -:.,$-tabdo let tabpages .= ' '.tabpagenr() -:call add(g:lines, 'tabdo:' . tabpages) -:e! test.out -:call append('$', g:lines) -:w|qa! -ENDTEST - - diff --git a/src/nvim/testdir/test_command_count.ok b/src/nvim/testdir/test_command_count.ok deleted file mode 100644 index e74155ec1b..0000000000 --- a/src/nvim/testdir/test_command_count.ok +++ /dev/null @@ -1,38 +0,0 @@ -RangeArguments 2 4 -RangeArguments 1 5 -RangeArgumentsAll 1 5 -RangeArguments 2 2 -RangeWindows 3 5 -RangeWindows 1 5 -RangeWindowsAll 1 5 -RangeLoadedBuffers 2 4 -RangeLoadedBuffers 1 4 -RangeLoadedBuffersAll 1 4 -RangeBuffers 2 5 -RangeBuffers 1 5 -RangeBuffersAll 1 5 -RangeTabs 2 5 -RangeTabs 1 5 -RangeTabsAll 1 5 -RangeLines 2 5 -LocalRangeLines 2 5 - -5argu E16: Invalid range -4argu d -1argu a -100b E16: Invalid range -0close -$close 3 -$+close E16: Invalid range -$tabe 2 -$+tabe E16: Invalid range -0tabm x - -aaa: 1 bbb: 1 ccc: 1 -aaa: 1 bbb: 0 ccc: 0 -aaa: 0 bbb: 0 ccc: 0 -argdo: c d e -windo: 2 3 4 -bufdo: 2 3 4 5 6 7 8 9 10 15 -bufdo: 4 5 6 7 -tabdo: 2 3 4 diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim new file mode 100644 index 0000000000..d819b7b092 --- /dev/null +++ b/src/nvim/testdir/test_cursor_func.vim @@ -0,0 +1,52 @@ +" Tests for cursor(). + +func Test_wrong_arguments() + try + call cursor(1. 3) + " not reached + call assert_false(1) + catch + call assert_exception('E474:') + endtry +endfunc + +func Test_move_cursor() + new + call setline(1, ['aaa', 'bbb', 'ccc', 'ddd']) + + call cursor([1, 1, 0, 1]) + call assert_equal([1, 1, 0, 1], getcurpos()[1:]) + call cursor([4, 3, 0, 3]) + call assert_equal([4, 3, 0, 3], getcurpos()[1:]) + + call cursor(2, 2) + call assert_equal([2, 2, 0, 2], getcurpos()[1:]) + " line number zero keeps the line number + call cursor(0, 1) + call assert_equal([2, 1, 0, 1], getcurpos()[1:]) + " col number zero keeps the column + call cursor(3, 0) + call assert_equal([3, 1, 0, 1], getcurpos()[1:]) + " below last line goes to last line + call cursor(9, 1) + call assert_equal([4, 1, 0, 1], getcurpos()[1:]) + + quit! +endfunc + +" Very short version of what matchparen does. +function s:Highlight_Matching_Pair() + let save_cursor = getcurpos() + call setpos('.', save_cursor) +endfunc + +func Test_curswant_with_autocommand() + new + call setline(1, ['func()', '{', '}', '----']) + autocmd! CursorMovedI * call s:Highlight_Matching_Pair() + exe "normal! 3Ga\<Down>X\<Esc>" + call assert_equal('-X---', getline(4)) + autocmd! CursorMovedI * + quit! +endfunc + diff --git a/src/nvim/testdir/test_eval.in b/src/nvim/testdir/test_eval.in deleted file mode 100644 index b2b982a434..0000000000 --- a/src/nvim/testdir/test_eval.in +++ /dev/null @@ -1,223 +0,0 @@ -Test for various eval features. vim: set ft=vim : - -Note: system clipboard is saved, changed and restored. - -STARTTEST -:so small.vim -:set noswapfile -:lang C -:fun AppendRegContents(reg) - call append('$', printf('%s: type %s; value: %s (%s), expr: %s (%s)', a:reg, getregtype(a:reg), getreg(a:reg), string(getreg(a:reg, 0, 1)), getreg(a:reg, 1), string(getreg(a:reg, 1, 1)))) -endfun -:command -nargs=? AR :call AppendRegContents(<q-args>) -:fun SetReg(...) - call call('setreg', a:000) - call append('$', printf('{{{2 setreg(%s)', string(a:000)[1:-2])) - call AppendRegContents(a:1) - if a:1 isnot# '=' - execute "silent normal! Go==\n==\e\"".a:1."P" - endif -endfun -:fun ErrExe(str) - call append('$', 'Executing '.a:str) - try - execute a:str - catch - $put =v:exception - endtry -endfun -:fun Test() -$put ='{{{1 let tests' -let @" = 'abc' -AR " -let @" = "abc\n" -AR " -let @" = "abc\<C-m>" -AR " -let @= = '"abc"' -AR = - -$put ='{{{1 Basic setreg tests' -call SetReg('a', 'abcA', 'c') -call SetReg('b', 'abcB', 'v') -call SetReg('c', 'abcC', 'l') -call SetReg('d', 'abcD', 'V') -call SetReg('e', 'abcE', 'b') -call SetReg('f', 'abcF', "\<C-v>") -call SetReg('g', 'abcG', 'b10') -call SetReg('h', 'abcH', "\<C-v>10") -call SetReg('I', 'abcI') - -$put ='{{{1 Appending single lines with setreg()' -call SetReg('A', 'abcAc', 'c') -call SetReg('A', 'abcAl', 'l') -call SetReg('A', 'abcAc2','c') -call SetReg('b', 'abcBc', 'ca') -call SetReg('b', 'abcBb', 'ba') -call SetReg('b', 'abcBc2','ca') -call SetReg('b', 'abcBb2','b50a') - -call SetReg('C', 'abcCl', 'l') -call SetReg('C', 'abcCc', 'c') -call SetReg('D', 'abcDb', 'b') - -call SetReg('E', 'abcEb', 'b') -call SetReg('E', 'abcEl', 'l') -call SetReg('F', 'abcFc', 'c') - -$put ='{{{1 Appending NL with setreg()' -call setreg('a', 'abcA2', 'c') -call setreg('b', 'abcB2', 'v') -call setreg('c', 'abcC2', 'l') -call setreg('d', 'abcD2', 'V') -call setreg('e', 'abcE2', 'b') -call setreg('f', 'abcF2', "\<C-v>") -call setreg('g', 'abcG2', 'b10') -call setreg('h', 'abcH2', "\<C-v>10") -call setreg('I', 'abcI2') - -call SetReg('A', "\n") -call SetReg('B', "\n", 'c') -call SetReg('C', "\n") -call SetReg('D', "\n", 'l') -call SetReg('E', "\n") -call SetReg('F', "\n", 'b') - -$put ='{{{1 Setting lists with setreg()' -call SetReg('a', ['abcA3'], 'c') -call SetReg('b', ['abcB3'], 'l') -call SetReg('c', ['abcC3'], 'b') -call SetReg('d', ['abcD3']) -call SetReg('e', [1, 2, 'abc', 3]) -call SetReg('f', [1, 2, 3]) - -$put ='{{{1 Appending lists with setreg()' -call SetReg('A', ['abcA3c'], 'c') -call SetReg('b', ['abcB3l'], 'la') -call SetReg('C', ['abcC3b'], 'lb') -call SetReg('D', ['abcD32']) - -call SetReg('A', ['abcA32']) -call SetReg('B', ['abcB3c'], 'c') -call SetReg('C', ['abcC3l'], 'l') -call SetReg('D', ['abcD3b'], 'b') - -$put ='{{{1 Appending lists with NL with setreg()' -call SetReg('A', ["\n", 'abcA3l2'], 'l') -call SetReg('B', ["\n", 'abcB3c2'], 'c') -call SetReg('C', ["\n", 'abcC3b2'], 'b') -call SetReg('D', ["\n", 'abcD3b50'],'b50') - -$put ='{{{1 Setting lists with NLs with setreg()' -call SetReg('a', ['abcA4-0', "\n", "abcA4-2\n", "\nabcA4-3", "abcA4-4\nabcA4-4-2"]) -call SetReg('b', ['abcB4c-0', "\n", "abcB4c-2\n", "\nabcB4c-3", "abcB4c-4\nabcB4c-4-2"], 'c') -call SetReg('c', ['abcC4l-0', "\n", "abcC4l-2\n", "\nabcC4l-3", "abcC4l-4\nabcC4l-4-2"], 'l') -call SetReg('d', ['abcD4b-0', "\n", "abcD4b-2\n", "\nabcD4b-3", "abcD4b-4\nabcD4b-4-2"], 'b') -call SetReg('e', ['abcE4b10-0', "\n", "abcE4b10-2\n", "\nabcE4b10-3", "abcE4b10-4\nabcE4b10-4-2"], 'b10') - -$put ='{{{1 Search and expressions' -call SetReg('/', ['abc/']) -call SetReg('/', ["abc/\n"]) -call SetReg('=', ['"abc/"']) -call SetReg('=', ["\"abc/\n\""]) -$put ='{{{1 System clipboard' -" Save and restore system clipboard. -" If no connection to X-Server is possible, test should succeed. -:let _clipreg = ['+', getreg('+'), getregtype('+')] -:let _clipopt = &cb -:let &cb='unnamedplus' -:1y -:AR + -:tabdo :windo :echo "hi" -:3y -:AR + -:let &cb=_clipopt -:call call('setreg', _clipreg) -$put ='{{{1 Errors' -call ErrExe('call setreg()') -call ErrExe('call setreg(1)') -call ErrExe('call setreg(1, 2, 3, 4)') -call ErrExe('call setreg([], 2)') -call ErrExe('call setreg(1, {})') -call ErrExe('call setreg(1, 2, [])') -call ErrExe('call setreg("/", ["1", "2"])') -call ErrExe('call setreg("=", ["1", "2"])') -call ErrExe('call setreg(1, ["", "", [], ""])') -endfun -:" -:call Test() -:" -:delfunction SetReg -:delfunction AppendRegContents -:delfunction ErrExe -:delfunction Test -:delcommand AR -:call garbagecollect(1) -:" -:/^start:/+1,$wq! test.out -:" vim: et ts=4 isk-=\: fmr=???,??? -:call getchar() -:e test.out -:%d - -:" function name not starting with a capital -:try -: func! g:test() -: echo "test" -: endfunc -:catch -: $put =v:exception -:endtry - -:" function name folowed by # -:try -: func! test2() "# -: echo "test2" -: endfunc -:catch -: $put =v:exception -:endtry - -:" function name includes a colon -:try -: func! b:test() -: echo "test" -: endfunc -:catch -: $put =v:exception -:endtry - -:" function name starting with/without "g:", buffer-local funcref. -:function! g:Foo(n) -: $put ='called Foo(' . a:n . ')' -:endfunction -:let b:my_func = function('Foo') -:call b:my_func(1) -:echo g:Foo(2) -:echo Foo(3) - -:" script-local function used in Funcref must exist. -:so test_eval_func.vim - -:" using $ instead of '$' must give an error -:try -: call append($, 'foobar') -:catch -: $put =v:exception -:endtry - -:$put ='{{{1 getcurpos/setpos' -/^012345678 -6l:let sp = getcurpos() -0:call setpos('.', sp) -jyl:$put - -:/^start:/+1,$wq! test.out -:" vim: et ts=4 isk-=\: fmr=???,??? -:call getchar() -ENDTEST - -012345678 -012345678 - -start: diff --git a/src/nvim/testdir/test_eval.ok b/src/nvim/testdir/test_eval.ok Binary files differdeleted file mode 100644 index 2cdb8f5da8..0000000000 --- a/src/nvim/testdir/test_eval.ok +++ /dev/null diff --git a/src/nvim/testdir/test_eval_func.vim b/src/nvim/testdir/test_eval_func.vim deleted file mode 100644 index 48d01df27d..0000000000 --- a/src/nvim/testdir/test_eval_func.vim +++ /dev/null @@ -1,12 +0,0 @@ -" Vim script used in test_eval.in. Needed for script-local function. - -func! s:Testje() - return "foo" -endfunc - -let Bar = function('s:Testje') - -$put ='s:Testje exists: ' . exists('s:Testje') -$put ='func s:Testje exists: ' . exists('*s:Testje') -$put ='Bar exists: ' . exists('Bar') -$put ='func Bar exists: ' . exists('*Bar') diff --git a/src/nvim/testdir/test_listlbr.in b/src/nvim/testdir/test_listlbr.in index 36235ea915..6084711786 100644 --- a/src/nvim/testdir/test_listlbr.in +++ b/src/nvim/testdir/test_listlbr.in @@ -24,20 +24,24 @@ STARTTEST : $put =g:line : wincmd p :endfu +:" :let g:test="Test 1: set linebreak" :redraw! :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() +:" :let g:test="Test 2: set linebreak + set list" :set linebreak list listchars= :redraw! :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() +:" :let g:test ="Test 3: set linebreak nolist" :set nolist linebreak :redraw! :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() +:" :let g:test ="Test 4: set linebreak with tab and 1 line as long as screen: should break!" :set nolist linebreak ts=8 :let line="1\t".repeat('a', winwidth(0)-2) @@ -51,6 +55,7 @@ STARTTEST :$put =line :$ :norm! zt +:" :let g:test ="Test 5: set linebreak with conceal and set list and tab displayed by different char (line may not be truncated)" :set cpo&vim list linebreak conceallevel=2 concealcursor=nv listchars=tab:ab :syn match ConcealVar contained /_/ conceal @@ -58,13 +63,57 @@ STARTTEST :let line=ScreenChar(winwidth(0)) :call DoRecordScreen() :set cpo&vim linebreak +:" :let g:test ="Test 6: set linebreak with visual block mode" :let line="REMOVE: this not" +:$put =g:test :$put =line :let line="REMOVE: aaaaaaaaaaaaa" :$put =line :1/^REMOVE: 0jf x:$put +:set cpo&vim linebreak +:" +:let g:test ="Test 7: set linebreak with visual block mode and v_b_A" +:$put =g:test +Golong line: 40afoobar aTARGET at end +:exe "norm! $3B\<C-v>eAx\<Esc>" +:set cpo&vim linebreak sbr= +:" +:let g:test ="Test 8: set linebreak with visual char mode and changing block" +:$put =g:test +Go1111-1111-1111-11-1111-1111-11110f-lv3lc2222bgj. +:" +:let g:test ="Test 9: using redo after block visual mode" +:$put =g:test +Go +aaa +aaa +a2k2j~e. +:" +:let g:test ="Test 10: using normal commands after block-visual" +:$put =g:test +:set linebreak +Go +abcd{ef +ghijklm +no}pqrs2k0f{c% +:" +:let g:test ="Test 11: using block replace mode after wrapping" +:$put =g:test +:set linebreak wrap +Go150aayypk147|jr0 +:" +:let g:test ="Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$" +:set list listchars=space:_,trail:-,tab:>-,eol:$ +:$put =g:test +:let line="a aaaaaaaaaaaaaaaaaaaaaa\ta " +:$put =line +:$ +:norm! zt +:redraw! +:let line=ScreenChar(winwidth(0)) +:call DoRecordScreen() :%w! test.out :qa! ENDTEST diff --git a/src/nvim/testdir/test_listlbr.ok b/src/nvim/testdir/test_listlbr.ok index ee74667661..b32a54969e 100644 --- a/src/nvim/testdir/test_listlbr.ok +++ b/src/nvim/testdir/test_listlbr.ok @@ -32,7 +32,31 @@ Sabbbbbb bla ~ ~ ~ +Test 6: set linebreak with visual block mode this not aaaaaaaaaaaaa REMOVE: REMOVE: +Test 7: set linebreak with visual block mode and v_b_A +long line: foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar TARGETx at end +Test 8: set linebreak with visual char mode and changing block +1111-2222-1111-11-1111-2222-1111 +Test 9: using redo after block visual mode + +AaA +AaA +A +Test 10: using normal commands after block-visual + +abcdpqrs +Test 11: using block replace mode after wrapping +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa +Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$ +a aaaaaaaaaaaaaaaaaaaaaa a + +Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$ +a_ +aaaaaaaaaaaaaaaaaaaa +aa>-----a-$ +~ diff --git a/src/nvim/testdir/test_viml.vim b/src/nvim/testdir/test_viml.vim new file mode 100644 index 0000000000..9f0618bd45 --- /dev/null +++ b/src/nvim/testdir/test_viml.vim @@ -0,0 +1,930 @@ +" Test various aspects of the Vim language. +" Most of this was formerly in test49. + +"------------------------------------------------------------------------------- +" Test environment {{{1 +"------------------------------------------------------------------------------- + +com! XpathINIT let g:Xpath = '' +com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args> + +" Append a message to the "messages" file +func! Xout(text) + split messages + $put =a:text + wq +endfunc + +com! -nargs=1 Xout call Xout(<args>) + +" MakeScript() - Make a script file from a function. {{{2 +" +" Create a script that consists of the body of the function a:funcname. +" Replace any ":return" by a ":finish", any argument variable by a global +" variable, and and every ":call" by a ":source" for the next following argument +" in the variable argument list. This function is useful if similar tests are +" to be made for a ":return" from a function call or a ":finish" in a script +" file. +function! MakeScript(funcname, ...) + let script = tempname() + execute "redir! >" . script + execute "function" a:funcname + redir END + execute "edit" script + " Delete the "function" and the "endfunction" lines. Do not include the + " word "function" in the pattern since it might be translated if LANG is + " set. When MakeScript() is being debugged, this deletes also the debugging + " output of its line 3 and 4. + exec '1,/.*' . a:funcname . '(.*)/d' + /^\d*\s*endfunction\>/,$d + %s/^\d*//e + %s/return/finish/e + %s/\<a:\(\h\w*\)/g:\1/ge + normal gg0 + let cnt = 0 + while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0 + let cnt = cnt + 1 + s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/ + endwhile + g/^\s*$/d + write + bwipeout + return script +endfunction + +" ExecAsScript - Source a temporary script made from a function. {{{2 +" +" Make a temporary script file from the function a:funcname, ":source" it, and +" delete it afterwards. +function! ExecAsScript(funcname) + " Make a script from the function passed as argument. + let script = MakeScript(a:funcname) + + " Source and delete the script. + exec "source" script + call delete(script) +endfunction + +com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) + + +"------------------------------------------------------------------------------- +" Test 1: :endwhile in function {{{1 +" +" Detect if a broken loop is (incorrectly) reactivated by the +" :endwhile. Use a :return to prevent an endless loop, and make +" this test first to get a meaningful result on an error before other +" tests will hang. +"------------------------------------------------------------------------------- + +function! T1_F() + Xpath 'a' + let first = 1 + while 1 + Xpath 'b' + if first + Xpath 'c' + let first = 0 + break + else + Xpath 'd' + return + endif + endwhile +endfunction + +function! T1_G() + Xpath 'h' + let first = 1 + while 1 + Xpath 'i' + if first + Xpath 'j' + let first = 0 + break + else + Xpath 'k' + return + endif + if 1 " unmatched :if + endwhile +endfunction + +func Test_endwhile_function() + XpathINIT + call T1_F() + Xpath 'F' + + try + call T1_G() + catch + " Catch missing :endif + call assert_true(v:exception =~ 'E171') + Xpath 'x' + endtry + Xpath 'G' + + call assert_equal('abcFhijxG', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 2: :endwhile in script {{{1 +" +" Detect if a broken loop is (incorrectly) reactivated by the +" :endwhile. Use a :finish to prevent an endless loop, and place +" this test before others that might hang to get a meaningful result +" on an error. +" +" This test executes the bodies of the functions T1_F and T1_G from +" the previous test as script files (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func Test_endwhile_script() + XpathINIT + ExecAsScript T1_F + Xpath 'F' + + try + ExecAsScript T1_G + catch + " Catch missing :endif + call assert_true(v:exception =~ 'E171') + Xpath 'x' + endtry + Xpath 'G' + + call assert_equal('abcFhijxG', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 3: :if, :elseif, :while, :continue, :break {{{1 +"------------------------------------------------------------------------------- + +function Test_if_while() + XpathINIT + if 1 + Xpath 'a' + let loops = 3 + while loops > -1 " main loop: loops == 3, 2, 1 (which breaks) + if loops <= 0 + let break_err = 1 + let loops = -1 + else + Xpath 'b' . loops + endif + if (loops == 2) + while loops == 2 " dummy loop + Xpath 'c' . loops + let loops = loops - 1 + continue " stop dummy loop + Xpath 'd' . loops + endwhile + continue " continue main loop + Xpath 'e' . loops + elseif (loops == 1) + let p = 1 + while p " dummy loop + Xpath 'f' . loops + let p = 0 + break " break dummy loop + Xpath 'g' . loops + endwhile + Xpath 'h' . loops + unlet p + break " break main loop + Xpath 'i' . loops + endif + if (loops > 0) + Xpath 'j' . loops + endif + while loops == 3 " dummy loop + let loops = loops - 1 + endwhile " end dummy loop + endwhile " end main loop + Xpath 'k' + else + Xpath 'l' + endif + Xpath 'm' + if exists("break_err") + Xpath 'm' + unlet break_err + endif + + unlet loops + + call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 4: :return {{{1 +"------------------------------------------------------------------------------- + +function! T4_F() + if 1 + Xpath 'a' + let loops = 3 + while loops > 0 " 3: 2: 1: + Xpath 'b' . loops + if (loops == 2) + Xpath 'c' . loops + return + Xpath 'd' . loops + endif + Xpath 'e' . loops + let loops = loops - 1 + endwhile + Xpath 'f' + else + Xpath 'g' + endif +endfunction + +function Test_return() + XpathINIT + call T4_F() + Xpath '4' + + call assert_equal('ab3e3b2c24', g:Xpath) +endfunction + + +"------------------------------------------------------------------------------- +" Test 5: :finish {{{1 +" +" This test executes the body of the function T4_F from the previous +" test as a script file (:return replaced by :finish). +"------------------------------------------------------------------------------- + +function Test_finish() + XpathINIT + ExecAsScript T4_F + Xpath '5' + + call assert_equal('ab3e3b2c25', g:Xpath) +endfunction + + + +"------------------------------------------------------------------------------- +" Test 6: Defining functions in :while loops {{{1 +" +" Functions can be defined inside other functions. An inner function +" gets defined when the outer function is executed. Functions may +" also be defined inside while loops. Expressions in braces for +" defining the function name are allowed. +" +" The functions are defined when sourcing the script, only the +" resulting path is checked in the test function. +"------------------------------------------------------------------------------- + +XpathINIT + +" The command CALL collects the argument of all its invocations in "calls" +" when used from a function (that is, when the global variable "calls" needs +" the "g:" prefix). This is to check that the function code is skipped when +" the function is defined. For inner functions, do so only if the outer +" function is not being executed. +" +let calls = "" +com! -nargs=1 CALL + \ if !exists("calls") && !exists("outer") | + \ let g:calls = g:calls . <args> | + \ endif + +let i = 0 +while i < 3 + let i = i + 1 + if i == 1 + Xpath 'a' + function! F1(arg) + CALL a:arg + let outer = 1 + + let j = 0 + while j < 1 + Xpath 'b' + let j = j + 1 + function! G1(arg) + CALL a:arg + endfunction + Xpath 'c' + endwhile + endfunction + Xpath 'd' + + continue + endif + + Xpath 'e' . i + function! F{i}(i, arg) + CALL a:arg + let outer = 1 + + if a:i == 3 + Xpath 'f' + endif + let k = 0 + while k < 3 + Xpath 'g' . k + let k = k + 1 + function! G{a:i}{k}(arg) + CALL a:arg + endfunction + Xpath 'h' . k + endwhile + endfunction + Xpath 'i' + +endwhile + +if exists("*G1") + Xpath 'j' +endif +if exists("*F1") + call F1("F1") + if exists("*G1") + call G1("G1") + endif +endif + +if exists("G21") || exists("G22") || exists("G23") + Xpath 'k' +endif +if exists("*F2") + call F2(2, "F2") + if exists("*G21") + call G21("G21") + endif + if exists("*G22") + call G22("G22") + endif + if exists("*G23") + call G23("G23") + endif +endif + +if exists("G31") || exists("G32") || exists("G33") + Xpath 'l' +endif +if exists("*F3") + call F3(3, "F3") + if exists("*G31") + call G31("G31") + endif + if exists("*G32") + call G32("G32") + endif + if exists("*G33") + call G33("G33") + endif +endif + +Xpath 'm' + +let g:test6_result = g:Xpath +let g:test6_calls = calls + +unlet calls +delfunction F1 +delfunction G1 +delfunction F2 +delfunction G21 +delfunction G22 +delfunction G23 +delfunction G31 +delfunction G32 +delfunction G33 + +function Test_defining_functions() + call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result) + call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls) +endfunc + +"------------------------------------------------------------------------------- +" Test 7: Continuing on errors outside functions {{{1 +" +" On an error outside a function, the script processing continues +" at the line following the outermost :endif or :endwhile. When not +" inside an :if or :while, the script processing continues at the next +" line. +"------------------------------------------------------------------------------- + +XpathINIT + +if 1 + Xpath 'a' + while 1 + Xpath 'b' + asdf + Xpath 'c' + break + endwhile | Xpath 'd' + Xpath 'e' +endif | Xpath 'f' +Xpath 'g' + +while 1 + Xpath 'h' + if 1 + Xpath 'i' + asdf + Xpath 'j' + endif | Xpath 'k' + Xpath 'l' + break +endwhile | Xpath 'm' +Xpath 'n' + +asdf +Xpath 'o' + +asdf | Xpath 'p' +Xpath 'q' + +let g:test7_result = g:Xpath + +func Test_error_in_script() + call assert_equal('abghinoq', g:test7_result) +endfunc + +"------------------------------------------------------------------------------- +" Test 8: Aborting and continuing on errors inside functions {{{1 +" +" On an error inside a function without the "abort" attribute, the +" script processing continues at the next line (unless the error was +" in a :return command). On an error inside a function with the +" "abort" attribute, the function is aborted and the script processing +" continues after the function call; the value -1 is returned then. +"------------------------------------------------------------------------------- + +XpathINIT + +function! T8_F() + if 1 + Xpath 'a' + while 1 + Xpath 'b' + asdf + Xpath 'c' + asdf | Xpath 'd' + Xpath 'e' + break + endwhile + Xpath 'f' + endif | Xpath 'g' + Xpath 'h' + + while 1 + Xpath 'i' + if 1 + Xpath 'j' + asdf + Xpath 'k' + asdf | Xpath 'l' + Xpath 'm' + endif + Xpath 'n' + break + endwhile | Xpath 'o' + Xpath 'p' + + return novar " returns (default return value 0) + Xpath 'q' + return 1 " not reached +endfunction + +function! T8_G() abort + if 1 + Xpath 'r' + while 1 + Xpath 's' + asdf " returns -1 + Xpath 't' + break + endwhile + Xpath 'v' + endif | Xpath 'w' + Xpath 'x' + + return -4 " not reached +endfunction + +function! T8_H() abort + while 1 + Xpath 'A' + if 1 + Xpath 'B' + asdf " returns -1 + Xpath 'C' + endif + Xpath 'D' + break + endwhile | Xpath 'E' + Xpath 'F' + + return -4 " not reached +endfunction + +" Aborted functions (T8_G and T8_H) return -1. +let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H() +Xpath 'X' +let g:test8_result = g:Xpath + +func Test_error_in_function() + call assert_equal(13, g:test8_sum) + call assert_equal('abcefghijkmnoprsABX', g:test8_result) + + delfunction T8_F + delfunction T8_G + delfunction T8_H +endfunc + + +"------------------------------------------------------------------------------- +" Test 9: Continuing after aborted functions {{{1 +" +" When a function with the "abort" attribute is aborted due to an +" error, the next function back in the call hierarchy without an +" "abort" attribute continues; the value -1 is returned then. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() abort + Xpath 'a' + let result = G() " not aborted + Xpath 'b' + if result != 2 + Xpath 'c' + endif + return 1 +endfunction + +function! G() " no abort attribute + Xpath 'd' + if H() != -1 " aborted + Xpath 'e' + endif + Xpath 'f' + return 2 +endfunction + +function! H() abort + Xpath 'g' + call I() " aborted + Xpath 'h' + return 4 +endfunction + +function! I() abort + Xpath 'i' + asdf " error + Xpath 'j' + return 8 +endfunction + +if F() != 1 + Xpath 'k' +endif + +let g:test9_result = g:Xpath + +delfunction F +delfunction G +delfunction H +delfunction I + +func Test_func_abort() + call assert_equal('adgifb', g:test9_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 10: :if, :elseif, :while argument parsing {{{1 +" +" A '"' or '|' in an argument expression must not be mixed up with +" a comment or a next command after a bar. Parsing errors should +" be recognized. +"------------------------------------------------------------------------------- + +XpathINIT + +function! MSG(enr, emsg) + let english = v:lang == "C" || v:lang =~ '^[Ee]n' + if a:enr == "" + Xout "TODO: Add message number for:" a:emsg + let v:errmsg = ":" . v:errmsg + endif + let match = 1 + if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) + let match = 0 + if v:errmsg == "" + Xout "Message missing." + else + let v:errmsg = escape(v:errmsg, '"') + Xout "Unexpected message:" v:errmsg + endif + endif + return match +endfunction + +if 1 || strlen("\"") | Xpath 'a' + Xpath 'b' +endif +Xpath 'c' + +if 0 +elseif 1 || strlen("\"") | Xpath 'd' + Xpath 'e' +endif +Xpath 'f' + +while 1 || strlen("\"") | Xpath 'g' + Xpath 'h' + break +endwhile +Xpath 'i' + +let v:errmsg = "" +if 1 ||| strlen("\"") | Xpath 'j' + Xpath 'k' +endif +Xpath 'l' +if !MSG('E15', "Invalid expression") + Xpath 'm' +endif + +let v:errmsg = "" +if 0 +elseif 1 ||| strlen("\"") | Xpath 'n' + Xpath 'o' +endif +Xpath 'p' +if !MSG('E15', "Invalid expression") + Xpath 'q' +endif + +let v:errmsg = "" +while 1 ||| strlen("\"") | Xpath 'r' + Xpath 's' + break +endwhile +Xpath 't' +if !MSG('E15', "Invalid expression") + Xpath 'u' +endif + +let g:test10_result = g:Xpath +delfunction MSG + +func Test_expr_parsing() + call assert_equal('abcdefghilpt', g:test10_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 11: :if, :elseif, :while argument evaluation after abort {{{1 +" +" When code is skipped over due to an error, the boolean argument to +" an :if, :elseif, or :while must not be evaluated. +"------------------------------------------------------------------------------- + +XpathINIT + +let calls = 0 + +function! P(num) + let g:calls = g:calls + a:num " side effect on call + return 0 +endfunction + +if 1 + Xpath 'a' + asdf " error + Xpath 'b' + if P(1) " should not be called + Xpath 'c' + elseif !P(2) " should not be called + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' + while P(4) " should not be called + Xpath 'g' + endwhile + Xpath 'h' +endif +Xpath 'x' + +let g:test11_calls = calls +let g:test11_result = g:Xpath + +unlet calls +delfunction P + +func Test_arg_abort() + call assert_equal(0, g:test11_calls) + call assert_equal('ax', g:test11_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 12: Expressions in braces in skipped code {{{1 +" +" In code skipped over due to an error or inactive conditional, +" an expression in braces as part of a variable or function name +" should not be evaluated. +"------------------------------------------------------------------------------- + +XpathINIT + +function! NULL() + Xpath 'a' + return 0 +endfunction + +function! ZERO() + Xpath 'b' + return 0 +endfunction + +function! F0() + Xpath 'c' +endfunction + +function! F1(arg) + Xpath 'e' +endfunction + +let V0 = 1 + +Xpath 'f' +echo 0 ? F{NULL() + V{ZERO()}}() : 1 + +Xpath 'g' +if 0 + Xpath 'h' + call F{NULL() + V{ZERO()}}() +endif + +Xpath 'i' +if 1 + asdf " error + Xpath 'j' + call F1(F{NULL() + V{ZERO()}}()) +endif + +Xpath 'k' +if 1 + asdf " error + Xpath 'l' + call F{NULL() + V{ZERO()}}() +endif + +let g:test12_result = g:Xpath + +func Test_braces_skipped() + call assert_equal('fgik', g:test12_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 13: Failure in argument evaluation for :while {{{1 +" +" A failure in the expression evaluation for the condition of a :while +" causes the whole :while loop until the matching :endwhile being +" ignored. Continuation is at the next following line. +"------------------------------------------------------------------------------- + +XpathINIT + +Xpath 'a' +while asdf + Xpath 'b' + while 1 + Xpath 'c' + break + endwhile + Xpath 'd' + break +endwhile +Xpath 'e' + +while asdf | Xpath 'f' | endwhile | Xpath 'g' +Xpath 'h' +let g:test13_result = g:Xpath + +func Test_while_fail() + call assert_equal('aeh', g:test13_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 14: Failure in argument evaluation for :if {{{1 +" +" A failure in the expression evaluation for the condition of an :if +" does not cause the corresponding :else or :endif being matched to +" a previous :if/:elseif. Neither of both branches of the failed :if +" are executed. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() + Xpath 'a' + let x = 0 + if x " false + Xpath 'b' + elseif !x " always true + Xpath 'c' + let x = 1 + if g:boolvar " possibly undefined + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' + elseif x " never executed + Xpath 'g' + endif + Xpath 'h' +endfunction + +let boolvar = 1 +call F() +Xpath '-' + +unlet boolvar +call F() +let g:test14_result = g:Xpath + +delfunction F + +func Test_if_fail() + call assert_equal('acdfh-acfh', g:test14_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 15: Failure in argument evaluation for :if (bar) {{{1 +" +" Like previous test, except that the failing :if ... | ... | :endif +" is in a single line. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() + Xpath 'a' + let x = 0 + if x " false + Xpath 'b' + elseif !x " always true + Xpath 'c' + let x = 1 + if g:boolvar | Xpath 'd' | else | Xpath 'e' | endif + Xpath 'f' + elseif x " never executed + Xpath 'g' + endif + Xpath 'h' +endfunction + +let boolvar = 1 +call F() +Xpath '-' + +unlet boolvar +call F() +let g:test15_result = g:Xpath + +delfunction F + +func Test_if_bar_fail() + call assert_equal('acdfh-acfh', g:test15_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 16: Recognizing {} in variable name. {{{1 +"------------------------------------------------------------------------------- + +func Test_curlies() + let s:var = 66 + let ns = 's' + call assert_equal(66, {ns}:var) + + let g:a = {} + let g:b = 't' + let g:a[g:b] = 77 + call assert_equal(77, g:a['t']) +endfunc + + +"------------------------------------------------------------------------------- +" Modelines {{{1 +" vim: ts=8 sw=4 tw=80 fdm=marker +" vim: fdt=substitute(substitute(foldtext(),\ '\\%(^+--\\)\\@<=\\(\\s*\\)\\(.\\{-}\\)\:\ \\%(\"\ \\)\\=\\(Test\ \\d*\\)\:\\s*',\ '\\3\ (\\2)\:\ \\1',\ \"\"),\ '\\(Test\\s*\\)\\(\\d\\)\\D\\@=',\ '\\1\ \\2',\ "") +"------------------------------------------------------------------------------- diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index b41e4d2fba..99eb230a88 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -224,9 +224,9 @@ static void tk_getkeys(TermInput *input, bool force) while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) { if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) { forward_simple_utf8(input, &key); - } else if (key.type == TERMKEY_TYPE_UNICODE || - key.type == TERMKEY_TYPE_FUNCTION || - key.type == TERMKEY_TYPE_KEYSYM) { + } else if (key.type == TERMKEY_TYPE_UNICODE + || key.type == TERMKEY_TYPE_FUNCTION + || key.type == TERMKEY_TYPE_KEYSYM) { forward_modified_utf8(input, &key); } else if (key.type == TERMKEY_TYPE_MOUSE) { forward_mouse_event(input, &key); @@ -282,9 +282,9 @@ static bool handle_focus_event(TermInput *input) static bool handle_bracketed_paste(TermInput *input) { - if (rbuffer_size(input->read_stream.buffer) > 5 && - (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) || - !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) { + if (rbuffer_size(input->read_stream.buffer) > 5 + && (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) + || !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) { bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0'; // Advance past the sequence rbuffer_consumed(input->read_stream.buffer, 6); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 7f7d138358..202c5666a1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -81,7 +81,7 @@ UI *tui_start(void) { UI *ui = xcalloc(1, sizeof(UI)); ui->stop = tui_stop; - ui->rgb = os_getenv("NVIM_TUI_ENABLE_TRUE_COLOR") != NULL; + ui->rgb = p_tgc; ui->resize = tui_resize; ui->clear = tui_clear; ui->eol_clear = tui_eol_clear; @@ -628,8 +628,8 @@ static void tui_suspend(UI *ui) static void tui_set_title(UI *ui, char *title) { TUIData *data = ui->data; - if (!(title && unibi_get_str(data->ut, unibi_to_status_line) && - unibi_get_str(data->ut, unibi_from_status_line))) { + if (!(title && unibi_get_str(data->ut, unibi_to_status_line) + && unibi_get_str(data->ut, unibi_from_status_line))) { return; } unibi_out(ui, unibi_to_status_line); @@ -694,8 +694,8 @@ static void update_size(UI *ui) } // 2 - try from a system call(ioctl/TIOCGWINSZ on unix) - if (data->out_isatty && - !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) { + if (data->out_isatty + && !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) { goto end; } @@ -845,7 +845,7 @@ static void fix_terminfo(TUIData *data) if ((term_prog && !strcmp(term_prog, "Konsole")) || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { // Konsole uses a proprietary escape code to set the cursor shape - // and does not suppport DECSCUSR. + // and does not support DECSCUSR. data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07")); data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, @@ -854,13 +854,15 @@ static void fix_terminfo(TUIData *data) TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07")); } else if (!vte_version || atoi(vte_version) >= 3900) { // Assume that the terminal supports DECSCUSR unless it is an - // old VTE based terminal + // old VTE based terminal. This should not get wrapped for tmux, + // which will handle it via its Ss/Se terminfo extension - usually + // according to its terminal-overrides. data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b[5 q")); + "\x1b[5 q"); data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b[3 q")); + "\x1b[3 q"); data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b[2 q")); + "\x1b[2 q"); } end: diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 786f6026de..d32969f149 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -55,14 +55,25 @@ static int height, width; // // See http://stackoverflow.com/a/11172679 for a better explanation of how it // works. -#define UI_CALL(...) \ - do { \ - flush_cursor_update(); \ - for (size_t i = 0; i < ui_count; i++) { \ - UI *ui = uis[i]; \ - UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \ - } \ - } while (0) +#ifdef _MSC_VER + #define UI_CALL(funname, ...) \ + do { \ + flush_cursor_update(); \ + for (size_t i = 0; i < ui_count; i++) { \ + UI *ui = uis[i]; \ + UI_CALL_MORE(funname, __VA_ARGS__); \ + } \ + } while (0) +#else + #define UI_CALL(...) \ + do { \ + flush_cursor_update(); \ + for (size_t i = 0; i < ui_count; i++) { \ + UI *ui = uis[i]; \ + UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \ + } \ + } while (0) +#endif #define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore) #define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6 #define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__) diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 69ac18ad54..f16b4264d7 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -4,29 +4,29 @@ * The saved lines are stored in a list of lists (one for each buffer): * * b_u_oldhead------------------------------------------------+ - * | - * V - * +--------------+ +--------------+ +--------------+ - * b_u_newhead--->| u_header | | u_header | | u_header | - * | uh_next------>| uh_next------>| uh_next---->NULL - * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | - * | uh_entry | | uh_entry | | uh_entry | - * +--------|-----+ +--------|-----+ +--------|-----+ - * | | | - * V V V - * +--------------+ +--------------+ +--------------+ - * | u_entry | | u_entry | | u_entry | - * | ue_next | | ue_next | | ue_next | - * +--------|-----+ +--------|-----+ +--------|-----+ - * | | | - * V V V - * +--------------+ NULL NULL - * | u_entry | - * | ue_next | - * +--------|-----+ - * | - * V - * etc. + * | + * V + * +--------------+ +--------------+ +--------------+ + * b_u_newhead--->| u_header | | u_header | | u_header | + * | uh_next------>| uh_next------>| uh_next---->NULL + * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | + * | uh_entry | | uh_entry | | uh_entry | + * +--------|-----+ +--------|-----+ +--------|-----+ + * | | | + * V V V + * +--------------+ +--------------+ +--------------+ + * | u_entry | | u_entry | | u_entry | + * | ue_next | | ue_next | | ue_next | + * +--------|-----+ +--------|-----+ +--------|-----+ + * | | | + * V V V + * +--------------+ NULL NULL + * | u_entry | + * | ue_next | + * +--------|-----+ + * | + * V + * etc. * * Each u_entry list contains the information for one undo or redo. * curbuf->b_u_curhead points to the header of the last undo (the next redo), @@ -37,30 +37,30 @@ * uh_seq field is numbered sequentially to be able to find a newer or older * branch. * - * +---------------+ +---------------+ - * b_u_oldhead --->| u_header | | u_header | - * | uh_alt_next ---->| uh_alt_next ----> NULL - * NULL <----- uh_alt_prev |<------ uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * V V - * +---------------+ +---------------+ - * | u_header | | u_header | - * | uh_alt_next | | uh_alt_next | - * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * V V - * NULL +---------------+ +---------------+ - * | u_header | | u_header | - * | uh_alt_next ---->| uh_alt_next | - * | uh_alt_prev |<------ uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * etc. etc. + * +---------------+ +---------------+ + * b_u_oldhead --->| u_header | | u_header | + * | uh_alt_next ---->| uh_alt_next ----> NULL + * NULL <----- uh_alt_prev |<------ uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * V V + * +---------------+ +---------------+ + * | u_header | | u_header | + * | uh_alt_next | | uh_alt_next | + * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * V V + * NULL +---------------+ +---------------+ + * | u_header | | u_header | + * | uh_alt_next ---->| uh_alt_next | + * | uh_alt_prev |<------ uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * etc. etc. * * * All data is allocated and will all be freed when the buffer is unloaded. @@ -77,6 +77,7 @@ #include <limits.h> #include <stdbool.h> #include <string.h> +#include <fcntl.h> #include "nvim/vim.h" #include "nvim/ascii.h" @@ -1781,7 +1782,13 @@ void undo_time(long step, int sec, int file, int absolute) /* "target" is the node below which we want to be. * Init "closest" to a value we can't reach. */ if (absolute) { - target = step; + if (step == 0) { + // target 0 does not exist, got to 1 and above it. + target = 1; + above = true; + } else { + target = step; + } closest = -1; } else { /* When doing computations with time_t subtract starttime, because @@ -2221,12 +2228,17 @@ static void u_undoredo(int undo) /* * restore marks from before undo/redo */ - for (i = 0; i < NMARKS; ++i) + for (i = 0; i < NMARKS; ++i) { if (curhead->uh_namedm[i].mark.lnum != 0) { free_fmark(curbuf->b_namedm[i]); curbuf->b_namedm[i] = curhead->uh_namedm[i]; + } + if (namedm[i].mark.lnum != 0) { curhead->uh_namedm[i] = namedm[i]; + } else { + curhead->uh_namedm[i].mark.lnum = 0; } + } if (curhead->uh_visual.vi_start.lnum != 0) { curbuf->b_visual = curhead->uh_visual; curhead->uh_visual = visualinfo; @@ -2864,9 +2876,7 @@ static char_u *u_save_line(linenr_T lnum) /// @return true if the buffer has changed bool bufIsChanged(buf_T *buf) { - return - !bt_dontwrite(buf) && - (buf->b_changed || file_ff_differs(buf, true)); + return !bt_dontwrite(buf) && (buf->b_changed || file_ff_differs(buf, true)); } /// Check if the 'modified' flag is set, or 'ff' has changed (only need to @@ -2876,9 +2886,8 @@ bool bufIsChanged(buf_T *buf) /// @return true if the current buffer has changed bool curbufIsChanged(void) { - return - !bt_dontwrite(curbuf) && - (curbuf->b_changed || file_ff_differs(curbuf, true)); + return (!bt_dontwrite(curbuf) + && (curbuf->b_changed || file_ff_differs(curbuf, true))); } /* diff --git a/src/nvim/version.c b/src/nvim/version.c index 2b0d6f22f2..7ee3b596de 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -35,11 +35,6 @@ char *Version = VIM_VERSION_SHORT; char *longVersion = NVIM_VERSION_LONG; -char *longVersionWithDate = NVIM_VERSION_LONG \ - " (compiled " __DATE__ " " __TIME__ ")"; -#ifdef NVIM_VERSION_COMMIT -char *version_commit = "Commit: " NVIM_VERSION_COMMIT; -#endif char *version_buildtype = "Build type: " NVIM_VERSION_BUILD_TYPE; char *version_cflags = "Compilation: " NVIM_VERSION_CFLAGS; @@ -74,275 +69,966 @@ static char *features[] = { // clang-format off static int included_patches[] = { - // 922, + 1809, + 1808, + 1806, + 1799, + 1757, + 1755, + 1753, + 1654, + 1652, + 1643, + 1641, + + // 1600 NA + // 1599 NA + // 1598 NA + // 1597 NA + // 1596, + // 1595 NA + // 1594 NA + // 1593 NA + // 1592, + // 1591, + // 1590, + // 1589, + // 1588, + // 1587 NA + // 1586, + // 1585, + // 1584 NA + // 1583 NA + // 1582, + + // 1581, + // 1580, + // 1579, + // 1578, + // 1577, + 1576, + // 1575 NA + 1574, + // 1573, + // 1572 NA + // 1571, + 1570, + 1569, + // 1568, + // 1567, + // 1566 NA + // 1565, + // 1564, + // 1563, + // 1562 NA + // 1561 NA + // 1560, + // 1559, + // 1558, + // 1557, + // 1556, + // 1555, + // 1554, + // 1553, + // 1552, + // 1551, + // 1550, + // 1549, + // 1548, + // 1547, + // 1546, + // 1545 NA + // 1544 NA + // 1543 NA + // 1542 NA + // 1541 NA + // 1540 NA + // 1539 NA + // 1538 NA + // 1537 NA + // 1536 NA + // 1535, + // 1534 NA + // 1533, + // 1532 NA + // 1531 NA + // 1530 NA + // 1529 NA + // 1528, + // 1527 NA + // 1526 NA + // 1525 NA + // 1524 NA + // 1523 NA + // 1522 NA + // 1521, + // 1520 NA + // 1519 NA + // 1518 NA + // 1517 NA + // 1516, + // 1515 NA + // 1514 NA + // 1513, + // 1512 NA + 1511, + // 1510 NA + // 1509 NA + // 1508 NA + // 1507 NA + // 1506 NA + // 1505 NA + // 1504 NA + // 1503 NA + // 1502 NA + // 1501 NA + 1500, + // 1499, + // 1498 NA + // 1497 NA + // 1496 NA + // 1495 NA + // 1494, + // 1493 NA + // 1492, + // 1491, + // 1490 NA + // 1489 NA + // 1488 NA + // 1487 NA + // 1486, + // 1485 NA + // 1484 NA + // 1483 NA + // 1482 NA + // 1481 NA + // 1480, + // 1479, + // 1478, + // 1477, + // 1476 NA + // 1475 NA + // 1474 NA + // 1473 NA + // 1472 NA + // 1471 NA + // 1470 NA + // 1469 NA + // 1468, + // 1467 NA + // 1466 NA + // 1465 NA + // 1464, + // 1463 NA + // 1462 NA + // 1461 NA + // 1460 NA + // 1459 NA + // 1458 NA + // 1457 NA + // 1456, + // 1455 NA + // 1454 NA + // 1453 NA + // 1452 NA + // 1451 NA + // 1450 NA + // 1449 NA + // 1448 NA + // 1447 NA + // 1446 NA + // 1445 NA + // 1444 NA + // 1443 NA + // 1442 NA + // 1441 NA + // 1440 NA + // 1439 NA + // 1438 NA + // 1437 NA + // 1436 NA + // 1435 NA + // 1434 NA + // 1433 NA + // 1432 NA + // 1431 NA + // 1430 NA + // 1429 NA + // 1428 NA + // 1427 NA + // 1426 NA + 1425, + // 1424 NA + // 1423 NA + // 1422 NA + // 1421 NA + // 1420 NA + // 1419 NA + // 1418 NA + // 1417 NA + // 1416 NA + // 1415 NA + // 1414 NA + // 1413 NA + // 1412 NA + // 1411 NA + 1410, + // 1409 NA + // 1408 NA + // 1407 NA + 1406, + 1405, + // 1404 NA + // 1403 NA + // 1402 NA + 1401, + // 1400 NA + // 1399 NA + // 1398 NA + // 1397, + // 1396, + // 1395 NA + // 1394, + // 1393 NA + // 1392 NA + // 1391 NA + // 1390 NA + // 1389 NA + // 1388, + // 1387 NA + // 1386 NA + // 1385 NA + // 1384, + // 1383 NA + // 1382 NA + // 1381 NA + // 1380 NA + // 1379 NA + // 1378 NA + // 1377 NA + // 1376 NA + // 1375 NA + // 1374 NA + // 1373 NA + // 1372 NA + // 1371 NA + // 1370 NA + // 1369 NA + // 1368 NA + // 1367 NA + 1366, + // 1365, + // 1364 NA + // 1363 NA + // 1362 NA + // 1361 NA + // 1360 NA + // 1359 NA + // 1358 NA + // 1357 NA + // 1356 NA + // 1355 NA + // 1354 NA + // 1353 NA + // 1352, + // 1351 NA + // 1350 NA + // 1349 NA + // 1348 NA + 1347, + 1346, + // 1345 NA + // 1344 NA + // 1343 NA + // 1342 NA + // 1341 NA + // 1340 NA + // 1339 NA + // 1338 NA + // 1337 NA + // 1336 NA + // 1335 NA + // 1334 NA + // 1333 NA + // 1332 NA + // 1331 NA + // 1330 NA + // 1329 NA + // 1328 NA + // 1327 NA + // 1326 NA + // 1325 NA + // 1324 NA + // 1323 NA + // 1322 NA + // 1321 NA + // 1320 NA + // 1319 NA + // 1318 NA + // 1317 NA + // 1316 NA + // 1315 NA + // 1314 NA + // 1313 NA + // 1312 NA + // 1311 NA + // 1310 NA + 1309, + // 1308 NA + // 1307 NA + // 1306 NA + // 1305, + 1304, + // 1303 NA + // 1302 NA + // 1301 NA + // 1300 NA + // 1299 NA + // 1298 NA + // 1297 NA + 1296, + // 1295 NA + // 1294 NA + // 1293 NA + 1292, + // 1291 NA + // 1290 NA + // 1289 NA + // 1288 NA + // 1287 NA + // 1286 NA + 1285, + 1284, + // 1283 NA + 1282, + // 1281, + // 1280 NA + // 1279 NA + // 1278 NA + // 1277 NA + // 1276, + // 1275 NA + // 1274 NA + // 1273, + // 1272 NA + 1271, + // 1270 NA + 1269, + // 1268 NA + 1267, + // 1266 + // 1265 NA + // 1264 NA + // 1263 NA + // 1262 NA + // 1261 NA + // 1260 NA + 1259, + // 1258 NA + // 1257 NA + // 1256 NA + // 1255 NA + // 1254 NA + // 1253 NA + // 1252 NA + // 1251 NA + // 1250 NA + // 1249 NA + // 1248 NA + // 1247 NA + // 1246 NA + // 1245 NA + // 1244 NA + // 1243 NA + // 1242 NA + // 1241 NA + // 1240 NA + // 1239 NA + // 1238 NA + // 1237, + 1236, + // 1235 NA + // 1234 NA + // 1233 NA + // 1232 NA + // 1231 NA + // 1230 NA + // 1229 NA + 1228, + // 1227 NA + // 1226 NA + // 1225 NA + // 1224 NA + // 1223, + // 1222 NA + // 1221 NA + // 1220 NA + // 1219 NA + // 1218 NA + // 1217 NA + // 1216 NA + // 1215 NA + // 1214 NA + // 1213 NA + // 1212 NA + // 1211 NA + // 1210 NA + // 1209 NA + // 1208 NA + // 1207 NA + // 1206 NA + // 1205 NA + // 1204 NA + // 1203 NA + // 1202 NA + // 1201 NA + // 1200 NA + // 1199 NA + // 1198 NA + // 1197 NA + // 1196 NA + // 1195 NA + // 1194 NA + // 1193 NA + // 1192 NA + // 1191 NA + // 1190 NA + // 1189 NA + // 1188 NA + // 1187 NA + // 1186, + // 1185 NA + // 1184 NA + // 1183 NA + // 1182 NA + 1181, + 1180, + // 1179, + 1178, + // 1177 NA + // 1176 NA + // 1175 NA + // 1174 NA + 1173, + // 1172 NA + // 1171 NA + // 1170 NA + // 1169 NA + 1168, + 1167, + 1166, + // 1165 NA + 1164, + 1163, + // 1162 NA + // 1161, + 1160, + // 1159 NA + // 1158 NA + 1157, + // 1156 NA + // 1155 NA + // 1154 NA + // 1153, + // 1152 NA + // 1151, + // 1150, + 1149, + // 1148 NA + // 1147, + // 1146 NA + // 1145 NA + 1144, + 1143, + // 1142, + 1141, + // 1140, + // 1139 NA + // 1138 NA + 1137, + // 1136, + // 1135 NA + // 1134 NA + // 1133 NA + // 1132, + // 1131 NA + // 1130, + // 1129 NA + // 1128 NA + // 1127 NA + // 1126, + // 1125 NA + // 1124 NA + // 1123, + // 1122 NA + // 1121, + 1120, + // 1119, + 1118, + 1117, + 1116, + // 1115 NA + 1114, + 1113, + 1112, + // 1111, + // 1110, + // 1109 NA + // 1108, + 1107, + // 1106 NA + 1105, + // 1104 NA + // 1103 NA + // 1102, + 1101, + // 1100 NA + // 1099 NA + // 1098 NA + // 1097, + // 1096, + // 1095 NA + // 1094, + 1093, + 1092, + 1091, + // 1090, + 1089, + 1088, + 1087, + 1086, + 1085, + 1084, + // 1083 NA + // 1082 NA + 1081, + // 1080 NA + // 1079, + // 1078 NA + // 1077 NA + 1076, + 1075, + // 1074 NA, + // 1073, + 1072, + // 1071, + // 1070 NA + // 1069 NA + // 1068, + // 1067 NA + // 1066 NA + 1065, + // 1064, + // 1063 NA + // 1062 NA + // 1061, + // 1060 NA + // 1059, + // 1058, + // 1057, + // 1056, + 1055, + 1054, + // 1053, + 1052, + // 1051, + // 1050, + 1049, + 1048, + 1047, + // 1046, + // 1045 NA + // 1044 NA + // 1043 NA + 1042, + 1041, + // 1040 NA + // 1039, + // 1038 NA + 1037, + // 1036, + 1035, + // 1034, + // 1033 NA + 1032, + // 1031 NA, + 1030, + 1029, + // 1028 NA + 1027, + // 1026 NA + // 1025 NA + // 1024 NA + // 1023 NA + // 1022 NA + // 1021 NA + // 1020 NA + // 1019 NA + // 1018, + // 1017, + // 1016 NA + 1015, + // 1014 NA + 1013, + // 1012 NA + // 1011 NA + // 1010 NA, + // 1009 NA + // 1008 NA + 1007, + 1006, + // 1005 NA, + // 1004 NA, + // 1003 NA, + // 1002 NA, + 1001, + 1000, + // 999 NA + 998, + // 997 NA + // 996 NA + // 995 NA + // 994 NA + // 993 NA + // 992 NA + 991, + // 990 NA + 989, + // 988 NA + // 987 NA + // 986 NA + // 985 NA + 984, + // 983 NA + // 982 NA + 981, + 980, + // 979 NA + 978, + 977, + // 976 NA + 975, + 974, + 973, + 972, + // 971 NA + // 970 NA + // 969 NA + // 968 NA + // 967 NA + // 966 NA + // 965 NA + // 964 NA + 963, + // 962 NA + 961, + // 960 NA + // 959 NA + 958, + 957, + // 956 NA + 955, + // 954 NA + 953, + 952, + 951, + 950, + 949, + // 948 NA + // 947 NA + 946, + 945, + 944, + // 943 NA + 942, + 941, + // 940 NA + 939, + // 938 NA + 937, + 936, + // 935 NA + // 934 NA + 933, + 932, + // 931 NA + // 930 NA + 929, + // 928 NA + // 927 NA + 926, + 925, + // 924 NA + // 923 NA + 922, // 921 NA // 920 NA // 919 NA // 918 NA // 917 NA 916, - // 915, - // 914, + 915, + // 914 NA // 913 NA - // 912, + 912, // 911 NA // 910 NA - // 909, + // 909 NA // 908 NA // 907 NA // 906 NA - // 905, - // 904, - // 903, + // 905 NA + // 904 NA + 903, // 902 NA - // 901, + 901, // 900 NA // 899 NA - // 898, - // 897, - // 896, - // 895, + 898, + // 897 NA + 896, + 895, // 894 NA - // 893, - // 892, - // 891, + 893, + // 892 NA + 891, // 890 NA - // 889, - // 888, - // 887, + 889, + 888, + 887, // 886 NA - // 885, + 885, // 884 NA - // 883, - // 882, - // 881, + 883, + 882, + 881, // 880 NA - // 879, - // 878, - // 877, + 879, + 878, + 877, // 876 NA // 875 NA // 874 NA - // 873, + // 873 NA // 872 NA - // 871, - // 870, + 871, + 870, // 869 NA - // 868, + 868, // 867 NA - // 866, - // 865, - // 864, - // 863, + // 866 NA + // 865 NA + // 864 NA + // 863 NA // 862 NA // 861 NA - // 860, - // 859, - // 858, - // 857, - // 856, + // 860 NA + 859, + 858, + 857, + 856, // 855 NA - // 854, - // 853, + // 854 NA + 853, // 852 NA // 851 NA // 850 NA 849, - // 848, - // 847, + 848, + 847, // 846 NA - // 845, - // 844, - // 843, - // 842, - // 841, + 845, + 844, + 843, + // 842 NA + // 841 NA // 840 NA - // 839, - // 838, + // 839 NA + // 838 NA // 837 NA 836, - // 835, - // 834, - // 833, - // 832, - // 831, - // 830, + 835, + 834, + 833, + 832, + 831, + 830, // 829 NA - // 828, - // 827, + 828, + // 827 NA 826, - // 825, + 825, // 824 NA - // 823, - // 822, - // 821, - // 820, - // 819, - // 818, - // 817, - // 816, - // 815, - // 814, + 823, + 822, + // 821 NA + 820, + 819, + 818, + 817, + 816, + 815, + 814, 813, - // 812, - // 811, - // 810, + // 812 NA + 811, + 810, 809, - // 808, - // 807, - // 806, - // 805, - // 804, - // 803, + // 808 NA + 807, + 806, + 805, + // 804 NA + 803, 802, - // 801, - // 800, + 801, + 800, 799, - // 798, - // 797, + 798, + // 797 NA // 796 NA - // 795, + 795, // 794 NA 793, - // 792, + 792, 791, 790, 789, // 788 NA 787, 786, - // 785, + 785, 784, // 783 NA - // 782, + 782, 781, - // 780 NA - // 779, - // 778 NA + 780, + 779, + 778, // 777 NA 776, 775, 774, 773, // 772 NA - // 771, + 771, // 770 NA - // 769, - // 768, - // 767, + 769, + 768, + // 767 NA // 766 NA - // 765, - // 764, + 765, + 764, // 763 NA // 762 NA // 761 NA - // 760, + 760, // 759 NA - // 758, + 758, // 757 NA // 756 NA - // 755, - // 754, - // 753, - // 752, + 755, + 754, + 753, + // 752 NA // 751 NA // 750 NA - // 749, - // 748, - // 747, - // 746, - // 745, + 749, + 748, + 747, + 746, + 745, // 744 NA - // 743, - // 742, - // 741, - // 740, - // 739, + 743, + 742, + 741, + 740, + 739, // 738 NA - // 737, + 737, 736, - // 735, - // 734, - // 733, - // 732, + // 735 NA + 734, + // 733 NA + 732, // 731 NA // 730 NA - // 729, + 729, // 728 NA // 727 NA // 726 NA - // 725, + // 725 NA // 724 NA - // 723, - // 722, - // 721, + 723, + 722, + 721, // 720 NA 719, - // 718, - // 717, - // 716, - // 715, - // 714, + 718, + 717, + 716, + 715, + 714, 713, 712, 711, 710, - // 709, - // 708, + 709, + 708, 707, 706, // 705 NA - // 704, + 704, // 703 NA 702, // 701 NA - // 700, + 700, 699, - // 698, - // 697, + 698, + 697, 696, 695, - // 694, - // 693, + 694, + 693, // 692 NA // 691 NA 690, - // 689, - // 688, + 689, + 688, // 687 NA 686, - // 685, - // 684, + 685, + // 684 NA // 683 NA - // 682, + 682, // 681 NA - // 680, + 680, // 679 NA // 678 NA // 677 NA // 676 NA - // 675, + 675, // 674 NA 673, - // 672, - // 671, - // 670, + 672, + 671, + 670, // 669 NA 668, 667, // 666 NA - // 665, + 665, // 664 NA // 663 NA - // 662, - // 661, + 662, + // 661 NA 660, 659, - // 658, + 658, // 657 NA - // 656, - // 655, - // 654, + 656, + 655, + 654, 653, // 652 NA 651, @@ -351,49 +1037,49 @@ static int included_patches[] = { // 648 NA // 647 NA 646, - // 645, + 645, // 644 NA - // 643, - // 642, - // 641, - // 640, - // 639, + 643, + 642, + // 641 NA + 640, + 639, // 638 NA 637, 636, - // 635, - // 634, + 635, + 634, 633, // 632 NA 631, 630, 629, - // 628, + // 628 NA // 627 NA // 626 NA // 625 NA - // 624, + // 624 NA 623, // 622 NA // 621 NA - // 620, + // 620 NA // 619 NA // 618 NA 617, - // 616, + 616, 615, - // 614, - // 613, - // 612, + 614, + 613, + 612, // 611 NA // 610 NA 609, - // 608, - // 607, + 608, + // 607 NA 606, - // 605, - // 604, - // 603, + 605, + 604, + 603, 602, 601, 600, @@ -423,11 +1109,11 @@ static int included_patches[] = { 576, 575, 574, - // 573, + 573, 572, // 571 NA // 570 NA - // 569, + 569, 568, 567, 566, @@ -752,13 +1438,13 @@ static int included_patches[] = { 247, // 246 NA 245, - // 244, + // 244 NA 243, 242, 241, 240, 239, - // 238, + // 238 NA 237, 236, 235, @@ -1098,10 +1784,7 @@ void list_version(void) { // When adding features here, don't forget to update the list of // internal variables in eval.c! - MSG(longVersionWithDate); -#ifdef NVIM_VERSION_COMMIT - MSG(version_commit); -#endif + MSG(longVersion); MSG(version_buildtype); MSG(version_cflags); diff --git a/src/nvim/vim.h b/src/nvim/vim.h index fa00d9efcf..165a44a148 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -27,20 +27,21 @@ Error: configure did not run properly.Check auto/config.log. # endif #endif -/* user ID of root is usually zero, but not for everybody */ -#define ROOT_UID 0 - +#include "nvim/os/os_defs.h" /* bring lots of system header files */ -/* Can't use "PACKAGE" here, conflicts with a Perl include file. */ -#ifndef VIMPACKAGE -# define VIMPACKAGE "vim" -#endif +/// length of a buffer to store a number in ASCII (64 bits binary + NUL) +#define NUMBUFLEN 65 -#include "nvim/os/os_defs.h" /* bring lots of system header files */ +// flags for vim_str2nr() +#define STR2NR_BIN 1 +#define STR2NR_OCT 2 +#define STR2NR_HEX 4 +#define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX) +#define STR2NR_FORCE 8 // only when ONE of the above is used -#define NUMBUFLEN 30 /* length of a buffer to store a number in ASCII */ +#define MAX_TYPENR 65535 -# define MAX_TYPENR 65535 +#define ROOT_UID 0 #include "nvim/keymap.h" #include "nvim/macros.h" @@ -200,15 +201,6 @@ enum { #define MAYBE 2 /* sometimes used for a variant on TRUE */ -/* - * Motion types, used for operators and for yank/delete registers. - */ -#define MCHAR 0 /* character-wise movement/register */ -#define MLINE 1 /* line-wise movement/register */ -#define MBLOCK 2 /* block-wise register */ - -#define MAUTO 0xff /* Decide between MLINE/MCHAR */ - #define STATUS_HEIGHT 1 /* height of a status line under a window */ #define QF_WINHEIGHT 10 /* default height for quickfix window */ @@ -232,15 +224,6 @@ enum { /* Size in bytes of the hash used in the undo file. */ #define UNDO_HASH_SIZE 32 -#ifdef HAVE_FCNTL_H -# include <fcntl.h> -#endif - - -#ifndef O_NOFOLLOW -# define O_NOFOLLOW 0 -#endif - /* * defines to avoid typecasts from (char_u *) to (char *) and back * (vim_strchr() and vim_strrchr() are now in alloc.c) @@ -279,26 +262,11 @@ enum { # define vim_strpbrk(s, cs) (char_u *)strpbrk((char *)(s), (char *)(cs)) -#define MSG(s) msg((char_u *)(s)) -#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr)) -#define EMSG(s) emsg((char_u *)(s)) -#define EMSG2(s, p) emsg2((char_u *)(s), (char_u *)(p)) -#define EMSG3(s, p, q) emsg3((char_u *)(s), (char_u *)(p), \ - (char_u *)(q)) -#define EMSGN(s, n) emsgn((char_u *)(s), (int64_t)(n)) -#define EMSGU(s, n) emsgu((char_u *)(s), (uint64_t)(n)) -#define OUT_STR(s) out_str((char_u *)(s)) -#define OUT_STR_NF(s) out_str_nf((char_u *)(s)) -#define MSG_PUTS(s) msg_puts((char_u *)(s)) -#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a)) -#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s)) -#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0) -#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a)) - -/* Prefer using emsg3(), because perror() may send the output to the wrong - * destination and mess up the screen. */ -#define PERROR(msg) \ - (void) emsg3((char_u *) "%s: %s", (char_u *)msg, (char_u *)strerror(errno)) +#include "nvim/message.h" + +// Prefer using emsgf(), because perror() may send the output to the wrong +// destination and mess up the screen. +#define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno)) #define SHOWCMD_COLS 10 /* columns needed by shown command */ #define STL_MAX_ITEM 80 /* max nr of %<flag> in statusline */ diff --git a/src/nvim/window.c b/src/nvim/window.c index 16ff7dfb14..bea55c465f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -97,7 +97,7 @@ do_window ( * don't replicate the quickfix buffer. */ if (bt_quickfix(curbuf)) goto newwindow; - win_split((int)Prenum, 0); + (void)win_split((int)Prenum, 0); break; /* split current window in two parts, vertically */ @@ -108,7 +108,7 @@ do_window ( * don't replicate the quickfix buffer. */ if (bt_quickfix(curbuf)) goto newwindow; - win_split((int)Prenum, WSP_VERT); + (void)win_split((int)Prenum, WSP_VERT); break; /* split current window and edit alternate file */ @@ -248,7 +248,7 @@ newwindow: /* First create a new tab with the window, then go back to * the old tab and close the window there. */ wp = curwin; - if (win_new_tabpage((int)Prenum) == OK + if (win_new_tabpage((int)Prenum, NULL) == OK && valid_tabpage(oldtab)) { newtab = curtab; goto_tabpage_tp(oldtab, TRUE, TRUE); @@ -1044,19 +1044,21 @@ static void win_init_some(win_T *newp, win_T *oldp) } -/* - * Check if "win" is a pointer to an existing window. - */ -int win_valid(win_T *win) +/// Check if "win" is a pointer to an existing window in the current tabpage. +/// +/// @param win window to check +bool win_valid(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - if (win == NULL) - return FALSE; + if (win == NULL) { + return false; + } + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp == win) { - return TRUE; + return true; } } - return FALSE; + return false; } /* @@ -1732,21 +1734,18 @@ close_windows ( shell_new_rows(); } -/* - * Return TRUE if the current window is the only window that exists (ignoring - * "aucmd_win"). - * Returns FALSE if there is a window, possibly in another tab page. - */ -static int last_window(void) +/// Check that current window is the last one. +/// +/// @return true if the current window is the only window that exists, false if +/// there is another, possibly in another tab page. +static bool last_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return one_window() && first_tabpage->tp_next == NULL; } -/* - * Return TRUE if there is only one window other than "aucmd_win" in the - * current tab page. - */ -bool one_window(void) +/// Check that current tab page contains no more then one window other than +/// "aucmd_win". +bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { bool seen_one = false; @@ -1761,14 +1760,20 @@ bool one_window(void) return true; } -/* - * Close the possibly last window in a tab page. - * Returns TRUE when the window was closed already. - */ -static int close_last_window_tabpage(win_T *win, int free_buf, tabpage_T *prev_curtab) +/// Close the possibly last window in a tab page. +/// +/// @param win window to close +/// @param free_buf whether to free the window's current buffer +/// @param prev_curtab previous tabpage that will be closed if "win" is the +/// last window in the tabpage +/// +/// @return true when the window was closed already. +static bool close_last_window_tabpage(win_T *win, bool free_buf, + tabpage_T *prev_curtab) + FUNC_ATTR_NONNULL_ARG(1) { if (firstwin != lastwin) { - return FALSE; + return false; } buf_T *old_curbuf = curbuf; @@ -1809,14 +1814,15 @@ static int close_last_window_tabpage(win_T *win, int free_buf, tabpage_T *prev_c terminal_resize(term, 0, 0); } - /* Since goto_tabpage_tp above did not trigger *Enter autocommands, do - * that now. */ - apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, FALSE, curbuf); - apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); - if (old_curbuf != curbuf) - apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); - return TRUE; + // Since goto_tabpage_tp above did not trigger *Enter autocommands, do + // that now. + apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, curbuf); + apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); + apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); + if (old_curbuf != curbuf) { + apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf); + } + return true; } /* @@ -1907,9 +1913,16 @@ int win_close(win_T *win, int free_buf) */ if (win->w_buffer != NULL) { win->w_closing = true; - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, TRUE); - if (win_valid(win)) + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true); + if (win_valid(win)) { win->w_closing = false; + } + + // Make sure curbuf is valid. It can become invalid if 'bufhidden' is + // "wipe". + if (!buf_valid(curbuf)) { + curbuf = firstbuf; + } } if (only_one_window() && win_valid(win) && win->w_buffer == NULL @@ -2301,20 +2314,22 @@ static win_T *frame2win(frame_T *frp) return frp->fr_win; } -/* - * Return TRUE if frame "frp" contains window "wp". - */ -static int frame_has_win(frame_T *frp, win_T *wp) +/// Check that the frame "frp" contains the window "wp". +/// +/// @param frp frame +/// @param wp window +static bool frame_has_win(frame_T *frp, win_T *wp) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { - frame_T *p; - - if (frp->fr_layout == FR_LEAF) + if (frp->fr_layout == FR_LEAF) { return frp->fr_win == wp; - - for (p = frp->fr_child; p != NULL; p = p->fr_next) - if (frame_has_win(p, wp)) - return TRUE; - return FALSE; + } + for (frame_T *p = frp->fr_child; p != NULL; p = p->fr_next) { + if (frame_has_win(p, wp)) { + return true; + } + } + return false; } /* @@ -2406,58 +2421,72 @@ frame_new_height ( topfrp->fr_height = height; } -/* - * Return TRUE if height of frame "frp" should not be changed because of - * the 'winfixheight' option. - */ -static int frame_fixed_height(frame_T *frp) +/// Return true if height of frame "frp" should not be changed because of +/// the 'winfixheight' option. +/// +/// @param frp frame +/// +/// @return true if the frame has a fixed height +static bool frame_fixed_height(frame_T *frp) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - /* frame with one window: fixed height if 'winfixheight' set. */ - if (frp->fr_win != NULL) + // frame with one window: fixed height if 'winfixheight' set. + if (frp->fr_win != NULL) { return frp->fr_win->w_p_wfh; - + } if (frp->fr_layout == FR_ROW) { - /* The frame is fixed height if one of the frames in the row is fixed - * height. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) - if (frame_fixed_height(frp)) - return TRUE; - return FALSE; + // The frame is fixed height if one of the frames in the row is fixed + // height. + for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + if (frame_fixed_height(frp)) { + return true; + } + } + return false; } - /* frp->fr_layout == FR_COL: The frame is fixed height if all of the - * frames in the row are fixed height. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) - if (!frame_fixed_height(frp)) - return FALSE; - return TRUE; + // frp->fr_layout == FR_COL: The frame is fixed height if all of the + // frames in the row are fixed height. + for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + if (!frame_fixed_height(frp)) { + return false; + } + } + return true; } -/* - * Return TRUE if width of frame "frp" should not be changed because of - * the 'winfixwidth' option. - */ -static int frame_fixed_width(frame_T *frp) +/// Return true if width of frame "frp" should not be changed because of +/// the 'winfixwidth' option. +/// +/// @param frp frame +/// +/// @return true if the frame has a fixed width +static bool frame_fixed_width(frame_T *frp) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - /* frame with one window: fixed width if 'winfixwidth' set. */ - if (frp->fr_win != NULL) + // frame with one window: fixed width if 'winfixwidth' set. + if (frp->fr_win != NULL) { return frp->fr_win->w_p_wfw; - + } if (frp->fr_layout == FR_COL) { - /* The frame is fixed width if one of the frames in the row is fixed - * width. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) - if (frame_fixed_width(frp)) - return TRUE; - return FALSE; + // The frame is fixed width if one of the frames in the row is fixed + // width. + for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + if (frame_fixed_width(frp)) { + return true; + } + } + return false; } - /* frp->fr_layout == FR_ROW: The frame is fixed width if all of the - * frames in the row are fixed width. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) - if (!frame_fixed_width(frp)) - return FALSE; - return TRUE; + // frp->fr_layout == FR_ROW: The frame is fixed width if all of the + // frames in the row are fixed width. + for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) { + if (!frame_fixed_width(frp)) { + return false; + } + } + return true; } /* @@ -2919,18 +2948,19 @@ void free_tabpage(tabpage_T *tp) unref_var_dict(tp->tp_vars); - + xfree(tp->localdir); // Free tab-local working directory xfree(tp); } -/* - * Create a new Tab page with one window. - * It will edit the current buffer, like after ":split". - * When "after" is 0 put it just after the current Tab page. - * Otherwise put it just before tab page "after". - * Return FAIL or OK. - */ -int win_new_tabpage(int after) +/// Create a new tabpage with one window. +/// +/// It will edit the current buffer, like after :split. +/// +/// @param after Put new tabpage after tabpage "after", or after the current +/// tabpage in case of 0. +/// @param filename Will be passed to apply_autocmds(). +/// @return Was the new tabpage created successfully? FAIL or OK. +int win_new_tabpage(int after, char_u *filename) { tabpage_T *tp = curtab; tabpage_T *newtp; @@ -2970,10 +3000,12 @@ int win_new_tabpage(int after) newtp->tp_topframe = topframe; last_status(FALSE); - redraw_all_later(CLEAR); - apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); + + apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); + apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); + apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); + return OK; } @@ -2994,7 +3026,7 @@ int may_open_tabpage(void) if (n != 0) { cmdmod.tab = 0; /* reset it to avoid doing it twice */ postponed_split_tab = 0; - return win_new_tabpage(n); + return win_new_tabpage(n, NULL); } return FAIL; } @@ -3018,9 +3050,11 @@ int make_tabpages(int maxcount) */ block_autocmds(); - for (todo = count - 1; todo > 0; --todo) - if (win_new_tabpage(0) == FAIL) + for (todo = count - 1; todo > 0; --todo) { + if (win_new_tabpage(0, NULL) == FAIL) { break; + } + } unblock_autocmds(); @@ -3028,10 +3062,10 @@ int make_tabpages(int maxcount) return count - todo; } -/* - * Return TRUE when "tpc" points to a valid tab page. - */ -bool valid_tabpage(tabpage_T *tpc) +/// Check that tpc points to a valid tab page. +/// +/// @param[in] tpc Tabpage to check. +bool valid_tabpage(tabpage_T *tpc) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { FOR_ALL_TABS(tp) { if (tp == tpc) { @@ -3252,17 +3286,27 @@ void goto_tabpage_win(tabpage_T *tp, win_T *wp) } } -/* - * Move the current tab page to before tab page "nr". - */ +// Move the current tab page to after tab page "nr". void tabpage_move(int nr) { - int n = nr; - tabpage_T *tp; + int n = 1; + tabpage_T *tp; + tabpage_T *tp_dst; if (first_tabpage->tp_next == NULL) return; + for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) { + ++n; + } + + if (tp == curtab || (nr > 0 && tp->tp_next != NULL + && tp->tp_next == curtab)) { + return; + } + + tp_dst = tp; + /* Remove the current tab page from the list of tab pages. */ if (curtab == first_tabpage) first_tabpage = curtab->tp_next; @@ -3275,15 +3319,13 @@ void tabpage_move(int nr) tp->tp_next = curtab->tp_next; } - /* Re-insert it at the specified position. */ - if (n <= 0) { + // Re-insert it at the specified position. + if (nr <= 0) { curtab->tp_next = first_tabpage; first_tabpage = curtab; } else { - for (tp = first_tabpage; tp->tp_next != NULL && n > 1; tp = tp->tp_next) - --n; - curtab->tp_next = tp->tp_next; - tp->tp_next = curtab; + curtab->tp_next = tp_dst->tp_next; + tp_dst->tp_next = curtab; } /* Need to redraw the tabline. Tab page contents doesn't change. */ @@ -3518,18 +3560,24 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri curwin->w_cursor.coladd = 0; changed_line_abv_curs(); /* assume cursor position needs updating */ - if (curwin->w_localdir != NULL) { - /* Window has a local directory: Save current directory as global - * directory (unless that was done already) and change to the local - * directory. */ + // The new directory is either the local directory of the window, of the tab + // or NULL. + char_u *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->localdir; + + if (new_dir) { + // Window/tab has a local directory: Save current directory as global + // directory (unless that was done already) and change to the local + // directory. if (globaldir == NULL) { char_u cwd[MAXPATHL]; - if (os_dirname(cwd, MAXPATHL) == OK) + if (os_dirname(cwd, MAXPATHL) == OK) { globaldir = vim_strsave(cwd); + } + } + if (os_chdir((char *)new_dir) == 0) { + shorten_fnames(true); } - if (os_chdir((char *)curwin->w_localdir) == 0) - shorten_fnames(TRUE); } else if (globaldir != NULL) { /* Window doesn't have a local directory and we are not in the global * directory: Change to the global directory. */ @@ -4538,10 +4586,19 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } assert(fr); - if (room < offset) /* Not enough room */ - offset = room; /* Move as far as we can */ - if (offset <= 0) /* No room at all, quit. */ + // Not enough room + if (room < offset) { + offset = room; // Move as far as we can + } + + // No room at all, quit. + if (offset <= 0) { return; + } + + if (fr == NULL) { + return; // Safety check, should not happen. + } /* grow frame fr by offset lines */ frame_new_width(fr, fr->fr_width + offset, left, FALSE); @@ -4573,10 +4630,8 @@ void win_drag_vsep_line(win_T *dragwin, int offset) #define FRACTION_MULT 16384L -/* - * Set wp->w_fraction for the current w_wrow and w_height. - */ -static void set_fraction(win_T *wp) +// Set wp->w_fraction for the current w_wrow and w_height. +void set_fraction(win_T *wp) { wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2) / (long)wp->w_height; @@ -4837,17 +4892,15 @@ static void frame_add_height(frame_T *frp, int n) */ char_u *grab_file_name(long count, linenr_T *file_lnum) { + int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC; if (VIsual_active) { size_t len; char_u *ptr; if (get_visual_text(NULL, &ptr, &len) == FAIL) return NULL; - return find_file_name_in_path(ptr, len, - FNAME_MESS|FNAME_EXP|FNAME_REL, - count, curbuf->b_ffname); + return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname); } - return file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP|FNAME_REL, count, - file_lnum); + return file_name_at_cursor(options | FNAME_HYP, count, file_lnum); } /* @@ -4918,12 +4971,18 @@ file_name_in_line ( * Also allow "://" when ':' is not in 'isfname'. */ len = 0; - while (vim_isfilec(ptr[len]) - || ((options & FNAME_HYP) && path_is_url((char *)ptr + len))) - if (has_mbyte) + while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') + || ((options & FNAME_HYP) && path_is_url((char *)ptr + len))) { + if (ptr[len] == '\\' && ptr[len + 1] == ' ') { + // Skip over the "\" in "\ ". + ++len; + } + if (has_mbyte) { len += (size_t)(*mb_ptr2len)(ptr + len); - else + } else { ++len; + } + } /* * If there is trailing punctuation, remove it. @@ -5048,27 +5107,22 @@ int min_rows(void) return total; } -/* - * Return TRUE if there is only one window (in the current tab page), not - * counting a help or preview window, unless it is the current window. - * Does not count "aucmd_win". - */ -int only_one_window(void) +/// Check that there is only one window (and only one tab page), not counting a +/// help or preview window, unless it is the current window. Does not count +/// "aucmd_win". +bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int count = 0; - - /* If there is another tab page there always is another window. */ - if (first_tabpage->tp_next != NULL) - return FALSE; + // If there is another tab page there always is another window. + if (first_tabpage->tp_next != NULL) { + return false; + } + int count = 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer != NULL && (!((wp->w_buffer->b_help && !curbuf->b_help) - || wp->w_p_pvw - ) || wp == curwin) - && wp != aucmd_win - ) { - ++count; + || wp->w_p_pvw) || wp == curwin) && wp != aucmd_win) { + count++; } } return count <= 1; @@ -5306,14 +5360,14 @@ void restore_buffer(buf_T *save_curbuf) } -/* - * Add match to the match list of window 'wp'. The pattern 'pat' will be - * highlighted with the group 'grp' with priority 'prio'. - * Optionally, a desired ID 'id' can be specified (greater than or equal to 1). - * If no particular ID is desired, -1 must be specified for 'id'. - * Return ID of added match, -1 on failure. - */ -int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id, list_T *pos_list) +// Add match to the match list of window 'wp'. The pattern 'pat' will be +// highlighted with the group 'grp' with priority 'prio'. +// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). +// If no particular ID is desired, -1 must be specified for 'id'. +// Return ID of added match, -1 on failure. +int match_add(win_T *wp, char_u *grp, char_u *pat, + int prio, int id, list_T *pos_list, + char_u *conceal_char) { matchitem_T *cur; matchitem_T *prev; @@ -5369,6 +5423,10 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id, list_T *pos m->match.regprog = regprog; m->match.rmm_ic = FALSE; m->match.rmm_maxcol = 0; + m->conceal_char = 0; + if (conceal_char != NULL) { + m->conceal_char = (*mb_ptr2char)(conceal_char); + } // Set up position matches if (pos_list != NULL) @@ -5567,38 +5625,42 @@ matchitem_T *get_match(win_T *wp, int id) } -/* - * Return TRUE if "topfrp" and its children are at the right height. - */ -static int frame_check_height(frame_T *topfrp, int height) +/// Check that "topfrp" and its children are at the right height. +/// +/// @param topfrp top frame pointer +/// @param height expected height +static bool frame_check_height(frame_T *topfrp, int height) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - frame_T *frp; - - if (topfrp->fr_height != height) - return FALSE; - - if (topfrp->fr_layout == FR_ROW) - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) - if (frp->fr_height != height) - return FALSE; - - return TRUE; + if (topfrp->fr_height != height) { + return false; + } + if (topfrp->fr_layout == FR_ROW) { + for (frame_T *frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + if (frp->fr_height != height) { + return false; + } + } + } + return true; } -/* - * Return TRUE if "topfrp" and its children are at the right width. - */ -static int frame_check_width(frame_T *topfrp, int width) +/// Check that "topfrp" and its children are at the right width. +/// +/// @param topfrp top frame pointer +/// @param width expected width +static bool frame_check_width(frame_T *topfrp, int width) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - frame_T *frp; - - if (topfrp->fr_width != width) - return FALSE; - - if (topfrp->fr_layout == FR_COL) - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) - if (frp->fr_width != width) - return FALSE; - - return TRUE; + if (topfrp->fr_width != width) { + return false; + } + if (topfrp->fr_layout == FR_COL) { + for (frame_T *frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) { + if (frp->fr_width != width) { + return false; + } + } + } + return true; } diff --git a/src/nvim/window.h b/src/nvim/window.h index eccc7835a8..2ac4c00c28 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -10,6 +10,7 @@ #define FNAME_INCL 8 /* apply 'includeexpr' */ #define FNAME_REL 16 /* ".." and "./" are relative to the (current) file instead of the current directory */ +#define FNAME_UNESC 32 // remove backslashes used for escaping /* * arguments for win_split() diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index c924988d06..0eefa25a13 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1,8 +1,9 @@ -- Sanity checks for buffer_* API calls via msgpack-rpc local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curwin, eq, ok = - helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, - helpers.eq, helpers.ok +local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer +local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq +local curbufmeths, ok = helpers.curbufmeths, helpers.ok +local funcs = helpers.funcs describe('buffer_* functions', function() before_each(clear) @@ -35,10 +36,11 @@ describe('buffer_* functions', function() eq('', curbuf('get_line', 0)) end) - it('get_line: out-of-bounds returns empty string', function() + it('get_line: out-of-bounds is an error', function() curbuf('set_line', 0, 'line1.a') - eq('', curbuf('get_line', 1)) - eq('', curbuf('get_line', -2)) + eq(1, curbuf('line_count')) -- sanity + eq(false, pcall(curbuf, 'get_line', 1)) + eq(false, pcall(curbuf, 'get_line', -2)) end) it('set_line, del_line: out-of-bounds is an error', function() @@ -68,14 +70,16 @@ describe('buffer_* functions', function() eq({}, curbuf('get_line_slice', -4, -5, true, true)) end) - it('set_line_slice: out-of-bounds is an error', function() + it('set_line_slice: out-of-bounds extends past end', function() curbuf('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 2, true, true)) --sanity eq({'c'}, curbuf('get_line_slice', -1, 4, true, true)) eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 5, true, true)) - eq(false, pcall(curbuf, 'set_line_slice', 4, 5, true, true, {'d'})) - eq(false, pcall(curbuf, 'set_line_slice', -4, -5, true, true, {'d'})) + curbuf('set_line_slice', 4, 5, true, true, {'d'}) + eq({'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) + curbuf('set_line_slice', -4, -5, true, true, {'e'}) + eq({'e', 'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) end) it('works', function() @@ -101,11 +105,144 @@ describe('buffer_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set}_lines', function() + local get_lines, set_lines = curbufmeths.get_lines, curbufmeths.set_lines + local line_count = curbufmeths.line_count + + it('has correct line_count when inserting and deleting', function() + eq(1, line_count()) + set_lines(-1, -1, true, {'line'}) + eq(2, line_count()) + set_lines(-1, -1, true, {'line'}) + eq(3, line_count()) + set_lines(-2, -1, true, {}) + eq(2, line_count()) + set_lines(-2, -1, true, {}) + set_lines(-2, -1, true, {}) + -- There's always at least one line + eq(1, line_count()) + end) + + it('can get, set and delete a single line', function() + eq({''}, get_lines(0, 1, true)) + set_lines(0, 1, true, {'line1'}) + eq({'line1'}, get_lines(0, 1, true)) + set_lines(0, 1, true, {'line2'}) + eq({'line2'}, get_lines(0, 1, true)) + set_lines(0, 1, true, {}) + eq({''}, get_lines(0, 1, true)) + end) + + it('can get a single line with strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(1, line_count()) -- sanity + eq(false, pcall(get_lines, 1, 2, true)) + eq(false, pcall(get_lines, -3, -2, true)) + end) + + it('can get a single line with non-strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(1, line_count()) -- sanity + eq({}, get_lines(1, 2, false)) + eq({}, get_lines(-3, -2, false)) + end) + + it('can set and delete a single line with strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(false, pcall(set_lines, 1, 2, true, {'line1.b'})) + eq(false, pcall(set_lines, -3, -2, true, {'line1.c'})) + eq({'line1.a'}, get_lines(0, -1, true)) + eq(false, pcall(set_lines, 1, 2, true, {})) + eq(false, pcall(set_lines, -3, -2, true, {})) + eq({'line1.a'}, get_lines(0, -1, true)) + end) + + it('can set and delete a single line with non-strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + set_lines(1, 2, false, {'line1.b'}) + set_lines(-4, -3, false, {'line1.c'}) + eq({'line1.c', 'line1.a', 'line1.b'}, get_lines(0, -1, true)) + set_lines(3, 4, false, {}) + set_lines(-5, -4, false, {}) + eq({'line1.c', 'line1.a', 'line1.b'}, get_lines(0, -1, true)) + end) + + it('can handle NULs', function() + set_lines(0, 1, true, {'ab\0cd'}) + eq({'ab\0cd'}, get_lines(0, -1, true)) + end) + + it('works with multiple lines', function() + eq({''}, get_lines(0, -1, true)) + -- Replace buffer + for _, mode in pairs({false, true}) do + set_lines(0, -1, mode, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, mode)) + eq({'b', 'c'}, get_lines(1, -1, mode)) + eq({'b'}, get_lines(1, 2, mode)) + eq({}, get_lines(1, 1, mode)) + eq({'a', 'b'}, get_lines(0, -2, mode)) + eq({'b'}, get_lines(1, -2, mode)) + eq({'b', 'c'}, get_lines(-3, -1, mode)) + set_lines(1, 2, mode, {'a', 'b', 'c'}) + eq({'a', 'a', 'b', 'c', 'c'}, get_lines(0, -1, mode)) + set_lines(-2, -1, mode, {'a', 'b', 'c'}) + eq({'a', 'a', 'b', 'c', 'a', 'b', 'c'}, + get_lines(0, -1, mode)) + set_lines(0, -4, mode, {}) + eq({'a', 'b', 'c'}, get_lines(0, -1, mode)) + set_lines(0, -1, mode, {}) + eq({''}, get_lines(0, -1, mode)) + end + end) + + it('can get line ranges with non-strict indexing', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq({}, get_lines(3, 4, false)) + eq({}, get_lines(3, 10, false)) + eq({}, get_lines(-5, -5, false)) + eq({}, get_lines(3, -1, false)) + eq({}, get_lines(-3, -4, false)) + end) + + it('can get line ranges with strict indexing', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq(false, pcall(get_lines, 3, 4, true)) + eq(false, pcall(get_lines, 3, 10, true)) + eq(false, pcall(get_lines, -5, -5, true)) + -- empty or inverted ranges are not errors + eq({}, get_lines(3, -1, true)) + eq({}, get_lines(-3, -4, true)) + end) + + it('set_line_slice: out-of-bounds can extend past end', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq({'c'}, get_lines(-2, 5, false)) + eq({'a', 'b', 'c'}, get_lines(0, 6, false)) + eq(false, pcall(set_lines, 4, 6, true, {'d'})) + set_lines(4, 6, false, {'d'}) + eq({'a', 'b', 'c', 'd'}, get_lines(0, -1, true)) + eq(false, pcall(set_lines, -6, -6, true, {'e'})) + set_lines(-6, -6, false, {'e'}) + eq({'e', 'a', 'b', 'c', 'd'}, get_lines(0, -1, true)) + end) + + end) + + describe('{get,set,del}_var', function() it('works', function() curbuf('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, curbuf('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 'b:lua')) + eq(1, funcs.exists('b:lua')) + curbufmeths.del_var('lua') + eq(0, funcs.exists('b:lua')) end) end) diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index a3ac864f79..1b33275803 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -32,6 +32,26 @@ describe('server -> client', function() end) end) + describe('empty string handling in arrays', function() + -- Because the msgpack encoding for an empty string was interpreted as an + -- error, msgpack arrays with an empty string looked like + -- [..., '', 0, ..., 0] after the conversion, regardless of the array + -- elements following the empty string. + it('works', function() + local function on_setup() + eq({1, 2, '', 3, 'asdf'}, eval('rpcrequest('..cid..', "nstring")')) + stop() + end + + local function on_request() + -- No need to evaluate the args, we are only interested in + -- a response that contains an array with an empty string. + return {1, 2, '', 3, 'asdf'} + end + run(on_request, nil, on_setup) + end) + end) + describe('recursive call', function() it('works', function() local function on_setup() @@ -145,8 +165,8 @@ describe('server -> client', function() eq('SOME TEXT', eval("rpcrequest(vim, 'buffer_get_line', "..buf..", 0)")) - -- Call get_line_slice(buf, range [0,0], includes start, includes end) - eq({'SOME TEXT'}, eval("rpcrequest(vim, 'buffer_get_line_slice', "..buf..", 0, 0, 1, 1)")) + -- Call get_lines(buf, range [0,0], strict_indexing) + eq({'SOME TEXT'}, eval("rpcrequest(vim, 'buffer_get_lines', "..buf..", 0, 1, 1)")) end) it('returns an error if the request failed', function() diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua index 9937e0c72e..c782107714 100644 --- a/test/functional/api/tabpage_spec.lua +++ b/test/functional/api/tabpage_spec.lua @@ -3,6 +3,8 @@ local helpers = require('test.functional.helpers') local clear, nvim, tabpage, curtab, eq, ok = helpers.clear, helpers.nvim, helpers.tabpage, helpers.curtab, helpers.eq, helpers.ok +local curtabmeths = helpers.curtabmeths +local funcs = helpers.funcs describe('tabpage_* functions', function() before_each(clear) @@ -21,11 +23,14 @@ describe('tabpage_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set,del}_var', function() it('works', function() curtab('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, curtab('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 't:lua')) + eq(1, funcs.exists('t:lua')) + curtabmeths.del_var('lua') + eq(0, funcs.exists('t:lua')) end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index cba0b7533b..20de6d0072 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,9 +1,12 @@ -- Sanity checks for vim_* API calls via msgpack-rpc local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') +local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed - +local os_name = helpers.os_name +local meths = helpers.meths +local funcs = helpers.funcs describe('vim_* functions', function() before_each(clear) @@ -17,7 +20,11 @@ describe('vim_* functions', function() nvim('command', 'w') local f = io.open(fname) ok(f ~= nil) - eq('testing\napi\n', f:read('*a')) + if os_name() == 'windows' then + eq('testing\r\napi\r\n', f:read('*a')) + else + eq('testing\napi\n', f:read('*a')) + end f:close() os.remove(fname) end) @@ -66,20 +73,31 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set,del}_var', function() it('works', function() nvim('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 'g:lua')) + eq(1, funcs.exists('g:lua')) + meths.del_var('lua') + eq(0, funcs.exists('g:lua')) end) it('set_var returns the old value', function() local val1 = {1, 2, {['3'] = 1}} local val2 = {4, 7} - eq(nil, nvim('set_var', 'lua', val1)) + eq(NIL, nvim('set_var', 'lua', val1)) eq(val1, nvim('set_var', 'lua', val2)) end) + it('del_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, meths.set_var('lua', val1)) + eq(val1, meths.set_var('lua', val2)) + eq(val2, meths.del_var('lua')) + end) + it('truncates values with NULs in them', function() nvim('set_var', 'xxx', 'ab\0cd') eq('ab', nvim('get_var', 'xxx')) @@ -140,15 +158,23 @@ describe('vim_* functions', function() describe('replace_termcodes', function() it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function() - eq(helpers.nvim('replace_termcodes', '\128', true, true, true), '\128\254X') + eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true)) end) - it('leaves non K_SPECIAL string unchanged', function() - eq(helpers.nvim('replace_termcodes', 'abc', true, true, true), 'abc') + it('leaves non-K_SPECIAL string unchanged', function() + eq('abc', helpers.nvim('replace_termcodes', 'abc', true, true, true)) end) it('converts <expressions>', function() - eq(helpers.nvim('replace_termcodes', '<Leader>', true, true, true), '\\') + eq('\\', helpers.nvim('replace_termcodes', '<Leader>', true, true, true)) + end) + + it('converts <LeftMouse> to K_SPECIAL KS_EXTRA KE_LEFTMOUSE', function() + -- K_SPECIAL KS_EXTRA KE_LEFTMOUSE + -- 0x80 0xfd 0x2c + -- 128 253 44 + eq('\128\253\44', helpers.nvim('replace_termcodes', + '<LeftMouse>', true, true, true)) end) end) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 17aacafe9b..92a33b4cdb 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -5,6 +5,8 @@ local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval local wait = helpers.wait +local curwinmeths = helpers.curwinmeths +local funcs = helpers.funcs -- check if str is visible at the beginning of some line local function is_visible(str) @@ -126,11 +128,14 @@ describe('window_* functions', function() end) end) - describe('{get,set}_var', function() + describe('{get,set,del}_var', function() it('works', function() curwin('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, curwin('get_var', 'lua')) eq({1, 2, {['3'] = 1}}, nvim('eval', 'w:lua')) + eq(1, funcs.exists('w:lua')) + curwinmeths.del_var('lua') + eq(0, funcs.exists('w:lua')) end) end) diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua new file mode 100644 index 0000000000..3c813abc2e --- /dev/null +++ b/test/functional/autocmd/autocmd_spec.lua @@ -0,0 +1,31 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local command = helpers.command +local eval = helpers.eval + +describe('autocmds:', function() + before_each(clear) + + it(':tabnew triggers events in the correct order', function() + local expected = { + 'WinLeave', + 'TabLeave', + 'TabNew', + 'WinEnter', + 'TabEnter', + 'BufLeave', + 'BufEnter' + } + command('let g:foo = []') + command('autocmd BufEnter * :call add(g:foo, "BufEnter")') + command('autocmd BufLeave * :call add(g:foo, "BufLeave")') + command('autocmd TabEnter * :call add(g:foo, "TabEnter")') + command('autocmd TabLeave * :call add(g:foo, "TabLeave")') + command('autocmd TabNew * :call add(g:foo, "TabNew")') + command('autocmd WinEnter * :call add(g:foo, "WinEnter")') + command('autocmd WinLeave * :call add(g:foo, "WinLeave")') + command('tabnew') + assert.same(expected, eval('g:foo')) + end) +end) diff --git a/test/functional/autocmd/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua index 5ab504889b..aaf9db0a99 100644 --- a/test/functional/autocmd/tabnew_spec.lua +++ b/test/functional/autocmd/tabnew_spec.lua @@ -1,22 +1,28 @@ local helpers = require('test.functional.helpers') -local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq -describe('TabNew', function() - setup(clear) - describe('au TabNew', function() - describe('with * as <afile>', function() - it('matches when opening any new tab', function() - nvim('command', 'au! TabNew * echom "tabnew:".tabpagenr().":".bufnr("")') - eq("\ntabnew:2:1", nvim('command_output', 'tabnew')) - eq("\ntabnew:3:2\n\"test.x\" [New File]", nvim('command_output', 'tabnew test.x')) - end) - end) - describe('with FILE as <afile>', function() - it('matches when opening a new tab for FILE', function() - local tmp_path = nvim('eval', 'tempname()') - nvim('command', 'au! TabNew '..tmp_path..' echom "tabnew:match"') - eq("\ntabnew:4:3\ntabnew:match\n\""..tmp_path.."\" [New File]", nvim('command_output', 'tabnew '..tmp_path)) - end) - end) - end) +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval + +describe('autocmd TabNew', function() + before_each(clear) + + it('matches when opening any new tab', function() + command('autocmd! TabNew * let g:test = "tabnew:".tabpagenr().":".bufnr("")') + command('tabnew') + eq('tabnew:2:1', eval('g:test')) + command('tabnew test.x') + eq('tabnew:3:2', eval('g:test')) + end) + + it('matches when opening a new tab for FILE', function() + local tmp_path = helpers.funcs.tempname() + command('let g:test = "foo"') + command('autocmd! TabNew ' .. tmp_path .. ' let g:test = "bar"') + command('tabnew ' .. tmp_path ..'X') + eq('foo', eval('g:test')) + command('tabnew ' .. tmp_path) + eq('bar', eval('g:test')) + end) end) diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua index 0961340e61..4de3f039c1 100644 --- a/test/functional/autocmd/termclose_spec.lua +++ b/test/functional/autocmd/termclose_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, execute, feed, nvim, nvim_dir = helpers.clear, helpers.execute, helpers.feed, helpers.nvim, helpers.nvim_dir +local eval, eq = helpers.eval, helpers.eq describe('TermClose event', function() local screen @@ -25,4 +26,19 @@ describe('TermClose event', function() TermClose works! | ]]) end) + + it('reports the correct <abuf>', function() + execute('set hidden') + execute('autocmd TermClose * let g:abuf = expand("<abuf>")') + execute('edit foo') + execute('edit bar') + eq(2, eval('bufnr("%")')) + execute('terminal') + feed('<c-\\><c-n>') + eq(3, eval('bufnr("%")')) + execute('buffer 1') + eq(1, eval('bufnr("%")')) + execute('3bdelete!') + eq('3', eval('g:abuf')) + end) end) diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua new file mode 100644 index 0000000000..c26ceeaedc --- /dev/null +++ b/test/functional/autocmd/textyankpost_spec.lua @@ -0,0 +1,216 @@ +local helpers = require('test.functional.helpers') +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local feed, execute, expect, command = helpers.feed, helpers.execute, helpers.expect, helpers.command +local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq + +describe('TextYankPost', function() + before_each(function() + clear() + + -- emulate the clipboard so system clipboard isn't affected + execute('let &rtp = "test/functional/fixtures,".&rtp') + + execute('let g:count = 0') + execute('autocmd TextYankPost * let g:event = copy(v:event)') + execute('autocmd TextYankPost * let g:count += 1') + + curbufmeths.set_lines(0, -1, true, { + 'foo\0bar', + 'baz text', + }) + end) + + it('is executed after yank and handles register types', function() + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + + -- v:event is cleared after the autocommand is done + eq({}, eval('v:event')) + + feed('+yw') + eq({ + operator = 'y', + regcontents = { 'baz ' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(2, eval('g:count')) + + feed('<c-v>eky') + eq({ + operator = 'y', + regcontents = { 'foo', 'baz' }, + regname = '', + regtype = "\0223" -- ^V + block width + }, eval('g:event')) + eq(3, eval('g:count')) + end) + + it('makes v:event immutable', function() + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + + execute('set debug=msg') + -- the regcontents should not be changed without copy. + local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])') + eq(status,false) + neq(nil, string.find(err, ':E742:')) + + -- can't mutate keys inside the autocommand + execute('autocmd! TextYankPost * let v:event.regcontents = 0') + status, err = pcall(command,'normal yy') + eq(status,false) + neq(nil, string.find(err, ':E46:')) + + -- can't add keys inside the autocommand + execute('autocmd! TextYankPost * let v:event.mykey = 0') + status, err = pcall(command,'normal yy') + eq(status,false) + neq(nil, string.find(err, ':E742:')) + end) + + it('is not invoked recursively', function() + execute('autocmd TextYankPost * normal "+yy') + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + eq({ 'foo\nbar' }, funcs.getreg('+',1,1)) + end) + + it('is executed after delete and change', function() + feed('dw') + eq({ + operator = 'd', + regcontents = { 'foo' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(1, eval('g:count')) + + feed('dd') + eq({ + operator = 'd', + regcontents = { '\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(2, eval('g:count')) + + feed('cwspam<esc>') + eq({ + operator = 'c', + regcontents = { 'baz' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(3, eval('g:count')) + end) + + it('is not executed after black-hole operation', function() + feed('"_dd') + eq(0, eval('g:count')) + + feed('"_cwgood<esc>') + eq(0, eval('g:count')) + + expect([[ + good text]]) + feed('"_yy') + eq(0, eval('g:count')) + + execute('delete _') + eq(0, eval('g:count')) + end) + + it('gives the correct register name', function() + feed('$"byiw') + eq({ + operator = 'y', + regcontents = { 'bar' }, + regname = 'b', + regtype = 'v' + }, eval('g:event')) + + feed('"*yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '*', + regtype = 'V' + }, eval('g:event')) + + execute("set clipboard=unnamed") + + -- regname still shows the name the user requested + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + + feed('"*yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '*', + regtype = 'V' + }, eval('g:event')) + end) + + it('works with Ex commands', function() + execute('1delete +') + eq({ + operator = 'd', + regcontents = { 'foo\nbar' }, + regname = '+', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + + execute('yank') + eq({ + operator = 'y', + regcontents = { 'baz text' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(2, eval('g:count')) + + execute('normal yw') + eq({ + operator = 'y', + regcontents = { 'baz ' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(3, eval('g:count')) + + execute('normal! dd') + eq({ + operator = 'd', + regcontents = { 'baz text' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(4, eval('g:count')) + end) + +end) diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/eval/json_functions_spec.lua new file mode 100644 index 0000000000..b32688a9d2 --- /dev/null +++ b/test/functional/eval/json_functions_spec.lua @@ -0,0 +1,800 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local funcs = helpers.funcs +local meths = helpers.meths +local eq = helpers.eq +local eval = helpers.eval +local execute = helpers.execute +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec +local NIL = helpers.NIL + +describe('json_decode() function', function() + local restart = function(cmd) + clear(cmd) + execute('language C') + execute([[ + function Eq(exp, act) + let act = a:act + let exp = a:exp + if type(exp) != type(act) + return 0 + endif + if type(exp) == type({}) + if sort(keys(exp)) !=# sort(keys(act)) + return 0 + endif + if sort(keys(exp)) ==# ['_TYPE', '_VAL'] + let exp_typ = v:msgpack_types[exp._TYPE] + let act_typ = act._TYPE + if exp_typ isnot act_typ + return 0 + endif + return Eq(exp._VAL, act._VAL) + else + return empty(filter(copy(exp), '!Eq(v:val, act[v:key])')) + endif + else + if type(exp) == type([]) + if len(exp) != len(act) + return 0 + endif + return empty(filter(copy(exp), '!Eq(v:val, act[v:key])')) + endif + return exp ==# act + endif + return 1 + endfunction + ]]) + execute([[ + function EvalEq(exp, act_expr) + let act = eval(a:act_expr) + if Eq(a:exp, act) + return 1 + else + return string(act) + endif + endfunction + ]]) + end + before_each(restart) + + local speq = function(expected, actual_expr) + eq(1, funcs.EvalEq(expected, actual_expr)) + end + + it('accepts readfile()-style list', function() + eq({Test=1}, funcs.json_decode({ + '{', + '\t"Test": 1', + '}', + })) + end) + + it('accepts strings with newlines', function() + eq({Test=1}, funcs.json_decode([[ + { + "Test": 1 + } + ]])) + end) + + it('parses null, true, false', function() + eq(NIL, funcs.json_decode('null')) + eq(true, funcs.json_decode('true')) + eq(false, funcs.json_decode('false')) + end) + + it('fails to parse incomplete null, true, false', function() + eq('Vim(call):E474: Expected null: n', + exc_exec('call json_decode("n")')) + eq('Vim(call):E474: Expected null: nu', + exc_exec('call json_decode("nu")')) + eq('Vim(call):E474: Expected null: nul', + exc_exec('call json_decode("nul")')) + eq('Vim(call):E474: Expected null: nul\n\t', + exc_exec('call json_decode("nul\\n\\t")')) + + eq('Vim(call):E474: Expected true: t', + exc_exec('call json_decode("t")')) + eq('Vim(call):E474: Expected true: tr', + exc_exec('call json_decode("tr")')) + eq('Vim(call):E474: Expected true: tru', + exc_exec('call json_decode("tru")')) + eq('Vim(call):E474: Expected true: tru\t\n', + exc_exec('call json_decode("tru\\t\\n")')) + + eq('Vim(call):E474: Expected false: f', + exc_exec('call json_decode("f")')) + eq('Vim(call):E474: Expected false: fa', + exc_exec('call json_decode("fa")')) + eq('Vim(call):E474: Expected false: fal', + exc_exec('call json_decode("fal")')) + eq('Vim(call):E474: Expected false: fal <', + exc_exec('call json_decode(" fal <")')) + eq('Vim(call):E474: Expected false: fals', + exc_exec('call json_decode("fals")')) + end) + + it('parses integer numbers', function() + eq(100000, funcs.json_decode('100000')) + eq(-100000, funcs.json_decode('-100000')) + eq(100000, funcs.json_decode(' 100000 ')) + eq(-100000, funcs.json_decode(' -100000 ')) + eq(0, funcs.json_decode('0')) + eq(0, funcs.json_decode('-0')) + end) + + it('fails to parse +numbers and .number', function() + eq('Vim(call):E474: Unidentified byte: +1000', + exc_exec('call json_decode("+1000")')) + eq('Vim(call):E474: Unidentified byte: .1000', + exc_exec('call json_decode(".1000")')) + end) + + it('fails to parse numbers with leading zeroes', function() + eq('Vim(call):E474: Leading zeroes are not allowed: 00.1', + exc_exec('call json_decode("00.1")')) + eq('Vim(call):E474: Leading zeroes are not allowed: 01', + exc_exec('call json_decode("01")')) + eq('Vim(call):E474: Leading zeroes are not allowed: -01', + exc_exec('call json_decode("-01")')) + eq('Vim(call):E474: Leading zeroes are not allowed: -001.0', + exc_exec('call json_decode("-001.0")')) + end) + + it('fails to parse incomplete numbers', function() + eq('Vim(call):E474: Missing number after minus sign: -.1', + exc_exec('call json_decode("-.1")')) + eq('Vim(call):E474: Missing number after minus sign: -', + exc_exec('call json_decode("-")')) + eq('Vim(call):E474: Missing number after decimal dot: -1.', + exc_exec('call json_decode("-1.")')) + eq('Vim(call):E474: Missing number after decimal dot: 0.', + exc_exec('call json_decode("0.")')) + eq('Vim(call):E474: Missing exponent: 0.0e', + exc_exec('call json_decode("0.0e")')) + eq('Vim(call):E474: Missing exponent: 0.0e+', + exc_exec('call json_decode("0.0e+")')) + eq('Vim(call):E474: Missing exponent: 0.0e-', + exc_exec('call json_decode("0.0e-")')) + eq('Vim(call):E474: Missing exponent: 0.0e-', + exc_exec('call json_decode("0.0e-")')) + eq('Vim(call):E474: Missing number after decimal dot: 1.e5', + exc_exec('call json_decode("1.e5")')) + eq('Vim(call):E474: Missing number after decimal dot: 1.e+5', + exc_exec('call json_decode("1.e+5")')) + eq('Vim(call):E474: Missing number after decimal dot: 1.e+', + exc_exec('call json_decode("1.e+")')) + end) + + it('parses floating-point numbers', function() + eq('100000.0', eval('string(json_decode("100000.0"))')) + eq(100000.5, funcs.json_decode('100000.5')) + eq(-100000.5, funcs.json_decode('-100000.5')) + eq(-100000.5e50, funcs.json_decode('-100000.5e50')) + eq(100000.5e50, funcs.json_decode('100000.5e50')) + eq(100000.5e50, funcs.json_decode('100000.5e+50')) + eq(-100000.5e-50, funcs.json_decode('-100000.5e-50')) + eq(100000.5e-50, funcs.json_decode('100000.5e-50')) + eq(100000e-50, funcs.json_decode('100000e-50')) + eq(0.5, funcs.json_decode('0.5')) + eq(0.005, funcs.json_decode('0.005')) + eq(0.005, funcs.json_decode('0.00500')) + eq(0.5, funcs.json_decode('0.00500e+002')) + eq(0.00005, funcs.json_decode('0.00500e-002')) + + eq(-0.0, funcs.json_decode('-0.0')) + eq(-0.0, funcs.json_decode('-0.0e0')) + eq(-0.0, funcs.json_decode('-0.0e+0')) + eq(-0.0, funcs.json_decode('-0.0e-0')) + eq(-0.0, funcs.json_decode('-0e-0')) + eq(-0.0, funcs.json_decode('-0e-2')) + eq(-0.0, funcs.json_decode('-0e+2')) + + eq(0.0, funcs.json_decode('0.0')) + eq(0.0, funcs.json_decode('0.0e0')) + eq(0.0, funcs.json_decode('0.0e+0')) + eq(0.0, funcs.json_decode('0.0e-0')) + eq(0.0, funcs.json_decode('0e-0')) + eq(0.0, funcs.json_decode('0e-2')) + eq(0.0, funcs.json_decode('0e+2')) + end) + + it('fails to parse numbers with spaces inside', function() + eq('Vim(call):E474: Missing number after minus sign: - 1000', + exc_exec('call json_decode("- 1000")')) + eq('Vim(call):E474: Missing number after decimal dot: 0. ', + exc_exec('call json_decode("0. ")')) + eq('Vim(call):E474: Missing number after decimal dot: 0. 0', + exc_exec('call json_decode("0. 0")')) + eq('Vim(call):E474: Missing exponent: 0.0e 1', + exc_exec('call json_decode("0.0e 1")')) + eq('Vim(call):E474: Missing exponent: 0.0e+ 1', + exc_exec('call json_decode("0.0e+ 1")')) + eq('Vim(call):E474: Missing exponent: 0.0e- 1', + exc_exec('call json_decode("0.0e- 1")')) + end) + + it('fails to parse "," and ":"', function() + eq('Vim(call):E474: Comma not inside container: , ', + exc_exec('call json_decode(" , ")')) + eq('Vim(call):E474: Colon not inside container: : ', + exc_exec('call json_decode(" : ")')) + end) + + it('parses empty containers', function() + eq({}, funcs.json_decode('[]')) + eq('[]', eval('string(json_decode("[]"))')) + end) + + it('fails to parse "[" and "{"', function() + eq('Vim(call):E474: Unexpected end of input: {', + exc_exec('call json_decode("{")')) + eq('Vim(call):E474: Unexpected end of input: [', + exc_exec('call json_decode("[")')) + end) + + it('fails to parse "}" and "]"', function() + eq('Vim(call):E474: No container to close: ]', + exc_exec('call json_decode("]")')) + eq('Vim(call):E474: No container to close: }', + exc_exec('call json_decode("}")')) + end) + + it('fails to parse containers which are closed by different brackets', + function() + eq('Vim(call):E474: Closing dictionary with square bracket: ]', + exc_exec('call json_decode("{]")')) + eq('Vim(call):E474: Closing list with curly bracket: }', + exc_exec('call json_decode("[}")')) + end) + + it('fails to parse concat inside container', function() + eq('Vim(call):E474: Expected comma before list item: []]', + exc_exec('call json_decode("[[][]]")')) + eq('Vim(call):E474: Expected comma before list item: {}]', + exc_exec('call json_decode("[{}{}]")')) + eq('Vim(call):E474: Expected comma before list item: ]', + exc_exec('call json_decode("[1 2]")')) + eq('Vim(call):E474: Expected comma before dictionary key: ": 4}', + exc_exec('call json_decode("{\\"1\\": 2 \\"3\\": 4}")')) + eq('Vim(call):E474: Expected colon before dictionary value: , "3" 4}', + exc_exec('call json_decode("{\\"1\\" 2, \\"3\\" 4}")')) + end) + + it('fails to parse containers with leading comma or colon', function() + eq('Vim(call):E474: Leading comma: ,}', + exc_exec('call json_decode("{,}")')) + eq('Vim(call):E474: Leading comma: ,]', + exc_exec('call json_decode("[,]")')) + eq('Vim(call):E474: Using colon not in dictionary: :]', + exc_exec('call json_decode("[:]")')) + eq('Vim(call):E474: Unexpected colon: :}', + exc_exec('call json_decode("{:}")')) + end) + + it('fails to parse containers with trailing comma', function() + eq('Vim(call):E474: Trailing comma: ]', + exc_exec('call json_decode("[1,]")')) + eq('Vim(call):E474: Trailing comma: }', + exc_exec('call json_decode("{\\"1\\": 2,}")')) + end) + + it('fails to parse dictionaries with missing value', function() + eq('Vim(call):E474: Expected value after colon: }', + exc_exec('call json_decode("{\\"1\\":}")')) + eq('Vim(call):E474: Expected value: }', + exc_exec('call json_decode("{\\"1\\"}")')) + end) + + it('fails to parse containers with two commas or colons', function() + eq('Vim(call):E474: Duplicate comma: , "2": 2}', + exc_exec('call json_decode("{\\"1\\": 1,, \\"2\\": 2}")')) + eq('Vim(call):E474: Duplicate comma: , "2", 2]', + exc_exec('call json_decode("[\\"1\\", 1,, \\"2\\", 2]")')) + eq('Vim(call):E474: Duplicate colon: : 2}', + exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":: 2}")')) + eq('Vim(call):E474: Comma after colon: , 2}', + exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":, 2}")')) + eq('Vim(call):E474: Unexpected colon: : "2": 2}', + exc_exec('call json_decode("{\\"1\\": 1,: \\"2\\": 2}")')) + eq('Vim(call):E474: Unexpected colon: :, "2": 2}', + exc_exec('call json_decode("{\\"1\\": 1:, \\"2\\": 2}")')) + end) + + it('fails to parse concat of two values', function() + eq('Vim(call):E474: Trailing characters: []', + exc_exec('call json_decode("{}[]")')) + end) + + it('parses containers', function() + eq({1}, funcs.json_decode('[1]')) + eq({NIL, 1}, funcs.json_decode('[null, 1]')) + eq({['1']=2}, funcs.json_decode('{"1": 2}')) + eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, + funcs.json_decode('{"1": 2, "3": [{"4": {"5": [[], 1]}}]}')) + end) + + it('fails to parse incomplete strings', function() + eq('Vim(call):E474: Expected string end: \t"', + exc_exec('call json_decode("\\t\\"")')) + eq('Vim(call):E474: Expected string end: \t"abc', + exc_exec('call json_decode("\\t\\"abc")')) + eq('Vim(call):E474: Unfinished escape sequence: \t"abc\\', + exc_exec('call json_decode("\\t\\"abc\\\\")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u', + exc_exec('call json_decode("\\t\\"abc\\\\u")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u0', + exc_exec('call json_decode("\\t\\"abc\\\\u0")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u00', + exc_exec('call json_decode("\\t\\"abc\\\\u00")')) + eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000', + exc_exec('call json_decode("\\t\\"abc\\\\u000")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u" ', + exc_exec('call json_decode("\\t\\"abc\\\\u\\" ")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u0" ', + exc_exec('call json_decode("\\t\\"abc\\\\u0\\" ")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u00" ', + exc_exec('call json_decode("\\t\\"abc\\\\u00\\" ")')) + eq('Vim(call):E474: Expected four hex digits after \\u: \\u000" ', + exc_exec('call json_decode("\\t\\"abc\\\\u000\\" ")')) + eq('Vim(call):E474: Expected string end: \t"abc\\u0000', + exc_exec('call json_decode("\\t\\"abc\\\\u0000")')) + end) + + it('fails to parse unknown escape sequnces', function() + eq('Vim(call):E474: Unknown escape sequence: \\a"', + exc_exec('call json_decode("\\t\\"\\\\a\\"")')) + end) + + it('parses strings properly', function() + eq('\n', funcs.json_decode('"\\n"')) + eq('', funcs.json_decode('""')) + eq('\\/"\t\b\n\r\f', funcs.json_decode([["\\\/\"\t\b\n\r\f"]])) + eq('/a', funcs.json_decode([["\/a"]])) + -- Unicode characters: 2-byte, 3-byte, 4-byte + eq({ + '«', + 'ફ', + '\240\144\128\128', + }, funcs.json_decode({ + '[', + '"«",', + '"ફ",', + '"\240\144\128\128"', + ']', + })) + end) + + it('fails on strings with invalid bytes', function() + eq('Vim(call):E474: Only UTF-8 strings allowed: \255"', + exc_exec('call json_decode("\\t\\"\\xFF\\"")')) + eq('Vim(call):E474: ASCII control characters cannot be present inside string: ', + exc_exec('call json_decode(["\\"\\n\\""])')) + -- 0xC2 starts 2-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \194"', + exc_exec('call json_decode("\\t\\"\\xC2\\"")')) + -- 0xE0 0xAA starts 3-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \224"', + exc_exec('call json_decode("\\t\\"\\xE0\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \224\170"', + exc_exec('call json_decode("\\t\\"\\xE0\\xAA\\"")')) + -- 0xF0 0x90 0x80 starts 4-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \240"', + exc_exec('call json_decode("\\t\\"\\xF0\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144"', + exc_exec('call json_decode("\\t\\"\\xF0\\x90\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144\128"', + exc_exec('call json_decode("\\t\\"\\xF0\\x90\\x80\\"")')) + -- 0xF9 0x80 0x80 0x80 starts 5-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \249"', + exc_exec('call json_decode("\\t\\"\\xF9\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\"")')) + -- 0xFC 0x90 0x80 0x80 0x80 starts 6-byte unicode character + eq('Vim(call):E474: Only UTF-8 strings allowed: \252"', + exc_exec('call json_decode("\\t\\"\\xFC\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\"")')) + -- Specification does not allow unquoted characters above 0x10FFFF + eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \249\128\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\x80\\"")')) + eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"', + exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\x80\\"")')) + -- '"\249\128\128\128\128"', + -- '"\252\144\128\128\128\128"', + end) + + it('parses surrogate pairs properly', function() + eq('\240\144\128\128', funcs.json_decode('"\\uD800\\uDC00"')) + eq('\237\160\128a\237\176\128', funcs.json_decode('"\\uD800a\\uDC00"')) + eq('\237\160\128\t\237\176\128', funcs.json_decode('"\\uD800\\t\\uDC00"')) + + eq('\237\160\128', funcs.json_decode('"\\uD800"')) + eq('\237\160\128a', funcs.json_decode('"\\uD800a"')) + eq('\237\160\128\t', funcs.json_decode('"\\uD800\\t"')) + + eq('\237\176\128', funcs.json_decode('"\\uDC00"')) + eq('\237\176\128a', funcs.json_decode('"\\uDC00a"')) + eq('\237\176\128\t', funcs.json_decode('"\\uDC00\\t"')) + + eq('\237\176\128', funcs.json_decode('"\\uDC00"')) + eq('a\237\176\128', funcs.json_decode('"a\\uDC00"')) + eq('\t\237\176\128', funcs.json_decode('"\\t\\uDC00"')) + + eq('\237\160\128¬', funcs.json_decode('"\\uD800\\u00AC"')) + + eq('\237\160\128\237\160\128', funcs.json_decode('"\\uD800\\uD800"')) + end) + + local sp_decode_eq = function(expected, json) + meths.set_var('__json', json) + speq(expected, 'json_decode(g:__json)') + execute('unlet! g:__json') + end + + it('parses strings with NUL properly', function() + sp_decode_eq({_TYPE='string', _VAL={'\n'}}, '"\\u0000"') + sp_decode_eq({_TYPE='string', _VAL={'\n', '\n'}}, '"\\u0000\\n\\u0000"') + sp_decode_eq({_TYPE='string', _VAL={'\n«\n'}}, '"\\u0000\\u00AB\\u0000"') + end) + + it('parses dictionaries with duplicate keys to special maps', function() + sp_decode_eq({_TYPE='map', _VAL={{'a', 1}, {'a', 2}}}, + '{"a": 1, "a": 2}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'a', 2}}}, + '{"b": 3, "a": 1, "a": 2}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}}}, + '{"b": 3, "a": 1, "c": 4, "a": 2}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}, + '{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}') + sp_decode_eq({{_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}, + '[{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}]') + sp_decode_eq({{d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, + '[{"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') + sp_decode_eq({1, {d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, + '[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') + sp_decode_eq({1, {a={}, d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, + '[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') + end) + + it('parses dictionaries with empty keys to special maps', function() + sp_decode_eq({_TYPE='map', _VAL={{'', 4}}}, + '{"": 4}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, + '{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}') + sp_decode_eq({_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, + '{"": 3, "a": 1, "c": 4, "d": 2, "": 4}') + sp_decode_eq({{_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}}, + '[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]') + end) + + it('parses dictionaries with keys with NUL bytes to special maps', function() + sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b'}}, 4}}}, + '{"a\\u0000\\nb": 4}') + sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b', ''}}, 4}}}, + '{"a\\u0000\\nb\\n": 4}') + sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {{_TYPE='string', _VAL={'\n'}}, 4}}}, + '{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}') + end) + + it('converts strings to latin1 when &encoding is latin1', function() + restart('set encoding=latin1') + eq('\171', funcs.json_decode('"\\u00AB"')) + sp_decode_eq({_TYPE='string', _VAL={'\n\171\n'}}, '"\\u0000\\u00AB\\u0000"') + end) + + it('fails to convert string to latin1 if it is impossible', function() + restart('set encoding=latin1') + eq('Vim(call):E474: Failed to convert string "ê¯" from UTF-8', + exc_exec('call json_decode(\'"\\uABCD"\')')) + end) + + it('parses U+00C3 correctly', function() + eq('\195\131', funcs.json_decode('"\195\131"')) + end) + + it('fails to parse empty string', function() + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode("")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode([])')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode([""])')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode(" ")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode("\\t")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode("\\n")')) + eq('Vim(call):E474: Attempt to decode a blank string', + exc_exec('call json_decode(" \\t\\n \\n\\t\\t \\n\\t\\n \\n \\t\\n\\t ")')) + end) + + it('accepts all spaces in every position where space may be put', function() + local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t' + local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s) + eq({key={'val', 'val2'}, key2=1}, funcs.json_decode(str)) + end) + + it('always treats input as UTF-8', function() + -- When &encoding is latin1 string "«" is U+00C2 U+00AB U+00C2: «Â. So if + -- '"«"' was parsed as latin1 json_decode would return three characters, and + -- only one U+00AB when this string is parsed as latin1. + restart('set encoding=latin1') + eq(('%c'):format(0xAB), funcs.json_decode('"«"')) + end) + + it('does not overflow when writing error message about decoding ["", ""]', + function() + eq('\nE474: Attempt to decode a blank string' + .. '\nE474: Failed to parse \n', + redir_exec('call json_decode(["", ""])')) + end) +end) + +describe('json_encode() function', function() + before_each(function() + clear() + execute('language C') + end) + + it('dumps strings', function() + eq('"Test"', funcs.json_encode('Test')) + eq('""', funcs.json_encode('')) + eq('"\\t"', funcs.json_encode('\t')) + eq('"\\n"', funcs.json_encode('\n')) + eq('"\\u001B"', funcs.json_encode('\27')) + eq('"þÿþ"', funcs.json_encode('þÿþ')) + end) + + it('dumps numbers', function() + eq('0', funcs.json_encode(0)) + eq('10', funcs.json_encode(10)) + eq('-10', funcs.json_encode(-10)) + end) + + it('dumps floats', function() + eq('0.0', eval('json_encode(0.0)')) + eq('10.5', funcs.json_encode(10.5)) + eq('-10.5', funcs.json_encode(-10.5)) + eq('-1.0e-5', funcs.json_encode(-1e-5)) + eq('1.0e50', eval('json_encode(1.0e50)')) + end) + + it('fails to dump NaN and infinite values', function() + eq('Vim(call):E474: Unable to represent NaN value in JSON', + exc_exec('call json_encode(str2float("nan"))')) + eq('Vim(call):E474: Unable to represent infinity in JSON', + exc_exec('call json_encode(str2float("inf"))')) + eq('Vim(call):E474: Unable to represent infinity in JSON', + exc_exec('call json_encode(-str2float("inf"))')) + end) + + it('dumps lists', function() + eq('[]', funcs.json_encode({})) + eq('[[]]', funcs.json_encode({{}})) + eq('[[], []]', funcs.json_encode({{}, {}})) + end) + + it('dumps dictionaries', function() + eq('{}', eval('json_encode({})')) + eq('{"d": []}', funcs.json_encode({d={}})) + eq('{"d": [], "e": []}', funcs.json_encode({d={}, e={}})) + end) + + it('cannot dump generic mapping with generic mapping keys and values', + function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('call add(todump._VAL, [todumpv1, todumpv2])') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with ext key', function() + execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with array key', function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with UINT64_MAX key', function() + execute('let todump = {"_TYPE": v:msgpack_types.integer}') + execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('cannot dump generic mapping with floating-point key', function() + execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) + end) + + it('can dump generic mapping with STR special key and NUL', function() + execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('{"\\u0000": 1}', eval('json_encode(todump)')) + end) + + it('can dump generic mapping with BIN special key and NUL', function() + execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') + eq('{"\\u0000": 1}', eval('json_encode(todump)')) + end) + + it('can dump STR special mapping with NUL and NL', function() + execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}') + eq('"\\u0000\\n"', eval('json_encode(todump)')) + end) + + it('can dump BIN special mapping with NUL and NL', function() + execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}') + eq('"\\u0000\\n"', eval('json_encode(todump)')) + end) + + it('cannot dump special ext mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') + eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)')) + end) + + it('can dump special array mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') + eq('[5, [""]]', eval('json_encode(todump)')) + end) + + it('can dump special UINT64_MAX mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.integer}') + execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') + eq('18446744073709551615', eval('json_encode(todump)')) + end) + + it('can dump special INT64_MIN mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.integer}') + execute('let todump._VAL = [-1, 2, 0, 0]') + eq('-9223372036854775808', eval('json_encode(todump)')) + end) + + it('can dump special BOOLEAN true mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') + eq('true', eval('json_encode(todump)')) + end) + + it('can dump special BOOLEAN false mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') + eq('false', eval('json_encode(todump)')) + end) + + it('can dump special NIL mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') + eq('null', eval('json_encode(todump)')) + end) + + it('fails to dump a function reference', function() + eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference', + exc_exec('call json_encode(function("tr"))')) + end) + + it('fails to dump a function reference in a list', function() + eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference', + exc_exec('call json_encode([function("tr")])')) + end) + + it('fails to dump a recursive list', function() + execute('let todump = [[[]]]') + execute('call add(todump[0][0], todump)') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails to dump a recursive dict', function() + execute('let todump = {"d": {"d": {}}}') + execute('call extend(todump.d.d, {"d": todump})') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode([todump])')) + end) + + it('can dump dict with two same dicts inside', function() + execute('let inter = {}') + execute('let todump = {"a": inter, "b": inter}') + eq('{"a": {}, "b": {}}', eval('json_encode(todump)')) + end) + + it('can dump list with two same lists inside', function() + execute('let inter = []') + execute('let todump = [inter, inter]') + eq('[[], []]', eval('json_encode(todump)')) + end) + + it('fails to dump a recursive list in a special dict', function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('call add(todump._VAL, todump)') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails to dump a recursive (val) map in a special dict', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') + execute('call add(todump._VAL, ["", todump])') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode([todump])')) + end) + + it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}') + execute('call add(todump._VAL[0][1], todump._VAL)') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails to dump a recursive (val) special list in a special dict', + function() + execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('call add(todump._VAL, ["", todump._VAL])') + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call json_encode(todump)')) + end) + + it('fails when called with no arguments', function() + eq('Vim(call):E119: Not enough arguments for function: json_encode', + exc_exec('call json_encode()')) + end) + + it('fails when called with two arguments', function() + eq('Vim(call):E118: Too many arguments for function: json_encode', + exc_exec('call json_encode(["", ""], 1)')) + end) + + it('converts strings from latin1 when &encoding is latin1', function() + clear('set encoding=latin1') + eq('"\\u00AB"', funcs.json_encode('\171')) + eq('"\\u0000\\u00AB\\u0000"', eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\\n\171\\n"]})')) + end) + + it('ignores improper values in &isprint', function() + meths.set_option('isprint', '1') + eq(1, eval('"\1" =~# "\\\\p"')) + eq('"\\u0001"', funcs.json_encode('\1')) + end) + + it('fails when using surrogate character in a UTF-8 string', function() + eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\160\128', + exc_exec('call json_encode("\237\160\128")')) + eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\175\191', + exc_exec('call json_encode("\237\175\191")')) + end) + + it('dumps control characters as expected', function() + eq([["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013"]], + eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\n\1\2\3\4\5\6\7\8\9", "\11\12\13\14\15\16\17\18\19"]})')) + end) + + it('can dump NULL string', function() + eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)')) + end) + + it('can dump NULL list', function() + eq('[]', eval('json_encode(v:_null_list)')) + end) + + it('can dump NULL dictionary', function() + eq('{}', eval('json_encode(v:_null_dict)')) + end) +end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index da42fc9d26..9e501353a5 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers') local clear = helpers.clear +local funcs = helpers.funcs local eval, eq = helpers.eval, helpers.eq local execute = helpers.execute local nvim = helpers.nvim @@ -382,30 +383,32 @@ describe('msgpack*() functions', function() eq({"\n"}, eval('parsed')) eq(1, eval('dumped ==# dumped2')) end) + + it('dump and restore special mapping with floating-point value', function() + execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') + eq({0.125}, eval('msgpackparse(msgpackdump([todump]))')) + end) end) describe('msgpackparse() function', function() before_each(clear) - it('restores nil as special dict', function() + it('restores nil as v:null', function() execute('let dumped = ["\\xC0"]') execute('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL=0}}, eval('parsed')) - eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.nil')) + eq('[v:null]', eval('string(parsed)')) end) - it('restores boolean false as zero', function() + it('restores boolean false as v:false', function() execute('let dumped = ["\\xC2"]') execute('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL=0}}, eval('parsed')) - eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean')) + eq({false}, eval('parsed')) end) - it('restores boolean true as one', function() + it('restores boolean true as v:true', function() execute('let dumped = ["\\xC3"]') execute('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL=1}}, eval('parsed')) - eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean')) + eq({true}, eval('parsed')) end) it('restores FIXSTR as special dict', function() @@ -512,56 +515,78 @@ describe('msgpackdump() function', function() eq({'\129\128\128'}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with ext', function() + it('can dump v:true', function() + eq({'\195'}, funcs.msgpackdump({true})) + end) + + it('can dump v:false', function() + eq({'\194'}, funcs.msgpackdump({false})) + end) + + it('can v:null', function() + execute('let todump = v:null') + end) + + it('can dump special bool mapping (true)', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') + eq({'\195'}, eval('msgpackdump([todump])')) + end) + + it('can dump special bool mapping (false)', function() + execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') + eq({'\194'}, eval('msgpackdump([todump])')) + end) + + it('can dump special nil mapping', function() + execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') + eq({'\192'}, eval('msgpackdump([todump])')) + end) + + it('can dump special ext mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') eq({'\212\005', ''}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with array', function() + it('can dump special array mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with UINT64_MAX', function() + it('can dump special UINT64_MAX mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.integer}') execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])')) end) - it('can dump generic mapping with INT64_MIN', function() + it('can dump special INT64_MIN mapping', function() execute('let todump = {"_TYPE": v:msgpack_types.integer}') execute('let todump._VAL = [-1, 2, 0, 0]') eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])')) end) - it('dump and restore generic mapping with floating-point value', function() - execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') - eq({0.125}, eval('msgpackparse(msgpackdump([todump]))')) - end) - it('fails to dump a function reference', function() execute('let Todump = function("tr")') - eq('Vim(call):E475: Invalid argument: attempt to dump function reference', + eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', exc_exec('call msgpackdump([Todump])')) end) it('fails to dump a function reference in a list', function() execute('let todump = [function("tr")]') - eq('Vim(call):E475: Invalid argument: attempt to dump function reference', + eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive list', function() execute('let todump = [[[]]]') execute('call add(todump[0][0], todump)') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive dict', function() execute('let todump = {"d": {"d": {}}}') execute('call extend(todump.d.d, {"d": todump})') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'', exc_exec('call msgpackdump([todump])')) end) @@ -580,21 +605,35 @@ describe('msgpackdump() function', function() it('fails to dump a recursive list in a special dict', function() execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') execute('call add(todump._VAL, todump)') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (key) map in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') execute('call add(todump._VAL, [todump, 0])') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 1', exc_exec('call msgpackdump([todump])')) end) it('fails to dump a recursive (val) map in a special dict', function() - execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') execute('call add(todump._VAL, [0, todump])') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map', + exc_exec('call msgpackdump([todump])')) + end) + + it('fails to dump a recursive (key) map in a special dict, _VAL reference', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') + execute('call add(todump._VAL[0][0], todump._VAL)') + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0', + exc_exec('call msgpackdump([todump])')) + end) + + it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() + execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') + execute('call add(todump._VAL[0][1], todump._VAL)') + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0', exc_exec('call msgpackdump([todump])')) end) @@ -602,7 +641,7 @@ describe('msgpackdump() function', function() function() execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') execute('call add(todump._VAL, [0, todump._VAL])') - eq('Vim(call):E475: Invalid argument: container references itself', + eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1', exc_exec('call msgpackdump([todump])')) end) @@ -640,4 +679,25 @@ describe('msgpackdump() function', function() eq('Vim(call):E686: Argument of msgpackdump() must be a List', exc_exec('call msgpackdump(0.0)')) end) + + it('fails to dump special value', function() + for _, val in ipairs({'v:true', 'v:false', 'v:null'}) do + eq('Vim(call):E686: Argument of msgpackdump() must be a List', + exc_exec('call msgpackdump(' .. val .. ')')) + end + end) + + it('can dump NULL string', function() + eq({'\196\n'}, eval('msgpackdump([$XXX_UNEXISTENT_VAR_XXX])')) + eq({'\196\n'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) + eq({'\160'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) + end) + + it('can dump NULL list', function() + eq({'\144'}, eval('msgpackdump([v:_null_list])')) + end) + + it('can dump NULL dictionary', function() + eq({'\128'}, eval('msgpackdump([v:_null_dict])')) + end) end) diff --git a/test/functional/eval/operators_spec.lua b/test/functional/eval/operators_spec.lua new file mode 100644 index 0000000000..bc9a17935c --- /dev/null +++ b/test/functional/eval/operators_spec.lua @@ -0,0 +1,28 @@ +local helpers = require('test.functional.helpers') +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear + +describe('Division operator', function() + before_each(clear) + + it('returns infinity on {positive}/0.0', function() + eq('str2float(\'inf\')', eval('string(1.0/0.0)')) + eq('str2float(\'inf\')', eval('string(1.0e-100/0.0)')) + eq('str2float(\'inf\')', eval('string(1.0e+100/0.0)')) + eq('str2float(\'inf\')', eval('string((1.0/0.0)/0.0)')) + end) + + it('returns -infinity on {negative}/0.0', function() + eq('-str2float(\'inf\')', eval('string((-1.0)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0e-100)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0e+100)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0/0.0)/0.0)')) + end) + + it('returns NaN on 0.0/0.0', function() + eq('str2float(\'nan\')', eval('string(0.0/0.0)')) + eq('str2float(\'nan\')', eval('string(-(0.0/0.0))')) + eq('str2float(\'nan\')', eval('string((-0.0)/0.0)')) + end) +end) diff --git a/test/functional/eval/printf_spec.lua b/test/functional/eval/printf_spec.lua new file mode 100644 index 0000000000..6180f4156a --- /dev/null +++ b/test/functional/eval/printf_spec.lua @@ -0,0 +1,60 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local funcs = helpers.funcs +local exc_exec = helpers.exc_exec + +describe('printf()', function() + before_each(clear) + + it('works with zero and %b', function() + eq('0', funcs.printf('%lb', 0)) + eq('0', funcs.printf('%llb', 0)) + eq('0', funcs.printf('%zb', 0)) + end) + it('works with one and %b', function() + eq('1', funcs.printf('%b', 1)) + eq('1', funcs.printf('%lb', 1)) + eq('1', funcs.printf('%llb', 1)) + eq('1', funcs.printf('%zb', 1)) + end) + it('works with 0xff and %b', function() + eq('11111111', funcs.printf('%b', 0xff)) + eq('11111111', funcs.printf('%lb', 0xff)) + eq('11111111', funcs.printf('%llb', 0xff)) + eq('11111111', funcs.printf('%zb', 0xff)) + end) + it('accepts width modifier with %b', function() + eq(' 1', funcs.printf('%3b', 1)) + end) + it('accepts prefix modifier with %b', function() + eq('0b1', funcs.printf('%#b', 1)) + end) + it('writes capital B with %B', function() + eq('0B1', funcs.printf('%#B', 1)) + end) + it('accepts prefix, zero-fill and width modifiers with %b', function() + eq('0b001', funcs.printf('%#05b', 1)) + end) + it('accepts prefix and width modifiers with %b', function() + eq(' 0b1', funcs.printf('%#5b', 1)) + end) + it('does not write prefix for zero with prefix and width modifier used with %b', function() + eq(' 0', funcs.printf('%#5b', 0)) + end) + it('accepts precision modifier with %b', function() + eq('00000', funcs.printf('%.5b', 0)) + end) + it('accepts all modifiers with %b at once', function() + -- zero-fill modifier is ignored when used with left-align + -- force-sign and add-blank are ignored + -- use-grouping-characters modifier is ignored always + eq('0b00011 ', funcs.printf('% \'+#0-10.5b', 3)) + end) + it('errors out when %b modifier is used for a list', function() + eq('Vim(call):E745: Using a List as a Number', exc_exec('call printf("%b", [])')) + end) + it('errors out when %b modifier is used for a float', function() + eq('Vim(call):E805: Using a Float as a Number', exc_exec('call printf("%b", 3.1415926535)')) + end) +end) diff --git a/test/functional/eval/reltime_spec.lua b/test/functional/eval/reltime_spec.lua new file mode 100644 index 0000000000..da55a3fac3 --- /dev/null +++ b/test/functional/eval/reltime_spec.lua @@ -0,0 +1,36 @@ +local helpers = require('test.functional.helpers') +local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok +local neq, execute, funcs = helpers.neq, helpers.execute, helpers.funcs +local reltime, reltimestr, reltimefloat = funcs.reltime, funcs.reltimestr, funcs.reltimefloat + +describe('reltimestr(), reltimefloat()', function() + before_each(clear) + + it('Checks', function() + local now = reltime() + execute('sleep 10m') + local later = reltime() + local elapsed = reltime(now) + + neq(reltimestr(elapsed), '0.0') + ok(reltimefloat(elapsed) > 0.0) + -- original vim test for < 0.1, but easily fails on travis + ok(nil ~= string.match(reltimestr(elapsed), "0%.")) + ok(reltimefloat(elapsed) < 1.0) + + local same = reltime(now, now) + local samestr = string.gsub(reltimestr(same), ' ', '') + samestr = string.sub(samestr, 1, 5) + + eq('0.000', samestr) + eq(0.0, reltimefloat(same)) + + local differs = reltime(now, later) + neq(reltimestr(differs), '0.0') + ok(reltimefloat(differs) > 0.0) + -- original vim test for < 0.1, but easily fails on travis + ok(nil ~= string.match(reltimestr(differs), "0%.")) + ok(reltimefloat(differs) < 1.0) + + end) +end) diff --git a/test/functional/server/server_spec.lua b/test/functional/eval/server_spec.lua index d9ce96057e..7f53522c08 100644 --- a/test/functional/server/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -1,7 +1,8 @@ local helpers = require('test.functional.helpers') local nvim, eq, neq, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.eval -local clear = helpers.clear +local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths +local os_name = helpers.os_name describe('serverstart(), serverstop()', function() before_each(clear) @@ -17,29 +18,32 @@ describe('serverstart(), serverstop()', function() eq('', eval('$NVIM_LISTEN_ADDRESS')) end) - it([[sets v:servername _only_ on nvim startup - (unless all servers are stopped)]], function() - local initial_server = eval('v:servername') + it('sets v:servername _only_ on nvim startup unless all servers are stopped', + function() + local initial_server = meths.get_vvar('servername') assert(initial_server ~= nil and initial_server:len() > 0, - "v:servername was not initialized") + 'v:servername was not initialized') -- v:servername is readonly so we cannot unset it--but we can test that it -- does not get set again thereafter. - local s = eval('serverstart()') + local s = funcs.serverstart() assert(s ~= nil and s:len() > 0, "serverstart() returned empty") neq(initial_server, s) -- serverstop() does _not_ modify v:servername... - nvim('command', "call serverstop('"..s.."')") - eq(initial_server, eval('v:servername')) + funcs.serverstop(s) + eq(initial_server, meths.get_vvar('servername')) -- ...unless we stop _all_ servers. - nvim('command', "call serverstop(serverlist()[0])") - eq('', eval('v:servername')) + funcs.serverstop(funcs.serverlist()[1]) + eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. - nvim('command', "call serverstart('test_server_socket')") - eq('test_server_socket', eval('v:servername')) + local servername = (os_name() == 'windows' + and [[\\.\pipe\Xtest-functional-server-server-pipe]] + or 'Xtest-functional-server-server-socket') + funcs.serverstart(servername) + eq(servername, meths.get_vvar('servername')) end) it('serverstop() ignores invalid input', function() diff --git a/test/functional/eval/special_vars_spec.lua b/test/functional/eval/special_vars_spec.lua new file mode 100644 index 0000000000..2526483830 --- /dev/null +++ b/test/functional/eval/special_vars_spec.lua @@ -0,0 +1,171 @@ +local helpers = require('test.functional.helpers') +local exc_exec = helpers.exc_exec +local execute = helpers.execute +local funcs = helpers.funcs +local clear = helpers.clear +local eval = helpers.eval +local eq = helpers.eq +local meths = helpers.meths +local NIL = helpers.NIL + +describe('Special values', function() + before_each(clear) + + it('do not cause error when freed', function() + execute([[ + function Test() + try + return v:true + finally + return 'something else' + endtry + endfunction + ]]) + eq(0, exc_exec('call Test()')) + end) + + it('work with empty()', function() + eq(0, funcs.empty(true)) + eq(1, funcs.empty(false)) + eq(1, funcs.empty(NIL)) + end) + + it('can be stringified and eval’ed back', function() + eq(true, funcs.eval(funcs.string(true))) + eq(false, funcs.eval(funcs.string(false))) + eq(NIL, funcs.eval(funcs.string(NIL))) + end) + + it('work with is/isnot properly', function() + eq(1, eval('v:null is v:null')) + eq(0, eval('v:null is v:true')) + eq(0, eval('v:null is v:false')) + eq(1, eval('v:true is v:true')) + eq(0, eval('v:true is v:false')) + eq(1, eval('v:false is v:false')) + + eq(0, eval('v:null is 0')) + eq(0, eval('v:true is 0')) + eq(0, eval('v:false is 0')) + + eq(0, eval('v:null is 1')) + eq(0, eval('v:true is 1')) + eq(0, eval('v:false is 1')) + + eq(0, eval('v:null is ""')) + eq(0, eval('v:true is ""')) + eq(0, eval('v:false is ""')) + + eq(0, eval('v:null is "null"')) + eq(0, eval('v:true is "true"')) + eq(0, eval('v:false is "false"')) + + eq(0, eval('v:null is []')) + eq(0, eval('v:true is []')) + eq(0, eval('v:false is []')) + + eq(0, eval('v:null isnot v:null')) + eq(1, eval('v:null isnot v:true')) + eq(1, eval('v:null isnot v:false')) + eq(0, eval('v:true isnot v:true')) + eq(1, eval('v:true isnot v:false')) + eq(0, eval('v:false isnot v:false')) + + eq(1, eval('v:null isnot 0')) + eq(1, eval('v:true isnot 0')) + eq(1, eval('v:false isnot 0')) + + eq(1, eval('v:null isnot 1')) + eq(1, eval('v:true isnot 1')) + eq(1, eval('v:false isnot 1')) + + eq(1, eval('v:null isnot ""')) + eq(1, eval('v:true isnot ""')) + eq(1, eval('v:false isnot ""')) + + eq(1, eval('v:null isnot "null"')) + eq(1, eval('v:true isnot "true"')) + eq(1, eval('v:false isnot "false"')) + + eq(1, eval('v:null isnot []')) + eq(1, eval('v:true isnot []')) + eq(1, eval('v:false isnot []')) + end) + + it('work with +/-/* properly', function() + eq(1, eval('0 + v:true')) + eq(0, eval('0 + v:null')) + eq(0, eval('0 + v:false')) + + eq(-1, eval('0 - v:true')) + eq( 0, eval('0 - v:null')) + eq( 0, eval('0 - v:false')) + + eq(1, eval('1 * v:true')) + eq(0, eval('1 * v:null')) + eq(0, eval('1 * v:false')) + end) + + it('does not work with +=/-=/.=', function() + meths.set_var('true', true) + meths.set_var('false', false) + execute('let null = v:null') + + eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let true += 1')) + eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let false += 1')) + eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let null += 1')) + + eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let true -= 1')) + eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let false -= 1')) + eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let null -= 1')) + + eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let true .= 1')) + eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let false .= 1')) + eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let null .= 1')) + end) + + it('work with . (concat) properly', function() + eq("true", eval('"" . v:true')) + eq("null", eval('"" . v:null')) + eq("false", eval('"" . v:false')) + end) + + it('work with type()', function() + eq(6, funcs.type(true)) + eq(6, funcs.type(false)) + eq(7, funcs.type(NIL)) + end) + + it('work with copy() and deepcopy()', function() + eq(true, funcs.deepcopy(true)) + eq(false, funcs.deepcopy(false)) + eq(NIL, funcs.deepcopy(NIL)) + + eq(true, funcs.copy(true)) + eq(false, funcs.copy(false)) + eq(NIL, funcs.copy(NIL)) + end) + + it('fails in index', function() + eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:true[0]')) + eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:false[0]')) + eq('Vim(echo):E909: Cannot index a special variable', exc_exec('echo v:null[0]')) + end) + + it('is accepted by assert_true and assert_false', function() + funcs.assert_false(false) + funcs.assert_false(true) + funcs.assert_false(NIL) + + funcs.assert_true(false) + funcs.assert_true(true) + funcs.assert_true(NIL) + + eq({ + 'Expected False but got v:true', + 'Expected False but got v:null', + 'Expected True but got v:false', + 'Expected True but got v:null', + }, meths.get_vvar('errors')) + end) +end) diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua new file mode 100644 index 0000000000..abda2c59cb --- /dev/null +++ b/test/functional/eval/string_spec.lua @@ -0,0 +1,197 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local command = helpers.command +local meths = helpers.meths +local eval = helpers.eval +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec +local funcs = helpers.funcs +local write_file = helpers.write_file +local NIL = helpers.NIL + +describe('string() function', function() + before_each(clear) + + describe('used to represent floating-point values', function() + it('dumps NaN values', function() + eq('str2float(\'nan\')', eval('string(str2float(\'nan\'))')) + end) + + it('dumps infinite values', function() + eq('str2float(\'inf\')', eval('string(str2float(\'inf\'))')) + eq('-str2float(\'inf\')', eval('string(str2float(\'-inf\'))')) + end) + + it('dumps regular values', function() + eq('1.5', funcs.string(1.5)) + eq('1.56e-20', funcs.string(1.56000e-020)) + eq('0.0', eval('string(0.0)')) + end) + + it('dumps special v: values', function() + eq('v:true', eval('string(v:true)')) + eq('v:false', eval('string(v:false)')) + eq('v:null', eval('string(v:null)')) + eq('v:true', funcs.string(true)) + eq('v:false', funcs.string(false)) + eq('v:null', funcs.string(NIL)) + end) + + it('dumps values with at most six digits after the decimal point', + function() + eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020)) + eq('1.234568', funcs.string(1.23456789123456789123456789)) + end) + + it('dumps values with at most seven digits before the decimal point', + function() + eq('1234567.891235', funcs.string(1234567.89123456789123456789)) + eq('1.234568e7', funcs.string(12345678.9123456789123456789)) + end) + + it('dumps negative values', function() + eq('-1.5', funcs.string(-1.5)) + eq('-1.56e-20', funcs.string(-1.56000e-020)) + eq('-1.234568e-20', funcs.string(-1.23456789123456789123456789e-020)) + eq('-1.234568', funcs.string(-1.23456789123456789123456789)) + eq('-1234567.891235', funcs.string(-1234567.89123456789123456789)) + eq('-1.234568e7', funcs.string(-12345678.9123456789123456789)) + end) + end) + + describe('used to represent numbers', function() + it('dumps regular values', function() + eq('0', funcs.string(0)) + eq('-1', funcs.string(-1)) + eq('1', funcs.string(1)) + end) + + it('dumps large values', function() + eq('2147483647', funcs.string(2^31-1)) + eq('-2147483648', funcs.string(-2^31)) + end) + end) + + describe('used to represent strings', function() + it('dumps regular strings', function() + eq('\'test\'', funcs.string('test')) + end) + + it('dumps empty strings', function() + eq('\'\'', funcs.string('')) + end) + + it('dumps strings with \' inside', function() + eq('\'\'\'\'\'\'\'\'', funcs.string('\'\'\'')) + eq('\'a\'\'b\'\'\'\'\'', funcs.string('a\'b\'\'')) + eq('\'\'\'b\'\'\'\'d\'', funcs.string('\'b\'\'d')) + eq('\'a\'\'b\'\'c\'\'d\'', funcs.string('a\'b\'c\'d')) + end) + + it('dumps NULL strings', function() + eq('\'\'', eval('string($XXX_UNEXISTENT_VAR_XXX)')) + end) + + it('dumps NULL lists', function() + eq('[]', eval('string(v:_null_list)')) + end) + + it('dumps NULL dictionaries', function() + eq('{}', eval('string(v:_null_dict)')) + end) + end) + + describe('used to represent funcrefs', function() + local fname = 'Xtest-functional-eval-string_spec-fref-script.vim' + + before_each(function() + write_file(fname, [[ + function Test1() + endfunction + + function s:Test2() + endfunction + + function g:Test3() + endfunction + + let g:Test2_f = function('s:Test2') + ]]) + command('source ' .. fname) + end) + + after_each(function() + os.remove(fname) + end) + + it('dumps references to built-in functions', function() + eq('function(\'function\')', eval('string(function("function"))')) + end) + + it('dumps references to user functions', function() + eq('function(\'Test1\')', eval('string(function("Test1"))')) + eq('function(\'g:Test3\')', eval('string(function("g:Test3"))')) + end) + + it('dumps references to script functions', function() + eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)')) + end) + end) + + describe('used to represent lists', function() + it('dumps empty list', function() + eq('[]', funcs.string({})) + end) + + it('dumps nested lists', function() + eq('[[[[[]]]]]', funcs.string({{{{{}}}}})) + end) + + it('dumps nested non-empty lists', function() + eq('[1, [[3, [[5], 4]], 2]]', funcs.string({1, {{3, {{5}, 4}}, 2}})) + end) + + it('errors when dumping recursive lists', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(l)')) + end) + + it('dumps recursive lists despite the error', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('\nE724: unable to correctly dump variable with self-referencing container\n[{E724@0}]', + redir_exec('echo string(l)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n[[{E724@1}]]', + redir_exec('echo string([l])')) + end) + end) + + describe('used to represent dictionaries', function() + it('dumps empty dictionary', function() + eq('{}', eval('string({})')) + end) + + it('dumps non-empty dictionary', function() + eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1})) + end) + + it('errors when dumping recursive dictionaries', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(d)')) + end) + + it('dumps recursive dictionaries despite the error', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'d\': {E724@0}}', + redir_exec('echo string(d)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'out\': {\'d\': {E724@1}}}', + redir_exec('echo string({"out": d})')) + end) + end) +end) diff --git a/test/functional/eval/vvar_event_spec.lua b/test/functional/eval/vvar_event_spec.lua new file mode 100644 index 0000000000..bbac86524f --- /dev/null +++ b/test/functional/eval/vvar_event_spec.lua @@ -0,0 +1,15 @@ +local helpers = require('test.functional.helpers') +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local command = helpers.command +describe('v:event', function() + before_each(clear) + it('is empty before any autocommand', function() + eq({}, eval('v:event')) + end) + + it('is immutable', function() + eq(false, pcall(command, 'let v:event = {}')) + eq(false, pcall(command, 'let v:event.mykey = {}')) + end) +end) + diff --git a/test/functional/ex_cmds/append_spec.lua b/test/functional/ex_cmds/append_spec.lua new file mode 100644 index 0000000000..2d5ab8e8c8 --- /dev/null +++ b/test/functional/ex_cmds/append_spec.lua @@ -0,0 +1,54 @@ +local helpers = require('test.functional.helpers') + +local eq = helpers.eq +local feed = helpers.feed +local clear = helpers.clear +local funcs = helpers.funcs +local command = helpers.command +local curbufmeths = helpers.curbufmeths + +before_each(function() + clear() + curbufmeths.set_lines(0, 1, true, { 'foo', 'bar', 'baz' }) +end) + +local buffer_contents = function() + return curbufmeths.get_lines(0, -1, false) +end + +local cmdtest = function(cmd, prep, ret1) + describe(':' .. cmd, function() + it(cmd .. 's' .. prep .. ' the current line by default', function() + command(cmd .. '\nabc\ndef\n') + eq(ret1, buffer_contents()) + end) + -- Used to crash because this invokes history processing which uses + -- hist_char2type which after fdb68e35e4c729c7ed097d8ade1da29e5b3f4b31 + -- crashed. + it(cmd .. 's' .. prep .. ' the current line by default when feeding', + function() + feed(':' .. cmd .. '\nabc\ndef\n.\n') + eq(ret1, buffer_contents()) + end) + -- This used to crash since that commit as well. + it('opens empty cmdline window', function() + local hisline = '" Some comment to be stored in history' + feed(':' .. hisline .. '<CR>') + feed(':' .. cmd .. '<CR>abc<CR>def<C-f>') + eq({ 'def' }, buffer_contents()) + eq(hisline, funcs.histget(':', -2)) + eq(cmd, funcs.histget(':')) + -- Test that command-line window was launched + eq('nofile', curbufmeths.get_option('buftype')) + eq('n', funcs.mode(1)) + feed('<CR>') + eq('c', funcs.mode(1)) + feed('.<CR>') + eq('n', funcs.mode(1)) + eq(ret1, buffer_contents()) + end) + end) +end +cmdtest('insert', ' before', { 'abc', 'def', 'foo', 'bar', 'baz' }) +cmdtest('append', ' after', { 'foo', 'abc', 'def', 'bar', 'baz' }) +cmdtest('change', '', { 'abc', 'def', 'bar', 'baz' }) diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua new file mode 100644 index 0000000000..69467632a4 --- /dev/null +++ b/test/functional/ex_cmds/cd_spec.lua @@ -0,0 +1,152 @@ +-- Specs for :cd, :tcd, :lcd and getcwd() + +local helpers = require('test.functional.helpers') +local execute, eq, clear, eval, exc_exec = + helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.exc_exec +local lfs = require('lfs') + +-- These directories will be created for testing +local directories = { + 'Xtest-functional-ex_cmds-cd_spec.1', -- Tab + 'Xtest-functional-ex_cmds-cd_spec.2', -- Window + 'Xtest-functional-ex_cmds-cd_spec.3', -- New global +} + +-- Shorthand writing to get the current working directory +local cwd = function() return eval('getcwd( )') end -- effective working dir +local wcwd = function() return eval('getcwd( 0 )') end -- window dir +local tcwd = function() return eval('getcwd(-1, 0)') end -- tab dir +--local gcwd = function() return eval('getcwd(-1, -1)') end -- global dir + +-- Same, except these tell us if there is a working directory at all +--local lwd = function() return eval('haslocaldir( )') end -- effective working dir +local wlwd = function() return eval('haslocaldir( 0 )') end -- window dir +local tlwd = function() return eval('haslocaldir(-1, 0)') end -- tab dir +--local glwd = function() return eval('haslocaldir(-1, -1)') end -- global dir + +-- Test both the `cd` and `chdir` variants +for _, cmd in ipairs {'cd', 'chdir'} do + describe(':*' .. cmd, function() + before_each(function() + clear() + for _, d in ipairs(directories) do + lfs.mkdir(d) + end + end) + + after_each(function() + for _, d in ipairs(directories) do + lfs.rmdir(d) + end + end) + + it('works', function() + -- Store the initial working directory + local globalDir = cwd() + + -- Create a new tab first and verify that is has the same working dir + execute('tabnew') + eq(globalDir, cwd()) + eq(globalDir, tcwd()) -- has no tab-local directory + eq(0, tlwd()) + eq(globalDir, wcwd()) -- has no window-local directory + eq(0, wlwd()) + + -- Change tab-local working directory and verify it is different + execute('silent t' .. cmd .. ' ' .. directories[1]) + eq(globalDir .. '/' .. directories[1], cwd()) + eq(cwd(), tcwd()) -- working directory maches tab directory + eq(1, tlwd()) + eq(cwd(), wcwd()) -- still no window-directory + eq(0, wlwd()) + + -- Create a new window in this tab to test `:lcd` + execute('new') + eq(1, tlwd()) -- Still tab-local working directory + eq(0, wlwd()) -- Still no window-local working directory + eq(globalDir .. '/' .. directories[1], cwd()) + execute('silent l' .. cmd .. ' ../' .. directories[2]) + eq(globalDir .. '/' .. directories[2], cwd()) + eq(globalDir .. '/' .. directories[1], tcwd()) + eq(1, wlwd()) + + -- Verify the first window still has the tab local directory + execute('wincmd w') + eq(globalDir .. '/' .. directories[1], cwd()) + eq(globalDir .. '/' .. directories[1], tcwd()) + eq(0, wlwd()) -- No window-local directory + + -- Change back to initial tab and verify working directory has stayed + execute('tabnext') + eq(globalDir, cwd() ) + eq(0, tlwd()) + eq(0, wlwd()) + + -- Verify global changes don't affect local ones + execute('silent ' .. cmd .. ' ' .. directories[3]) + eq(globalDir .. '/' .. directories[3], cwd()) + execute('tabnext') + eq(globalDir .. '/' .. directories[1], cwd()) + eq(globalDir .. '/' .. directories[1], tcwd()) + eq(0, wlwd()) -- Still no window-local directory in this window + + -- Unless the global change happened in a tab with local directory + execute('silent ' .. cmd .. ' ..') + eq(globalDir, cwd() ) + eq(0 , tlwd()) + eq(0 , wlwd()) + -- Which also affects the first tab + execute('tabnext') + eq(globalDir, cwd()) + + -- But not in a window with its own local directory + execute('tabnext | wincmd w') + eq(globalDir .. '/' .. directories[2], cwd() ) + eq(0 , tlwd()) + eq(globalDir .. '/' .. directories[2], wcwd()) + end) + end) +end + +-- Test legal parameters for 'getcwd' and 'haslocaldir' +for _, cmd in ipairs {'getcwd', 'haslocaldir'} do + describe(cmd..'()', function() + before_each(function() + clear() + end) + + -- Test invalid argument types + local err474 = 'Vim(call):E474: Invalid argument' + it('fails on string', function() + eq(err474, exc_exec('call ' .. cmd .. '("some string")')) + end) + it('fails on float', function() + eq(err474, exc_exec('call ' .. cmd .. '(1.0)')) + end) + it('fails on list', function() + eq(err474, exc_exec('call ' .. cmd .. '([1, 2])')) + end) + it('fails on dictionary', function() + eq(err474, exc_exec('call ' .. cmd .. '({"key": "value"})')) + end) + it('fails on funcref', function() + eq(err474, exc_exec('call ' .. cmd .. '(function("tr"))')) + end) + + -- Test invalid numbers + it('fails on number less than -1', function() + eq(err474, exc_exec('call ' .. cmd .. '(-2)')) + end) + local err5001 = 'Vim(call):E5001: Higher scope cannot be -1 if lower scope is >= 0.' + it('fails on -1 if previous arg is >=0', function() + eq(err5001, exc_exec('call ' .. cmd .. '(0, -1)')) + end) + + -- Test wrong number of arguments + local err118 = 'Vim(call):E118: Too many arguments for function: ' .. cmd + it('fails to parse more than one argument', function() + eq(err118, exc_exec('call ' .. cmd .. '(0, 0, 0)')) + end) + end) +end + diff --git a/test/functional/ex_cmds/encoding_spec.lua b/test/functional/ex_cmds/encoding_spec.lua index 997776ca25..6d402b7974 100644 --- a/test/functional/ex_cmds/encoding_spec.lua +++ b/test/functional/ex_cmds/encoding_spec.lua @@ -21,20 +21,21 @@ describe('&encoding', function() eq(3, eval('strwidth("Bär")')) end) - it('is not changed by `set all&`', function() - -- we need to set &encoding to something non-default - -- use 'latin1' when enc&vi is 'utf-8', 'utf-8' otherwise - execute('set fenc=default') - local enc_default, enc_other, width = eval('&fenc'), 'utf-8', 3 - if enc_default == 'utf-8' then - enc_other = 'latin1' - width = 4 -- utf-8 string 'Bär' will count as 4 latin1 chars - end + it('can be changed before startup', function() + clear('set enc=latin1') + execute('set encoding=utf-8') + -- error message expected + feed('<cr>') + eq('latin1', eval('&encoding')) + eq(4, eval('strwidth("Bär")')) + end) - clear('set enc=' .. enc_other) + it('is not changed by `set all&`', function() + -- we need to set &encoding to something non-default. Use 'latin1' + clear('set enc=latin1') execute('set all&') - eq(enc_other, eval('&encoding')) - eq(width, eval('strwidth("Bär")')) + eq('latin1', eval('&encoding')) + eq(4, eval('strwidth("Bär")')) end) end) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index dac6757a97..5bba1a0e7c 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -2,7 +2,7 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers') local buf, eq, execute = helpers.curbufmeths, helpers.eq, helpers.execute -local feed, nvim_prog = helpers.feed, helpers.nvim_prog +local feed, nvim_prog, wait = helpers.feed, helpers.nvim_prog, helpers.wait local ok, set_session, spawn = helpers.ok, helpers.set_session, helpers.spawn local shada_file = 'test.shada' @@ -59,9 +59,13 @@ describe(':oldfiles!', function() execute('edit testfile2') filename2 = buf.get_name() execute('wshada ' .. shada_file) + wait() _clear() execute('rshada! ' .. shada_file) + -- Ensure nvim is out of "Press ENTER..." screen + feed('<cr>') + -- Ensure v:oldfiles isn't busted. Since things happen so fast, -- the ordering of v:oldfiles is unstable (it uses qsort() under-the-hood). -- Let's verify the contents and the length of v:oldfiles before moving on. diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua new file mode 100644 index 0000000000..d90b297ca8 --- /dev/null +++ b/test/functional/ex_cmds/write_spec.lua @@ -0,0 +1,38 @@ +-- Specs for :write + +local helpers = require('test.functional.helpers') +local eq, eval, clear, write_file, execute, source = + helpers.eq, helpers.eval, helpers.clear, helpers.write_file, + helpers.execute, helpers.source + +describe(':write', function() + it('&backupcopy=auto preserves symlinks', function() + clear('set backupcopy=auto') + os.remove('test_bkc_file.txt') + os.remove('test_bkc_link.txt') + write_file('test_bkc_file.txt', 'content0') + execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + source([[ + edit test_bkc_link.txt + call setline(1, ['content1']) + write + ]]) + eq(eval("['content1']"), eval("readfile('test_bkc_file.txt')")) + eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')")) + end) + + it('&backupcopy=no replaces symlink with new file', function() + clear('set backupcopy=no') + os.remove('test_bkc_file.txt') + os.remove('test_bkc_link.txt') + write_file('test_bkc_file.txt', 'content0') + execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + source([[ + edit test_bkc_link.txt + call setline(1, ['content1']) + write + ]]) + eq(eval("['content0']"), eval("readfile('test_bkc_file.txt')")) + eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')")) + end) +end) diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua index c3147e1c0f..969dfea3d9 100644 --- a/test/functional/ex_cmds/wundo_spec.lua +++ b/test/functional/ex_cmds/wundo_spec.lua @@ -24,6 +24,6 @@ describe('u_* functions', function() '-c', 'set undodir=. undofile'}) set_session(session) execute('echo "True"') -- Should not error out due to crashed Neovim - session:exit(0) + session:close() end) end) diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index 207d94124f..21f14be62c 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -9,7 +9,7 @@ describe(':wshada', function() before_each(function() if session then - session:exit(0) + session:close() end -- Override the default session because we need 'swapfile' for these tests. diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index 5fa8a58049..d9ec254aff 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -1,25 +1,59 @@ -// A simple implementation of a shell for testing -// `termopen([&sh, &shcf, '{cmd'}])` and `termopen([&sh])`. -// -// If launched with no arguments, prints "ready $ ", otherwise prints -// "ready $ {cmd}\n". - #include <stdio.h> #include <string.h> +#include <stdint.h> + +static void help(void) +{ + puts("A simple implementation of a shell for testing termopen()."); + puts(""); + puts("Usage:"); + puts(" shell-test --help"); + puts(" Prints this help to stdout."); + puts(" shell-test"); + puts(" shell-test EXE"); + puts(" Prints \"ready $ \" to stderr."); + puts(" shell-test EXE \"prog args...\""); + puts(" Prints \"ready $ prog args...\\n\" to stderr."); + puts(" shell-test REP {byte} \"line line line\""); + puts(" Prints \"{lnr}: line line line\\n\" to stdout {byte} times."); + puts(" I.e. for `shell-test REP ab \"test\"'"); + puts(" 0: test"); + puts(" ..."); + puts(" 96: test"); + puts(" will be printed because byte `a' is equal to 97."); +} int main(int argc, char **argv) { - fprintf(stderr, "ready $ "); + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + help(); + } - if (argc == 3) { - // argv should be {"terminal-test", "EXE", "prog args..."} - if (strcmp(argv[1], "EXE") != 0) { - fprintf(stderr, "first argument must be 'EXE'\n"); - return 2; + if (argc >= 2) { + if (strcmp(argv[1], "EXE") == 0) { + fprintf(stderr, "ready $ "); + if (argc >= 3) { + fprintf(stderr, "%s\n", argv[2]); + } + } else if (strcmp(argv[1], "REP") == 0) { + if (argc < 4) { + fprintf(stderr, "Not enough REP arguments\n"); + return 4; + } + uint8_t number = (uint8_t) *argv[2]; + for (uint8_t i = 0; i < number; i++) { + printf("%d: %s\n", (int) i, argv[3]); + } + } else { + fprintf(stderr, "Unknown first argument\n"); + return 3; } - - fprintf(stderr, "%s\n", argv[2]); + return 0; + } else if (argc == 1) { + fprintf(stderr, "ready $ "); + return 0; + } else { + fprintf(stderr, "Missing first argument\n"); + return 2; } - - return 0; } diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 9562457c8e..37b7bf664c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,16 +1,16 @@ require('coxpcall') local lfs = require('lfs') local assert = require('luassert') -local Loop = require('nvim.loop') -local MsgpackStream = require('nvim.msgpack_stream') -local AsyncSession = require('nvim.async_session') +local ChildProcessStream = require('nvim.child_process_stream') local Session = require('nvim.session') local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim' local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', - '--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 encoding=utf-8 undodir=. directory=. viewdir=. backupdir=.', + '--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 undodir=. directory=. viewdir=. backupdir=.', '--embed'} +local mpack = require('mpack') + -- Formulate a path to the directory containing nvim. We use this to -- help run test executables. It helps to keep the tests working, even -- when the build is not in the default location. @@ -59,6 +59,9 @@ end local session, loop_running, last_error local function set_session(s) + if session then + session:close() + end session = s end @@ -132,6 +135,22 @@ local function nvim_eval(expr) return request('vim_eval', expr) end +local os_name = (function() + local name = nil + return (function() + if not name then + if nvim_eval('has("win32")') == 1 then + name = 'windows' + elseif nvim_eval('has("macunix")') == 1 then + name = 'osx' + else + name = 'unix' + end + end + return name + end) +end)() + local function nvim_call(name, ...) return request('vim_call_function', name, {...}) end @@ -157,7 +176,7 @@ local function dedent(str) return str end -- create a pattern for the indent - indent = indent:gsub('%s', '%%s') + indent = indent:gsub('%s', '[ \t]') -- strip it from the first line str = str:gsub('^'..indent, '') -- strip it from the remaining lines @@ -193,24 +212,17 @@ local function merge_args(...) end local function spawn(argv, merge) - local loop = Loop.new() - local msgpack_stream = MsgpackStream.new(loop) - local async_session = AsyncSession.new(msgpack_stream) - local sess = Session.new(async_session) - loop:spawn(merge and merge_args(prepend_argv, argv) or argv) - return sess + local child_stream = ChildProcessStream.spawn(merge and merge_args(prepend_argv, argv) or argv) + return Session.new(child_stream) end local function clear(extra_cmd) - if session then - session:exit(0) - end local args = {unpack(nvim_argv)} if extra_cmd ~= nil then table.insert(args, '--cmd') table.insert(args, extra_cmd) end - session = spawn(args) + set_session(spawn(args)) end local function insert(...) @@ -246,9 +258,13 @@ end local function source(code) local tmpname = os.tmpname() + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then + tmpname = '/private'..tmpname + end write_file(tmpname, code) nvim_command('source '..tmpname) os.remove(tmpname) + return tmpname end local function eq(expected, actual) @@ -300,7 +316,7 @@ local function curbuf_contents() -- previously sent keys are processed(vim_eval is a deferred function, and -- only processed after all input) wait() - return table.concat(curbuf('get_line_slice', 0, -1, true, true), '\n') + return table.concat(curbuf('get_lines', 0, -1, true), '\n') end local function curwin(method, ...) @@ -328,15 +344,13 @@ local function rmdir(path) return nil end for file in lfs.dir(path) do - if file == '.' or file == '..' then - goto continue - end - local ret, err = os.remove(path..'/'..file) - if not ret then - error('os.remove: '..err) - return nil + if file ~= '.' and file ~= '..' then + local ret, err = os.remove(path..'/'..file) + if not ret then + error('os.remove: '..err) + return nil + end end - ::continue:: end local ret, err = os.remove(path) if not ret then @@ -425,6 +439,7 @@ return { wait = wait, set_session = set_session, write_file = write_file, + os_name = os_name, rmdir = rmdir, mkdir = lfs.mkdir, exc_exec = exc_exec, @@ -438,4 +453,5 @@ return { curbufmeths = curbufmeths, curwinmeths = curwinmeths, curtabmeths = curtabmeths, + NIL = mpack.NIL } diff --git a/test/functional/job/job_spec.lua b/test/functional/job/job_spec.lua index 0915ab0955..d21b9051e2 100644 --- a/test/functional/job/job_spec.lua +++ b/test/functional/job/job_spec.lua @@ -142,6 +142,35 @@ describe('jobs', function() nvim('command', "call jobstart(['cat', '-'], g:job_opts)") end) + it('can get the pid value using getpid', function() + nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + local pid = eval('jobpid(j)') + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + nvim('command', 'call jobstop(j)') + eq({'notification', 'exit', {0, 0}}, next_msg()) + neq(0,os.execute('ps -p '..pid..' > /dev/null')) + end) + + it("doesn't survive the exit of nvim", function() + -- use sleep, which doesn't die on stdin close + nvim('command', "let j = jobstart(['sleep', '1000'], g:job_opts)") + local pid = eval('jobpid(j)') + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + clear() + neq(0,os.execute('ps -p '..pid..' > /dev/null')) + end) + + it('can survive the exit of nvim with "detach"', function() + nvim('command', 'let g:job_opts.detach = 1') + nvim('command', "let j = jobstart(['sleep', '1000'], g:job_opts)") + local pid = eval('jobpid(j)') + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + clear() + eq(0,os.execute('ps -p '..pid..' > /dev/null')) + -- clean up after ourselves + os.execute('kill -9 '..pid..' > /dev/null') + end) + it('can pass user data to the callback', function() nvim('command', 'let g:job_opts.user = {"n": 5, "s": "str", "l": [1]}') nvim('command', "call jobstart(['echo'], g:job_opts)") diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 39de3e8280..4b838eda1d 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -674,6 +674,13 @@ describe('cindent', function() { } + A::A(int a, int b) + : aa(a), + bb(b), + cc(c) + { + } + class CAbc : public BaseClass1, protected BaseClass2 @@ -901,6 +908,72 @@ describe('cindent', function() { 111111111111111111; } + void getstring() { + /* Raw strings */ + const char* s = R"( + test { + # comment + field: 123 + } + )"; + } + void getstring() { + const char* s = R"foo( + test { + # comment + field: 123 + } + )foo"; + } + + { + int a[4] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + }; + } + + { + a = b[2] + + 3; + } + + { + if (1) + /* aaaaa + * bbbbb + */ + a = 1; + } + + void func() + { + switch (foo) + { + case (bar): + if (baz()) + quux(); + break; + case (shmoo): + if (!bar) + { + } + case (foo1): + switch (bar) + { + case baz: + baz_f(); + break; + } + break; + default: + baz(); + baz(); + break; + } + } /* end of AUTO */ ]=]) @@ -1563,6 +1636,13 @@ describe('cindent', function() { } + A::A(int a, int b) + : aa(a), + bb(b), + cc(c) + { + } + class CAbc : public BaseClass1, protected BaseClass2 @@ -1790,6 +1870,72 @@ describe('cindent', function() { 111111111111111111; } + void getstring() { + /* Raw strings */ + const char* s = R"( + test { + # comment + field: 123 + } + )"; + } + void getstring() { + const char* s = R"foo( + test { + # comment + field: 123 + } + )foo"; + } + + { + int a[4] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + }; + } + + { + a = b[2] + + 3; + } + + { + if (1) + /* aaaaa + * bbbbb + */ + a = 1; + } + + void func() + { + switch (foo) + { + case (bar): + if (baz()) + quux(); + break; + case (shmoo): + if (!bar) + { + } + case (foo1): + switch (bar) + { + case baz: + baz_f(); + break; + } + break; + default: + baz(); + baz(); + break; + } + } /* end of AUTO */ ]=]) @@ -4213,4 +4359,321 @@ describe('cindent', function() JSEND ]=]) end) + + it('javascript indent / vim-patch 7.4.670', function() + insert_([=[ + + JSSTART + // Results of JavaScript indent + // 1 + (function(){ + var a = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 2 + (function(){ + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 3 + (function(){ + var a = [ + 0 + + // comment 1 + 5 * + /* comment 2 */ + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 4 + { + var a = [ + 0, + 1 + ]; + var b; + var c; + } + + // 5 + { + var a = [ + [ + 0 + ], + 2, + 3 + ]; + } + + // 6 + { + var a = [ + [ + 0, + 1 + ], + 2, + 3 + ]; + } + + // 7 + { + var a = [ + // [ + 0, + // 1 + // ], + 2, + 3 + ]; + } + + // 8 + var x = [ + (function(){ + var a, + b, + c, + d, + e, + f, + g, + h, + i; + }) + ]; + + // 9 + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + + // 10 + var a, + b, + c, + d, + e, + f, + g, + h, + i; + JSEND + ]=]) + + -- :set cino=j1,J1,+2 + execute('set cino=j1,J1,+2') + execute('/^JSSTART') + feed('=/^JSEND<cr>') + + expect([=[ + + JSSTART + // Results of JavaScript indent + // 1 + (function(){ + var a = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 2 + (function(){ + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 3 + (function(){ + var a = [ + 0 + + // comment 1 + 5 * + /* comment 2 */ + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 4 + { + var a = [ + 0, + 1 + ]; + var b; + var c; + } + + // 5 + { + var a = [ + [ + 0 + ], + 2, + 3 + ]; + } + + // 6 + { + var a = [ + [ + 0, + 1 + ], + 2, + 3 + ]; + } + + // 7 + { + var a = [ + // [ + 0, + // 1 + // ], + 2, + 3 + ]; + } + + // 8 + var x = [ + (function(){ + var a, + b, + c, + d, + e, + f, + g, + h, + i; + }) + ]; + + // 9 + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + + // 10 + var a, + b, + c, + d, + e, + f, + g, + h, + i; + JSEND + ]=]) + end) end) diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua new file mode 100644 index 0000000000..483e465cee --- /dev/null +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -0,0 +1,230 @@ +-- Tests for autocommands +-- - FileWritePre writing a compressed file +-- - FileReadPost reading a compressed file +-- - BufNewFile reading a file template +-- - BufReadPre decompressing the file to be read +-- - FilterReadPre substituting characters in the temp file +-- - FilterReadPost substituting characters after filtering +-- - FileReadPre set options for decompression +-- - FileReadPost decompress the file +-- Note: This test is skipped if "gzip" is not available. +-- $GZIP is made empty, "-v" would cause trouble. +-- Use a FileChangedShell autocommand to avoid a prompt for "Xtestfile.gz" +-- being modified outside of Vim (noticed on Solaris). + +local helpers, lfs = require('test.functional.helpers'), require('lfs') +local clear, execute, expect, eq, neq, dedent, write_file, feed = + helpers.clear, helpers.execute, helpers.expect, helpers.eq, helpers.neq, + helpers.dedent, helpers.write_file, helpers.feed + +local function has_gzip() + return os.execute('gzip --help >/dev/null 2>&1') == 0 +end + +local function prepare_gz_file(name, text) + write_file(name, text..'\n') + -- Compress the file with gzip. + os.execute('gzip --force '..name) + -- This should create the .gz file and delete the original. + neq(nil, lfs.attributes(name..'.gz')) + eq(nil, lfs.attributes(name)) +end + +describe('file reading, writing and bufnew and filter autocommands', function() + local text1 = dedent([[ + start of testfile + line 2 Abcdefghijklmnopqrstuvwxyz + line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 4 Abcdefghijklmnopqrstuvwxyz + line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 6 Abcdefghijklmnopqrstuvwxyz + line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 8 Abcdefghijklmnopqrstuvwxyz + line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 10 Abcdefghijklmnopqrstuvwxyz + end of testfile]]) + setup(function() + write_file('Xtest.c', [[ + /* + * Here is a new .c file + */ + ]]) + end) + before_each(clear) + teardown(function() + os.remove('Xtestfile.gz') + os.remove('Xtest.c') + os.remove('test.out') + end) + + if not has_gzip() then + pending('skipped (missing `gzip` utility)', function() end) + else + + it('FileReadPost (using gzip)', function() + prepare_gz_file('Xtestfile', text1) + execute('let $GZIP = ""') + --execute('au FileChangedShell * echo "caught FileChangedShell"') + execute('set bin') + execute("au FileReadPost *.gz '[,']!gzip -d") + -- Read and decompress the testfile. + execute('$r Xtestfile.gz') + expect('\n'..text1) + end) + + it('BufReadPre, BufReadPost (using gzip)', function() + prepare_gz_file('Xtestfile', text1) + local gzip_data = io.open('Xtestfile.gz'):read('*all') + execute('let $GZIP = ""') + -- Setup autocommands to decompress before reading and re-compress afterwards. + execute("au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand('<afile>'))") + execute("au BufReadPre *.gz call rename(expand('<afile>:r'), expand('<afile>'))") + execute("au BufReadPost *.gz call rename(expand('<afile>'), expand('<afile>:r'))") + execute("au BufReadPost *.gz exe '!gzip ' . shellescape(expand('<afile>:r'))") + -- Edit compressed file. + execute('e! Xtestfile.gz') + -- Discard all prompts and messages. + feed('<C-L>') + -- Expect the decompressed file in the buffer. + expect(text1) + -- Expect the original file to be unchanged. + eq(gzip_data, io.open('Xtestfile.gz'):read('*all')) + end) + + it('FileReadPre, FileReadPost', function() + prepare_gz_file('Xtestfile', text1) + execute('au! FileReadPre *.gz exe "silent !gzip -d " . shellescape(expand("<afile>"))') + execute('au FileReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))') + execute("au! FileReadPost *.gz '[,']s/l/L/") + -- Read compressed file. + execute('$r Xtestfile.gz') + -- Discard all prompts and messages. + feed('<C-L>') + expect([[ + + start of testfiLe + Line 2 Abcdefghijklmnopqrstuvwxyz + Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 4 Abcdefghijklmnopqrstuvwxyz + Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 6 Abcdefghijklmnopqrstuvwxyz + Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 8 Abcdefghijklmnopqrstuvwxyz + Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Line 10 Abcdefghijklmnopqrstuvwxyz + end of testfiLe]]) + end) + + end + + it('FileAppendPre, FileAppendPost', function() + execute('au BufNewFile *.c read Xtest.c') + -- Will load Xtest.c. + execute('e! foo.c') + execute("au FileAppendPre *.out '[,']s/new/NEW/") + execute('au FileAppendPost *.out !cat Xtest.c >>test.out') + -- Append it to the output file. + execute('w>>test.out') + -- Discard all prompts and messages. + feed('<C-L>') + -- Expect the decompressed file in the buffer. + execute('e test.out') + expect([[ + + /* + * Here is a NEW .c file + */]]) + end) + + it('FilterReadPre, FilterReadPost', function() + -- Write a special input file for this test block. + write_file('test.out', dedent([[ + startstart + ]]) .. text1 .. dedent([[ + + + start of test.c + /* + * Here is a new .c file + */ + end of test.c + ]]) .. text1 .. dedent([[ + + + /* + * Here is a NEW .c file + */ + /* + * Here is a new .c file + */ + ]]) .. text1 .. dedent([[ + + /* + * Here is a new .c file + */]])) + -- Need temp files here. + execute('set shelltemp') + execute('au FilterReadPre *.out call rename(expand("<afile>"), expand("<afile>") . ".t")') + execute('au FilterReadPre *.out exe "silent !sed s/e/E/ " . shellescape(expand("<afile>")) . ".t >" . shellescape(expand("<afile>"))') + execute('au FilterReadPre *.out exe "silent !rm " . shellescape(expand("<afile>")) . ".t"') + execute("au FilterReadPost *.out '[,']s/x/X/g") + -- Edit the output file. + execute('e! test.out') + execute('23,$!cat') + -- Discard all prompts and messages. + feed('<C-L>') + -- Remove CR for when sed adds them. + execute([[23,$s/\r$//]]) + expect([[ + startstart + start of testfile + line 2 Abcdefghijklmnopqrstuvwxyz + line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 4 Abcdefghijklmnopqrstuvwxyz + line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 6 Abcdefghijklmnopqrstuvwxyz + line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 8 Abcdefghijklmnopqrstuvwxyz + line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 10 Abcdefghijklmnopqrstuvwxyz + end of testfile + + start of test.c + /* + * Here is a new .c file + */ + end of test.c + start of testfile + line 2 Abcdefghijklmnopqrstuvwxyz + line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + line 4 Abcdefghijklmnopqrstuvwxyz + linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 6 AbcdefghijklmnopqrstuvwXyz + linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 8 AbcdefghijklmnopqrstuvwXyz + linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 10 AbcdefghijklmnopqrstuvwXyz + End of testfile + + /* + * HEre is a NEW .c file + */ + /* + * HEre is a new .c file + */ + start of tEstfile + linE 2 AbcdefghijklmnopqrstuvwXyz + linE 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 4 AbcdefghijklmnopqrstuvwXyz + linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 6 AbcdefghijklmnopqrstuvwXyz + linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 8 AbcdefghijklmnopqrstuvwXyz + linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + linE 10 AbcdefghijklmnopqrstuvwXyz + End of testfile + /* + * HEre is a new .c file + */]]) + end) +end) diff --git a/test/functional/legacy/031_close_commands_spec.lua b/test/functional/legacy/031_close_commands_spec.lua index 3597cba12a..b79b1903ba 100644 --- a/test/functional/legacy/031_close_commands_spec.lua +++ b/test/functional/legacy/031_close_commands_spec.lua @@ -10,7 +10,7 @@ -- :edit local helpers = require('test.functional.helpers') -local feed, insert = helpers.feed, helpers.insert +local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect describe('Commands that close windows and/or buffers', function() @@ -84,6 +84,28 @@ describe('Commands that close windows and/or buffers', function() feed('GA 4<Esc>:all!<CR>') execute('1wincmd w') expect('testtext 2 2 2') + + -- Test ":q!" and hidden buffer. + execute('bw! Xtest1 Xtest2 Xtest3 Xtest4') + execute('sp Xtest1') + execute('wincmd w') + execute('bw!') + execute('set modified') + execute('bot sp Xtest2') + execute('set modified') + execute('bot sp Xtest3') + execute('set modified') + execute('wincmd t') + execute('hide') + execute('q!') + expect('testtext 3') + execute('q!') + feed('<CR>') + expect('testtext 1') + source([[ + q! + " Now nvim should have exited + throw "Oh, Not finished yet."]]) end) teardown(function() diff --git a/test/functional/legacy/035_increment_and_decrement_spec.lua b/test/functional/legacy/035_increment_and_decrement_spec.lua index 20c0cc4206..e6252c384b 100644 --- a/test/functional/legacy/035_increment_and_decrement_spec.lua +++ b/test/functional/legacy/035_increment_and_decrement_spec.lua @@ -11,34 +11,40 @@ describe('increment and decrement commands', function() it('should work', function() -- Insert some numbers in various bases. insert([[ - 100 0x100 077 0 - 100 0x100 077 + 0b101 100 0x100 077 0 + 0b101 100 0x100 077 100 0x100 077 0xfF 0xFf - 100 0x100 077]]) + 100 0x100 077 + 0x0b101 0b1101]]) -- Increment and decrement numbers in the first row, interpreting the -- numbers as decimal, octal or hexadecimal. - execute('set nrformats=octal,hex', '1') - feed('102ll64128$') + execute('set nrformats=bin,octal,hex', '1') + feed('63l102ll64128$') -- For the second row, treat the numbers as decimal or octal. -- 0x100 should be interpreted as decimal 0, the character x, and decimal 100. execute('set nrformats=octal', '2') - feed('0102l2w65129blx6lD') + feed('0w102l2w65129blx6lD') -- For the third row, treat the numbers as decimal or hexadecimal. -- 077 should be interpreted as decimal 77. execute('set nrformats=hex', '3') feed('0101l257Txldt ') - -- For the last row, interpret all numbers as decimal. + -- For the fourth row, interpret all numbers as decimal. execute('set nrformats=', '4') feed('0200l100w78') + -- For the last row, interpret as binary and hexadecimal. + execute('set nrformats=bin,hex', '5') + feed('010065l6432') + expect([[ - 0 0x0ff 0000 -1 - 0 1x100 0777777 + 0b011 0 0x0ff 0000 -1 + 1b101 0 1x100 0777777 -1 0x0 078 0xFE 0xfe - -100 -100x100 000]]) + -100 -100x100 000 + 0x0b0de 0b0101101]]) end) end) diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua new file mode 100644 index 0000000000..de080f4b43 --- /dev/null +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -0,0 +1,271 @@ +-- Test character classes in regexp using regexpengine 0, 1, 2. + +local helpers = require('test.functional.helpers') +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local source, write_file = helpers.source, helpers.write_file +local os_name = helpers.os_name + +local function sixlines(text) + local result = '' + for _ = 1, 6 do + result = result .. text .. '\n' + end + return result +end + +local function diff(text, nodedent) + local tmpname = os.tmpname() + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then + tmpname = '/private'..tmpname + end + execute('w! '..tmpname) + helpers.wait() + local data = io.open(tmpname):read('*all') + if nodedent then + helpers.eq(text, data) + else + helpers.eq(helpers.dedent(text), data) + end + os.remove(tmpname) +end + +describe('character classes in regexp', function() + local ctrl1 = '\t\012\r' + local punct1 = " !\"#$%&'()#+'-./" + local digits = '0123456789' + local punct2 = ':;<=>?@' + local upper = 'ABCDEFGHIXYZ' + local punct3 = '[\\]^_`' + local lower = 'abcdefghiwxyz' + local punct4 = '{|}~' + local ctrl2 = '\127\128\130\144\155' + local iso_text = '\166\177\188\199\211\233' -- "¦±¼ÇÓé" in utf-8 + setup(function() + -- The original test32.in file was not in utf-8 encoding and did also + -- contain some control characters. We use lua escape sequences to write + -- them to the test file. + local line = ctrl1..punct1..digits..punct2..upper..punct3..lower..punct4..ctrl2..iso_text + write_file('test36.in', sixlines(line)) + end) + before_each(function() + clear() + execute('e test36.in') + end) + teardown(function() + os.remove('test36.in') + end) + + it('is working', function() + source([[ + 1 s/\%#=0\d//g + 2 s/\%#=1\d//g + 3 s/\%#=2\d//g + 4 s/\%#=0[0-9]//g + 5 s/\%#=1[0-9]//g + 6 s/\%#=2[0-9]//g]]) + diff(sixlines(ctrl1..punct1..punct2..upper..punct3..lower..punct4.. + ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\D//g + 2 s/\%#=1\D//g + 3 s/\%#=2\D//g + 4 s/\%#=0[^0-9]//g + 5 s/\%#=1[^0-9]//g + 6 s/\%#=2[^0-9]//g]]) + expect([[ + 0123456789 + 0123456789 + 0123456789 + 0123456789 + 0123456789 + 0123456789]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\o//g + 2 s/\%#=1\o//g + 3 s/\%#=2\o//g + 4 s/\%#=0[0-7]//g + 5 s/\%#=1[0-7]//g + 6 s/\%#=2[0-7]//g]]) + diff(sixlines(ctrl1..punct1..'89'..punct2..upper..punct3..lower..punct4..ctrl2.. + iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\O//g + 2 s/\%#=1\O//g + 3 s/\%#=2\O//g + 4 s/\%#=0[^0-7]//g + 5 s/\%#=1[^0-7]//g + 6 s/\%#=2[^0-7]//g]]) + expect([[ + 01234567 + 01234567 + 01234567 + 01234567 + 01234567 + 01234567]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\x//g + 2 s/\%#=1\x//g + 3 s/\%#=2\x//g + 4 s/\%#=0[0-9A-Fa-f]//g + 5 s/\%#=1[0-9A-Fa-f]//g + 6 s/\%#=2[0-9A-Fa-f]//g]]) + diff(sixlines(ctrl1..punct1..punct2..'GHIXYZ'..punct3..'ghiwxyz'..punct4..ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\X//g + 2 s/\%#=1\X//g + 3 s/\%#=2\X//g + 4 s/\%#=0[^0-9A-Fa-f]//g + 5 s/\%#=1[^0-9A-Fa-f]//g + 6 s/\%#=2[^0-9A-Fa-f]//g]]) + expect([[ + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef + 0123456789ABCDEFabcdef]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\w//g + 2 s/\%#=1\w//g + 3 s/\%#=2\w//g + 4 s/\%#=0[0-9A-Za-z_]//g + 5 s/\%#=1[0-9A-Za-z_]//g + 6 s/\%#=2[0-9A-Za-z_]//g]]) + diff(sixlines(ctrl1..punct1..punct2..'[\\]^`'..punct4..ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\W//g + 2 s/\%#=1\W//g + 3 s/\%#=2\W//g + 4 s/\%#=0[^0-9A-Za-z_]//g + 5 s/\%#=1[^0-9A-Za-z_]//g + 6 s/\%#=2[^0-9A-Za-z_]//g]]) + expect([[ + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz + 0123456789ABCDEFGHIXYZ_abcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\h//g + 2 s/\%#=1\h//g + 3 s/\%#=2\h//g + 4 s/\%#=0[A-Za-z_]//g + 5 s/\%#=1[A-Za-z_]//g + 6 s/\%#=2[A-Za-z_]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..'[\\]^`'..punct4..ctrl2.. + iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\H//g + 2 s/\%#=1\H//g + 3 s/\%#=2\H//g + 4 s/\%#=0[^A-Za-z_]//g + 5 s/\%#=1[^A-Za-z_]//g + 6 s/\%#=2[^A-Za-z_]//g]]) + expect([[ + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz + ABCDEFGHIXYZ_abcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\a//g + 2 s/\%#=1\a//g + 3 s/\%#=2\a//g + 4 s/\%#=0[A-Za-z]//g + 5 s/\%#=1[A-Za-z]//g + 6 s/\%#=2[A-Za-z]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..punct3..punct4..ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\A//g + 2 s/\%#=1\A//g + 3 s/\%#=2\A//g + 4 s/\%#=0[^A-Za-z]//g + 5 s/\%#=1[^A-Za-z]//g + 6 s/\%#=2[^A-Za-z]//g]]) + expect([[ + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz + ABCDEFGHIXYZabcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\l//g + 2 s/\%#=1\l//g + 3 s/\%#=2\l//g + 4 s/\%#=0[a-z]//g + 5 s/\%#=1[a-z]//g + 6 s/\%#=2[a-z]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..upper..punct3..punct4.. + ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\L//g + 2 s/\%#=1\L//g + 3 s/\%#=2\L//g + 4 s/\%#=0[^a-z]//g + 5 s/\%#=1[^a-z]//g + 6 s/\%#=2[^a-z]//g]]) + expect([[ + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz + abcdefghiwxyz]]) + end) + it('is working', function() + source([[ + 1 s/\%#=0\u//g + 2 s/\%#=1\u//g + 3 s/\%#=2\u//g + 4 s/\%#=0[A-Z]//g + 5 s/\%#=1[A-Z]//g + 6 s/\%#=2[A-Z]//g]]) + diff(sixlines(ctrl1..punct1..digits..punct2..punct3..lower..punct4.. + ctrl2..iso_text)) + end) + it('is working', function() + source([[ + 1 s/\%#=0\U//g + 2 s/\%#=1\U//g + 3 s/\%#=2\U//g + 4 s/\%#=0[^A-Z]//g + 5 s/\%#=1[^A-Z]//g + 6 s/\%#=2[^A-Z]//g]]) + expect([[ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ + ABCDEFGHIXYZ]]) + end) +end) diff --git a/test/functional/legacy/039_visual_block_mode_commands_spec.lua b/test/functional/legacy/039_visual_block_mode_commands_spec.lua new file mode 100644 index 0000000000..7195d7d11d --- /dev/null +++ b/test/functional/legacy/039_visual_block_mode_commands_spec.lua @@ -0,0 +1,234 @@ +-- Test Visual block mode commands +-- And test "U" in Visual mode, also on German sharp S. + +local helpers = require('test.functional.helpers') +local nvim, eq = helpers.meths, helpers.eq +local insert, feed = helpers.insert, helpers.feed +local clear, expect = helpers.clear, helpers.expect +local execute = helpers.execute + +describe('Visual block mode', function() + + before_each(function() + clear() + + execute('set ts&vi sw&vi sts&vi noet') -- Vim compatible + end) + + it('should shift, insert, replace and change a block', function() + insert([[ + abcdefghijklm + abcdefghijklm + abcdefghijklm + abcdefghijklm + abcdefghijklm]]) + + feed('gg') + -- Test shift-right of a block + feed('jllll<C-v>jj>wll<C-v>jlll><CR>') + -- Test shift-left of a block + feed('G$hhhh<C-v>kk<lt>') + -- Test block-insert + feed('Gkl<C-v>kkkIxyz<ESC>') + -- Test block-replace + feed('Gllll<C-v>kkklllrq') + -- Test block-change + feed('G$khhh<C-v>hhkkcmno<ESC>') + + expect([[ + axyzbcdefghijklm + axyzqqqq mno ghijklm + axyzqqqqef mno ghijklm + axyzqqqqefgmnoklm + abcdqqqqijklm]]) + end) + + it('should insert a block using cursor keys for movement', function() + insert([[ + aaaaaa + bbbbbb + cccccc + dddddd + + xaaa + bbbb + cccc + dddd]]) + + execute('/^aa') + feed('l<C-v>jjjlllI<Right><Right> <ESC>') + execute('/xaaa$') + feed('<C-v>jjjI<lt>><Left>p<ESC>') + + expect([[ + aaa aaa + bbb bbb + ccc ccc + ddd ddd + + <p>xaaa + <p>bbbb + <p>cccc + <p>dddd]]) + end) + + it('should create a block', function() + insert([[ + A23 + 4567 + + B23 + 4567 + + C23 + 4567]]) + + -- Test for Visual block was created with the last <C-v>$. + execute('/^A23$/') + feed('l<C-v>j$Aab<ESC>') + -- Test for Visual block was created with the middle <C-v>$ (1). + execute('/^B23$/') + feed('l<C-v>j$hAab<ESC>') + -- Test for Visual block was created with the middle <C-v>$ (2). + execute('/^C23$/') + feed('l<C-v>j$hhAab<ESC>') + + expect([[ + A23ab + 4567ab + + B23 ab + 4567ab + + C23ab + 456ab7]]) + end) + + it('should insert and append a block when virtualedit=all', function() + insert([[ + line1 + line2 + line3 + ]]) + + -- Test for Visual block insert when virtualedit=all and utf-8 encoding. + execute('set ve=all') + execute('/\t\tline') + feed('07l<C-v>jjIx<ESC>') + + expect([[ + x line1 + x line2 + x line3 + ]]) + + -- Test for Visual block append when virtualedit=all. + feed('012l<C-v>jjAx<ESC>') + + expect([[ + x x line1 + x x line2 + x x line3 + ]]) + end) + + it('should make a selected part uppercase', function() + -- GUe must uppercase a whole word, also when ß changes to SS. + feed('Gothe youtußeuu end<ESC>Ypk0wgUe<CR>') + -- GUfx must uppercase until x, inclusive. + feed('O- youßtußexu -<ESC>0fogUfx<CR>') + -- VU must uppercase a whole line. + feed('YpkVU<CR>') + -- Same, when it's the last line in the buffer. + feed('YPGi111<ESC>VUddP<CR>') + -- Uppercase two lines. + feed('Oblah di<CR>') + feed('doh dut<ESC>VkUj<CR>') + -- Uppercase part of two lines. + feed('ddppi333<ESC>k0i222<esc>fyllvjfuUk<CR>') + + expect([[ + + the YOUTUSSEUU end + - yOUSSTUSSEXu - + THE YOUTUSSEUU END + 111THE YOUTUSSEUU END + BLAH DI + DOH DUT + 222the yoUTUSSEUU END + 333THE YOUTUßeuu end]]) + end) + + it('should replace using Enter or NL', function() + -- Visual replace using Enter or NL. + feed('G3o123456789<ESC>2k05l<C-v>2jr<CR>') + feed('G3o98765<ESC>2k02l<C-v>2jr<C-v><CR>') + feed('G3o123456789<ESC>2k05l<C-v>2jr<CR>') + feed('G3o98765<ESC>2k02l<C-v>2jr<C-v><Nul>') + + local expected = [[ + + 12345 + 789 + 12345 + 789 + 12345 + 789 + 98<CR>65 + 98<CR>65 + 98<CR>65 + 12345 + 789 + 12345 + 789 + 12345 + 789 + 98<Nul>65 + 98<Nul>65 + 98<Nul>65]] + expected = expected:gsub('<CR>', '\r') + expected = expected:gsub('<Nul>', '\000') + + expect(expected) + end) + + it('should treat cursor position correctly when virtualedit=block', function() + insert([[ + 12345 + 789 + 98765]]) + + -- Test cursor position. When virtualedit=block and Visual block mode and $gj. + execute('set ve=block') + feed('G2l') + feed('2k<C-v>$gj<ESC>') + execute([[let cpos=getpos("'>")]]) + local cpos = nvim.get_var('cpos') + local expected = { + col = 4, + off = 0 + } + local actual = { + col = cpos[3], + off = cpos[4] + } + + eq(expected, actual) + end) + + it('should replace spaces in front of the block with tabs', function() + insert([[ + #define BO_ALL 0x0001 + #define BO_BS 0x0002 + #define BO_CRSR 0x0004]]) + + -- Block_insert when replacing spaces in front of the block with tabs. + execute('set ts=8 sts=4 sw=4') + feed('ggf0<C-v>2jI<TAB><ESC>') + + expect([[ + #define BO_ALL 0x0001 + #define BO_BS 0x0002 + #define BO_CRSR 0x0004]]) + end) +end) diff --git a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua index 1359b45228..2a4c0149fa 100644 --- a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua +++ b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua @@ -31,7 +31,8 @@ local function run_test_with_regexpengine(regexpengine) h AÀÃÂÃÄÅĀĂĄÇÇžÇ áº¢ BḂḆ CÇĆĈĊČ DÄŽÄḊḎḠEÈÉÊËĒĔĖĘĚẺẼ FḞ GÄœÄžÄ Ä¢Ç¤Ç¦Ç´á¸ HĤĦḢḦḨ IÃŒÃÃŽÃĨĪĬĮİÇỈ JÄ´ KĶǨḰḴ LĹĻĽĿÅḺ MḾṀ NÑŃŅŇṄṈ OÃ’Ã“Ã”Ã•Ã–Ã˜ÅŒÅŽÅÆ ǑǪǬỎ PṔṖ Q RŔŖŘṘṞ SŚŜŞŠṠTŢŤŦṪṮ UÙÚÛÜŨŪŬŮŰŲƯǓỦ Vá¹¼ WŴẀẂẄẆ XẊẌ YÃŶŸẎỲỶỸ ZŹŻŽƵáºáº” i aà áâãäåÄăąǎǟǡả bḃḇ cÃ§Ä‡Ä‰Ä‹Ä dÄđḋá¸á¸‘ eèéêëēĕėęěẻẽ fḟ gÄğġģǥǧǵḡ hĥħḣḧḩẖ iìÃîïĩīÄįÇỉ jĵǰ kķǩḱḵ lĺļľŀłḻ mḿṠnñńņňʼnṅṉ oòóôõöøÅÅőơǒǫÇá» pṕṗ q rŕŗřṙṟ sÅ›Åşšṡ tţťŧṫṯẗ uùúûüũūÅůűųưǔủ vá¹½ wŵáºáºƒáº…ẇẘ xẋẠyýÿŷáºáº™á»³á»·á»¹ zźżžƶẑẕ j 0123â¤x - k combinations]]) + k combinations + l ä ö ü ᾱ̆Ì]]) execute('set re=' .. regexpengine) @@ -85,6 +86,11 @@ local function run_test_with_regexpengine(regexpengine) execute([[let @w=':%s#comb[i]nations#œ̄ṣÌm̥̄ᾱ̆Ì#g']]) execute('@w') + -- Line l. Ex command ":s/ \?/ /g" should NOT split multi-byte characters + -- into bytes (fixed by vim-7.3.192). + execute([[/^l]]) + execute([[s/ \?/ /g]]) + -- Additional tests. Test matchstr() with multi-byte characters. feed('G') execute([[put =matchstr(\"×בגד\", \".\", 0, 2)]]) -- ב @@ -123,6 +129,7 @@ local function run_test_with_regexpengine(regexpengine) i aà áâãäåÄăąǎǟǡả bḃḇ cÃ§Ä‡Ä‰Ä‹Ä dÄđḋá¸á¸‘ eèéêëēĕėęěẻẽ fḟ gÄğġģǥǧǵḡ hĥħḣḧḩẖ iìÃîïĩīÄįÇỉ jĵǰ kķǩḱḵ lĺļľŀłḻ mḿṠnñńņňʼnṅṉ oòóôõöøÅÅőơǒǫÇá» pṕṗ q rŕŗřṙṟ sÅ›Åşšṡ tţťŧṫṯẗ uùúûüũūÅůűųưǔủ vá¹½ wŵáºáºƒáº…ẇẘ xẋẠyýÿŷáºáº™á»³á»·á»¹ zźżžƶẑ j 012⤠k œ̄ṣÌmÌ¥Ì„Î±Ì„Ì†Ì + l ä ö ü Î±Ì„Ì†Ì ×‘ בג × diff --git a/test/functional/legacy/045_folding_spec.lua b/test/functional/legacy/045_folding_spec.lua new file mode 100644 index 0000000000..04b623ff3b --- /dev/null +++ b/test/functional/legacy/045_folding_spec.lua @@ -0,0 +1,139 @@ +-- Tests for folding. + +local helpers = require('test.functional.helpers') +local feed, insert, clear, execute, expect = + helpers.feed, helpers.insert, helpers.clear, helpers.execute, helpers.expect + +describe('folding', function() + before_each(clear) + + it('is working', function() + insert([[ + 1 aa + 2 bb + 3 cc + 4 dd {{{ + 5 ee {{{ }}} + 6 ff }}} + 7 gg + 8 hh + 9 ii + a jj + b kk + last]]) + + -- Basic test if a fold can be created, opened, moving to the end and + -- closed. + execute('/^1') + feed('zf2j') + execute('call append("$", "manual " . getline(foldclosed(".")))') + feed('zo') + execute('call append("$", foldclosed("."))') + feed(']z') + execute('call append("$", getline("."))') + feed('zc') + execute('call append("$", getline(foldclosed(".")))') + -- Test folding with markers. + execute('set fdm=marker fdl=1 fdc=3') + execute('/^5') + execute('call append("$", "marker " . foldlevel("."))') + feed('[z') + execute('call append("$", foldlevel("."))') + feed('jo{{ <esc>r{jj') + execute('call append("$", foldlevel("."))') + feed('kYpj') + execute('call append("$", foldlevel("."))') + -- Test folding with indent. + execute('set fdm=indent sw=2') + execute('/^2 b') + feed('i <esc>jI <esc>') + execute('call append("$", "indent " . foldlevel("."))') + feed('k') + execute('call append("$", foldlevel("."))') + -- Test syntax folding. + execute('set fdm=syntax fdl=0') + execute('syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3') + execute('syn region Fd1 start="ee" end="ff" fold contained') + execute('syn region Fd2 start="gg" end="hh" fold contained') + execute('syn region Fd3 start="commentstart" end="commentend" fold contained') + feed('Gzk') + execute('call append("$", "folding " . getline("."))') + feed('k') + execute('call append("$", getline("."))') + feed('jAcommentstart <esc>Acommentend<esc>') + execute('set fdl=1') + feed('3j') + execute('call append("$", getline("."))') + execute('set fdl=0') + feed('zO<C-L>j') + execute('call append("$", getline("."))') + -- Test expression folding. + execute('fun Flvl()') + execute(' let l = getline(v:lnum)') + execute(' if l =~ "bb$"') + execute(' return 2') + execute(' elseif l =~ "gg$"') + execute(' return "s1"') + execute(' elseif l =~ "ii$"') + execute(' return ">2"') + execute(' elseif l =~ "kk$"') + execute(' return "0"') + execute(' endif') + execute(' return "="') + execute('endfun') + execute('set fdm=expr fde=Flvl()') + execute('/bb$') + execute('call append("$", "expr " . foldlevel("."))') + execute('/hh$') + execute('call append("$", foldlevel("."))') + execute('/ii$') + execute('call append("$", foldlevel("."))') + execute('/kk$') + execute('call append("$", foldlevel("."))') + execute('0,/^last/delete') + execute('delfun Flvl') + + -- Assert buffer contents. + expect([[ + manual 1 aa + -1 + 3 cc + 1 aa + marker 2 + 1 + 1 + 0 + indent 2 + 1 + folding 9 ii + 3 cc + 7 gg + 8 hh + expr 2 + 1 + 2 + 0]]) + end) + + it('can open after :move', function() + insert([[ + Test fdm=indent and :move bug END + line2 + Test fdm=indent START + line3 + line4]]) + + execute('set noai nosta') + execute('set fdm=indent') + execute('1m1') + feed('2jzc') + execute('m0') + + expect([[ + Test fdm=indent START + line3 + line4 + Test fdm=indent and :move bug END + line2]]) + end) +end) diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index 585b391198..36062ded3a 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -600,4 +600,90 @@ describe(':sort', function() eq('Vim(sort):E474: Invalid argument', eval('tmpvar')) expect(text) end) + + it('binary', function() + insert([[ + 0b111000 + 0b101100 + 0b101001 + 0b101001 + 0b101000 + 0b000000 + 0b001000 + 0b010000 + 0b101000 + 0b100000 + 0b101010 + 0b100010 + 0b100100 + 0b100010]]) + execute([[sort b]]) + expect([[ + 0b000000 + 0b001000 + 0b010000 + 0b100000 + 0b100010 + 0b100010 + 0b100100 + 0b101000 + 0b101000 + 0b101001 + 0b101001 + 0b101010 + 0b101100 + 0b111000]]) + end) + + it('binary with leading characters', function() + insert([[ + 0b100010 + 0b010000 + 0b101001 + b0b101100 + 0b100010 + 0b100100 + a0b001000 + 0b101000 + 0b101000 + a0b101001 + ab0b100000 + 0b101010 + 0b000000 + b0b111000]]) + execute([[sort b]]) + expect([[ + 0b000000 + a0b001000 + 0b010000 + ab0b100000 + 0b100010 + 0b100010 + 0b100100 + 0b101000 + 0b101000 + 0b101001 + a0b101001 + 0b101010 + b0b101100 + b0b111000]]) + end) + + it('float', function() + insert([[ + 1.234 + 0.88 + 123.456 + 1.15e-6 + -1.1e3 + -1.01e3]]) + execute([[sort f]]) + expect([[ + -1.1e3 + -1.01e3 + 1.15e-6 + 0.88 + 1.234 + 123.456]]) + end) end) diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua index 5794e875a0..63df387be3 100644 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ b/test/functional/legacy/059_utf8_spell_checking_spec.lua @@ -31,8 +31,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -92,8 +90,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -300,6 +296,24 @@ describe("spell checking with 'encoding' set to utf-8", function() tail/123 middle/77,1 ]]) + write_latin1('Xtest8.aff', [[ + SET ISO8859-1 + + NOSPLITSUGS + ]]) + write_latin1('Xtest8.dic', [[ + 1234 + foo + bar + faabar + ]]) + write_latin1('Xtest9.aff', [[ + ]]) + write_latin1('Xtest9.dic', [[ + 1234 + foo + bar + ]]) write_latin1('Xtest-sal.aff', [[ SET ISO8859-1 TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäà âöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ @@ -314,8 +328,6 @@ describe("spell checking with 'encoding' set to utf-8", function() RAR ? BAD ! - #NOSPLITSUGS - PFX I N 1 PFX I 0 in . @@ -483,6 +495,10 @@ describe("spell checking with 'encoding' set to utf-8", function() os.remove('Xtest6.dic') os.remove('Xtest7.aff') os.remove('Xtest7.dic') + os.remove('Xtest8.aff') + os.remove('Xtest8.dic') + os.remove('Xtest9.aff') + os.remove('Xtest9.dic') end) -- Function to test .aff/.dic with list of good and bad words. This was a @@ -940,4 +956,46 @@ describe("spell checking with 'encoding' set to utf-8", function() leadprobar ['leadprebar', 'lead prebar', 'leadbar']]=]) end) + + it('part 8-8', function() + insert([[ + 8good: foo bar faabar + bad: foobar barfoo + badend + ]]) + -- NOSPLITSUGS + test_one(8, 8) + -- Assert buffer contents. + execute('1,/^test 8-8/-1d') + expect([=[ + test 8-8 + # file: Xtest.utf-8.spl + bar + faabar + foo + ------- + bad + ['bar', 'foo'] + foobar + ['faabar', 'foo bar', 'bar'] + barfoo + ['bar foo', 'bar', 'foo']]=]) + end) + + it('part 9-9', function() + insert([[ + 9good: 0b1011 0777 1234 0x01ff + badend + ]]) + -- NOSPLITSUGS + test_one(9, 9) + -- Assert buffer contents. + execute('1,/^test 9-9/-1d') + expect([=[ + test 9-9 + # file: Xtest.utf-8.spl + bar + foo + -------]=]) + end) end) diff --git a/test/functional/legacy/061_undo_tree_spec.lua b/test/functional/legacy/061_undo_tree_spec.lua index ceb114b2a7..6db37bf1ff 100644 --- a/test/functional/legacy/061_undo_tree_spec.lua +++ b/test/functional/legacy/061_undo_tree_spec.lua @@ -191,7 +191,7 @@ describe('undo tree:', function() end) it('undo an expression-register', function() - local normal_commands = 'o1\x1ba2\x12=string(123)\n\x1b' + local normal_commands = 'o1\027a2\018=string(123)\n\027' write_file('Xtest.source', normal_commands) feed('oa<esc>') diff --git a/test/functional/legacy/062_tab_pages_spec.lua b/test/functional/legacy/062_tab_pages_spec.lua index 6bbb06f9a7..f1c8b8d58b 100644 --- a/test/functional/legacy/062_tab_pages_spec.lua +++ b/test/functional/legacy/062_tab_pages_spec.lua @@ -86,21 +86,35 @@ describe('tab pages', function() feed('1gt') eq(1, eval('tabpagenr()')) execute('tabmove 5') - eq(6, eval('tabpagenr()')) - execute('tabmove -2') + eq(5, eval('tabpagenr()')) + execute('.tabmove') + eq(5, eval('tabpagenr()')) + execute('tabmove -') eq(4, eval('tabpagenr()')) + execute('tabmove +') + eq(5, eval('tabpagenr()')) + execute('tabmove -2') + eq(3, eval('tabpagenr()')) execute('tabmove +4') - eq(8, eval('tabpagenr()')) + eq(7, eval('tabpagenr()')) execute('tabmove') eq(10, eval('tabpagenr()')) execute('tabmove -20') eq(1, eval('tabpagenr()')) execute('tabmove +20') eq(10, eval('tabpagenr()')) + execute('0tabmove') + eq(1, eval('tabpagenr()')) + execute('$tabmove') + eq(10, eval('tabpagenr()')) + execute('tabmove 0') + eq(1, eval('tabpagenr()')) + execute('tabmove $') + eq(10, eval('tabpagenr()')) execute('3tabmove') eq(4, eval('tabpagenr()')) execute('7tabmove 5') - eq(6, eval('tabpagenr()')) + eq(5, eval('tabpagenr()')) execute('let a="No error caught."') execute('try') execute('tabmove foo') diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index a354d4d328..23b4f4551b 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -89,7 +89,11 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( execute("call clearmatches()") execute("call setmatches(ml)") eq(ml, eval('getmatches()')) + + -- Check that "setmatches()" can correctly restore the matches from matchaddpos() execute("call clearmatches()") + execute("call setmatches(ml)") + eq(ml, eval('getmatches()')) -- Check that "setmatches()" will not add two matches with the same ID. The -- expected behaviour (for now) is to add the first match but not the diff --git a/test/functional/legacy/068_text_formatting_spec.lua b/test/functional/legacy/068_text_formatting_spec.lua new file mode 100644 index 0000000000..cac8be77f3 --- /dev/null +++ b/test/functional/legacy/068_text_formatting_spec.lua @@ -0,0 +1,207 @@ +local helpers = require('test.functional.helpers') +local feed, insert = helpers.feed, helpers.insert +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('text formatting', function() + setup(clear) + + it('is working', function() + -- The control character <C-A> (byte \x01) needs to be put in the buffer + -- directly. But the insert function sends the text to nvim in insert + -- mode so it has to be escaped with <C-V>. + insert([[ + Results of test68: + + + { + + + } + + + { + a b + + a + } + + + { + a + } + + + { + a b + #a b + } + + + { + 1 a + # 1 a + } + + + { + + x a + b + c + + } + + + { + # 1 a b + } + + + { + # x + # a b + } + + + { + 1aa + 2bb + } + + + /* abc def ghi jkl + * mno pqr stu + */ + + + # 1 xxxxx + ]]) + + execute('/^{/+1') + execute('set noai tw=2 fo=t') + feed('gRa b<esc>') + + execute('/^{/+1') + execute('set ai tw=2 fo=tw') + feed('gqgqjjllab<esc>') + + execute('/^{/+1') + execute('set tw=3 fo=t') + feed('gqgqo<cr>') + feed('a <C-V><C-A><esc><esc>') + + execute('/^{/+1') + execute('set tw=2 fo=tcq1 comments=:#') + feed('gqgqjgqgqo<cr>') + feed('a b<cr>') + feed('#a b<esc>') + + execute('/^{/+1') + execute('set tw=5 fo=tcn comments=:#') + feed('A b<esc>jA b<esc>') + + execute('/^{/+3') + execute('set tw=5 fo=t2a si') + feed('i <esc>A_<esc>') + + execute('/^{/+1') + execute('set tw=5 fo=qn comments=:#') + feed('gwap<cr>') + + execute('/^{/+1') + execute('set tw=5 fo=q2 comments=:#') + feed('gwap<cr>') + + execute('/^{/+2') + execute('set tw& fo=a') + feed('I^^<esc><esc>') + + execute('/mno pqr/') + execute('setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/') + feed('A vwx yz<esc>') + + execute('/^#/') + execute('setl tw=12 fo=tqnc comments=:#') + feed('A foobar<esc>') + + -- Assert buffer contents. + expect([[ + Results of test68: + + + { + a + b + } + + + { + a + b + + a + b + } + + + { + a + + + a + + } + + + { + a b + #a b + + a b + #a b + } + + + { + 1 a + b + # 1 a + # b + } + + + { + + x a + b_ + c + + } + + + { + # 1 a + # b + } + + + { + # x a + # b + } + + + { 1aa ^^2bb } + + + /* abc def ghi jkl + * mno pqr stu + * vwx yz + */ + + + # 1 xxxxx + # foobar + ]]) + end) +end) diff --git a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua new file mode 100644 index 0000000000..6b5ee60568 --- /dev/null +++ b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua @@ -0,0 +1,110 @@ +-- Tests for tag search with !_TAG_FILE_ENCODING. + +local helpers = require('test.functional.helpers') +local insert, source, clear, expect, write_file = helpers.insert, + helpers.source, helpers.clear, helpers.expect, helpers.write_file + +local function has_iconv() + clear() -- ensures session + return 1 == helpers.eval('has("iconv")') +end + +describe('tag search with !_TAG_FILE_ENCODING', function() + setup(function() + clear() + -- Create some temp files that are needed for the test run. In the old + -- test suite this was done by putting the text inside the file test83.in + -- and executing some "/first/,/last/w! tmpfile" commands. + write_file('Xtags1.txt', 'text for tags1\nabcdefghijklmnopqrs\n') + write_file('Xtags2.txt', 'text for tags2\nABC\n') + write_file('Xtags3.txt', 'text for tags3\nABC\n') + write_file('Xtags1', [[ + !_TAG_FILE_ENCODING utf-8 // + abcdefghijklmnopqrs Xtags1.txt /abcdefghijklmnopqrs + ]]) + write_file('test83-tags2', + '!_TAG_FILE_ENCODING cp932 //\n' .. + '\130`\130a\130b Xtags2.txt /\130`\130a\130b\n' + ) + -- The last file is very long but repetetive and can be generated on the + -- fly. + local text = helpers.dedent([[ + !_TAG_FILE_SORTED 1 // + !_TAG_FILE_ENCODING cp932 // + ]]) + local line = ' Xtags3.txt /\130`\130a\130b\n' + for i = 1, 100 do + text = text .. 'abc' .. i .. line + end + write_file('test83-tags3', text) + end) + teardown(function() + os.remove('Xtags1') + os.remove('Xtags1.txt') + os.remove('Xtags2.txt') + os.remove('Xtags3.txt') + os.remove('test83-tags2') + os.remove('test83-tags3') + end) + + if not has_iconv() then + pending('skipped (missing iconv)', function() end) + else + it('is working', function() + + insert('Results of test83') + + -- Case1: + source([[ + new + set tags=Xtags1 + let v:errmsg = '' + tag abcdefghijklmnopqrs + if v:errmsg =~ 'E426:' || getline('.') != 'abcdefghijklmnopqrs' + close + put ='case1: failed' + else + close + put ='case1: ok' + endif + ]]) + + -- Case2: + source([[ + new + set tags=test83-tags2 + let v:errmsg = '' + tag /.BC + if v:errmsg =~ 'E426:' || getline('.') != 'ABC' + close + put ='case2: failed' + else + close + put ='case2: ok' + endif + ]]) + + -- Case3: + source([[ + new + set tags=test83-tags3 + let v:errmsg = '' + tag abc50 + if v:errmsg =~ 'E426:' || getline('.') != 'ABC' + close + put ='case3: failed' + else + close + put ='case3: ok' + endif + ]]) + + -- Assert buffer contents. + expect([[ + Results of test83 + case1: ok + case2: ok + case3: ok]]) + end) + end +end) diff --git a/test/functional/legacy/088_conceal_tabs_spec.lua b/test/functional/legacy/088_conceal_tabs_spec.lua new file mode 100644 index 0000000000..c78f4e5c3e --- /dev/null +++ b/test/functional/legacy/088_conceal_tabs_spec.lua @@ -0,0 +1,96 @@ +-- Tests for correct display (cursor column position) with +conceal and +-- tabulators. + +local helpers = require('test.functional.helpers') +local feed, insert, clear, execute = + helpers.feed, helpers.insert, helpers.clear, helpers.execute + +local expect_pos = function(row, col) + return helpers.eq({row, col}, helpers.eval('[screenrow(), screencol()]')) +end + +describe('cursor and column position with conceal and tabulators', function() + setup(clear) + + it('are working', function() + insert([[ + start: + .concealed. text + |concealed| text + + .concealed. text + |concealed| text + + .a. .b. .c. .d. + |a| |b| |c| |d|]]) + + -- Conceal settings. + execute('set conceallevel=2') + execute('set concealcursor=nc') + execute('syntax match test /|/ conceal') + -- Start test. + execute('/^start:') + feed('ztj') + expect_pos(2, 1) + -- We should end up in the same column when running these commands on the + -- two lines. + feed('ft') + expect_pos(2, 17) + feed('$') + expect_pos(2, 20) + feed('0j') + expect_pos(3, 1) + feed('ft') + expect_pos(3, 17) + feed('$') + expect_pos(3, 20) + feed('j0j') + expect_pos(5, 8) + -- Same for next test block. + feed('ft') + expect_pos(5, 25) + feed('$') + expect_pos(5, 28) + feed('0j') + expect_pos(6, 8) + feed('ft') + expect_pos(6, 25) + feed('$') + expect_pos(6, 28) + feed('0j0j') + expect_pos(8, 1) + -- And check W with multiple tabs and conceals in a line. + feed('W') + expect_pos(8, 9) + feed('W') + expect_pos(8, 17) + feed('W') + expect_pos(8, 25) + feed('$') + expect_pos(8, 27) + feed('0j') + expect_pos(9, 1) + feed('W') + expect_pos(9, 9) + feed('W') + expect_pos(9, 17) + feed('W') + expect_pos(9, 25) + feed('$') + expect_pos(9, 26) + execute('set lbr') + feed('$') + expect_pos(9, 26) + execute('set list listchars=tab:>-') + feed('0') + expect_pos(9, 1) + feed('W') + expect_pos(9, 9) + feed('W') + expect_pos(9, 17) + feed('W') + expect_pos(9, 25) + feed('$') + expect_pos(9, 26) + end) +end) diff --git a/test/functional/legacy/091_context_variables_spec.lua b/test/functional/legacy/091_context_variables_spec.lua index ffeb0c657e..2c46ef643c 100644 --- a/test/functional/legacy/091_context_variables_spec.lua +++ b/test/functional/legacy/091_context_variables_spec.lua @@ -13,6 +13,9 @@ describe('context variables', function() -- Test for getbufvar(). -- Use strings to test for memory leaks. source([[ + let t:testvar='abcd' + $put =string(gettabvar(1, 'testvar')) + $put =string(gettabvar(1, 'testvar')) let b:var_num = '1234' let def_num = '5678' $put =string(getbufvar(1, 'var_num')) @@ -125,6 +128,8 @@ describe('context variables', function() -- Assert buffer contents. expect([[ start: + 'abcd' + 'abcd' '1234' '1234' {'var_num': '1234'} diff --git a/test/functional/legacy/094_visual_mode_operators_spec.lua b/test/functional/legacy/094_visual_mode_operators_spec.lua index c4aebe4ecc..4dce39b8d2 100644 --- a/test/functional/legacy/094_visual_mode_operators_spec.lua +++ b/test/functional/legacy/094_visual_mode_operators_spec.lua @@ -24,6 +24,27 @@ local function source_user_functions() ]]) end +local function put_abc() + source([[ + $put ='a' + $put ='b' + $put ='c']]) +end + +local function put_aaabbbccc() + source([[ + $put ='aaa' + $put ='bbb' + $put ='ccc']]) +end + +local function define_select_mode_maps() + source([[ + snoremap <lt>End> <End> + snoremap <lt>Down> <Down> + snoremap <lt>Del> <Del>]]) +end + describe('Visual mode and operator', function() before_each(function() clear() @@ -150,4 +171,228 @@ describe('Visual mode and operator', function() ok ok]]) end) + + describe('characterwise visual mode:', function() + it('replace last line', function() + source([[ + $put ='a' + let @" = 'x']]) + feed('v$p') + + expect([[ + + x]]) + end) + + it('delete middle line', function() + put_abc() + feed('kkv$d') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkvj$d') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('v$d') + + expect([[ + + a + b + ]]) + end) + + it('delete last two line', function() + put_abc() + feed('kvj$d') + + expect([[ + + a + ]]) + end) + end) + + describe('characterwise select mode:', function() + before_each(function() + define_select_mode_maps() + end) + + it('delete middle line', function() + put_abc() + feed('kkgh<End><Del>') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkgh<Down><End><Del>') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('gh<End><Del>') + + expect([[ + + a + b + ]]) + end) + + it('delete last two line', function() + put_abc() + feed('kgh<Down><End><Del>') + + expect([[ + + a + ]]) + end) + end) + + describe('linewise select mode:', function() + before_each(function() + define_select_mode_maps() + end) + + it('delete middle line', function() + put_abc() + feed(' kkgH<Del> ') + + expect([[ + + b + c]]) + end) + + it('delete middle two line', function() + put_abc() + feed('kkgH<Down><Del>') + + expect([[ + + c]]) + end) + + it('delete last line', function() + put_abc() + feed('gH<Del>') + + expect([[ + + a + b]]) + end) + + it('delete last two line', function() + put_abc() + feed('kgH<Down><Del>') + + expect([[ + + a]]) + end) + end) + + describe('v_p:', function() + it('replace last character with line register at middle line', function() + put_aaabbbccc() + execute('-2yank') + feed('k$vp') + + expect([[ + + aaa + bb + aaa + + ccc]]) + end) + + it('replace last character with line register at middle line selecting newline', function() + put_aaabbbccc() + execute('-2yank') + feed('k$v$p') + + expect([[ + + aaa + bb + aaa + ccc]]) + end) + + it('replace last character with line register at last line', function() + put_aaabbbccc() + execute('-2yank') + feed('$vp') + + expect([[ + + aaa + bbb + cc + aaa + ]]) + end) + + it('replace last character with line register at last line selecting newline', function() + put_aaabbbccc() + execute('-2yank') + feed('$v$p') + + expect([[ + + aaa + bbb + cc + aaa + ]]) + end) + end) + + it('gv in exclusive select mode after operation', function() + source([[ + $put ='zzz ' + $put ='äà ' + set selection=exclusive]]) + feed('kv3lyjv3lpgvcxxx<Esc>') + + expect([[ + + zzz + xxx ]]) + end) + + it('gv in exclusive select mode without operation', function() + source([[ + $put ='zzz ' + set selection=exclusive]]) + feed('0v3l<Esc>gvcxxx<Esc>') + + expect([[ + + xxx ]]) + end) end) diff --git a/test/functional/legacy/100_undo_level_spec.lua b/test/functional/legacy/100_undo_level_spec.lua deleted file mode 100644 index 3bf72341d6..0000000000 --- a/test/functional/legacy/100_undo_level_spec.lua +++ /dev/null @@ -1,142 +0,0 @@ --- Tests for 'undolevel' setting being global-local - -local helpers = require('test.functional.helpers') -local source = helpers.source -local clear, expect = helpers.clear, helpers.expect - -describe('undolevel', function() - setup(clear) - - it('is working', function() - source([[ - set ul=5 - fu! FillBuffer() - for i in range(1,13) - put=i - exe "setg ul=" . &g:ul - endfor - endfu - fu! UndoLevel() - redir @z - setglobal undolevels? - echon ' global' - setlocal undolevels? - echon ' local' - redir end - $put z - endfu - - 0put ='ONE: expecting global undolevels: 5, local undolevels: -123456 (default)' - call FillBuffer() - setlocal undolevels< - earlier 10 - call UndoLevel() - set ff=unix - %yank A - %delete - - 0put ='TWO: expecting global undolevels: 5, local undolevels: 2 (first) then 10 (afterwards)' - setlocal ul=2 - call FillBuffer() - earlier 10 - call UndoLevel() - setlocal ul=10 - call UndoLevel() - set ff=unix - %yank A - %delete - setlocal undolevels< - redir @A - echo "global value shouldn't be changed and still be 5!" - echo 'ONE: expecting global undolevels: 5, local undolevels: -123456 (default)' - setglobal undolevels? - echon ' global' - setlocal undolevels? - echon ' local' - echo "" - redir end - - setglobal ul=50 - 1put ='global value should be changed to 50' - 2put ='THREE: expecting global undolevels: 50, local undolevels: -123456 (default)' - call UndoLevel() - set ff=unix - %yank A - %delete - setglobal lispwords=foo,bar,baz - setlocal lispwords-=foo - setlocal lispwords+=quux - redir @A - echo "Testing 'lispwords' local value" - setglobal lispwords? - setlocal lispwords? - echo &lispwords - echo '' - redir end - setlocal lispwords< - redir @A - echo "Testing 'lispwords' value reset" - setglobal lispwords? - setlocal lispwords? - echo &lispwords - redir end - - 0put a - $d - ]]) - - -- Assert buffer contents. - expect([[ - ONE: expecting global undolevels: 5, local undolevels: -123456 (default) - 1 - 2 - 3 - 4 - 5 - 6 - 7 - - - undolevels=5 global - undolevels=-123456 local - TWO: expecting global undolevels: 5, local undolevels: 2 (first) then 10 (afterwards) - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - - - undolevels=5 global - undolevels=2 local - - undolevels=5 global - undolevels=10 local - - global value shouldn't be changed and still be 5! - ONE: expecting global undolevels: 5, local undolevels: -123456 (default) - undolevels=5 global - undolevels=-123456 local - - global value should be changed to 50 - THREE: expecting global undolevels: 50, local undolevels: -123456 (default) - - undolevels=50 global - undolevels=-123456 local - - Testing 'lispwords' local value - lispwords=foo,bar,baz - lispwords=bar,baz,quux - bar,baz,quux - - Testing 'lispwords' value reset - lispwords=foo,bar,baz - lispwords=foo,bar,baz - foo,bar,baz]]) - end) -end) diff --git a/test/functional/legacy/105_filename_modifiers_spec.lua b/test/functional/legacy/105_filename_modifiers_spec.lua deleted file mode 100644 index 3413667022..0000000000 --- a/test/functional/legacy/105_filename_modifiers_spec.lua +++ /dev/null @@ -1,81 +0,0 @@ --- Test filename modifiers. - -local helpers = require('test.functional.helpers') -local clear = helpers.clear -local execute, expect = helpers.execute, helpers.expect - -describe('filename modifiers', function() - setup(clear) - - it('is working', function() - local tmpdir = helpers.nvim('eval', 'resolve("/tmp")') - - execute('cd ' .. tmpdir) - execute([=[set shell=sh]=]) - execute([=[set shellslash]=]) - execute([=[let tab="\t"]=]) - execute([=[command -nargs=1 Put :let expr=<q-args> | $put =expr.tab.strtrans(string(eval(expr)))]=]) - execute([=[let $HOME=fnamemodify('.', ':p:h:h:h')]=]) - execute([=[Put fnamemodify('.', ':p' )[-1:]]=]) - execute([=[Put fnamemodify('.', ':p:h' )[-1:]]=]) - execute([=[Put fnamemodify('test.out', ':p' )[-1:]]=]) - execute([=[Put fnamemodify('test.out', ':.' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':.' )]=]) - execute([=[Put fnamemodify('test.out', ':~' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':~' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':t' )]=]) - execute([=[Put fnamemodify('.', ':p:t' )]=]) - execute([=[Put fnamemodify('test.out', ':p:t' )]=]) - execute([=[Put fnamemodify('test.out', ':p:e' )]=]) - execute([=[Put fnamemodify('test.out', ':p:t:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r:r' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r:r:r' )]=]) - execute([=[Put substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '')]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:r' )]=]) - execute([=[Put fnamemodify('abc def', ':S' )]=]) - execute([=[Put fnamemodify('abc" "def', ':S' )]=]) - execute([=[Put fnamemodify('abc"%"def', ':S' )]=]) - execute([=[Put fnamemodify('abc'' ''def', ':S' )]=]) - execute([=[Put fnamemodify('abc''%''def', ':S' )]=]) - execute([=[Put fnamemodify("abc\ndef", ':S' )]=]) - execute([=[set shell=tcsh]=]) - execute([=[Put fnamemodify("abc\ndef", ':S' )]=]) - execute([=[1 delete _]=]) - - -- Assert buffer contents. - expect([=[ - fnamemodify('.', ':p' )[-1:] '/' - fnamemodify('.', ':p:h' )[-1:] 'p' - fnamemodify('test.out', ':p' )[-1:] 't' - fnamemodify('test.out', ':.' ) 'test.out' - fnamemodify('../testdir/a', ':.' ) '../testdir/a' - fnamemodify('test.out', ':~' ) 'test.out' - fnamemodify('../testdir/a', ':~' ) '../testdir/a' - fnamemodify('../testdir/a', ':t' ) 'a' - fnamemodify('.', ':p:t' ) '' - fnamemodify('test.out', ':p:t' ) 'test.out' - fnamemodify('test.out', ':p:e' ) 'out' - fnamemodify('test.out', ':p:t:e' ) 'out' - fnamemodify('abc.fb2.tar.gz', ':r' ) 'abc.fb2.tar' - fnamemodify('abc.fb2.tar.gz', ':r:r' ) 'abc.fb2' - fnamemodify('abc.fb2.tar.gz', ':r:r:r' ) 'abc' - substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '') ']=] .. tmpdir .. [=[/abc.fb2' - fnamemodify('abc.fb2.tar.gz', ':e' ) 'gz' - fnamemodify('abc.fb2.tar.gz', ':e:e' ) 'tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:e' ) 'fb2.tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:e:e') 'fb2.tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:r' ) 'tar' - fnamemodify('abc def', ':S' ) '''abc def''' - fnamemodify('abc" "def', ':S' ) '''abc" "def''' - fnamemodify('abc"%"def', ':S' ) '''abc"%"def''' - fnamemodify('abc'' ''def', ':S' ) '''abc''\'''' ''\''''def''' - fnamemodify('abc''%''def', ':S' ) '''abc''\''''%''\''''def''' - fnamemodify("abc\ndef", ':S' ) '''abc^@def''' - fnamemodify("abc\ndef", ':S' ) '''abc\^@def''']=]) - end) -end) diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua new file mode 100644 index 0000000000..1ce665360d --- /dev/null +++ b/test/functional/legacy/assert_spec.lua @@ -0,0 +1,145 @@ +local helpers = require('test.functional.helpers') +local nvim, call = helpers.meths, helpers.call +local clear, eq = helpers.clear, helpers.eq +local source, execute = helpers.source, helpers.execute + +local function expected_errors(errors) + eq(errors, nvim.get_vvar('errors')) +end + +local function expected_empty() + eq({}, nvim.get_vvar('errors')) +end + +describe('assert function:', function() + + before_each(function() + clear() + end) + + -- assert_equal({expected}, {actual}, [, {msg}]) + describe('assert_equal', function() + it('should not change v:errors when expected is equal to actual', function() + source([[ + let s = 'foo' + call assert_equal('foo', s) + let n = 4 + call assert_equal(4, n) + let l = [1, 2, 3] + call assert_equal([1, 2, 3], l) + fu Func() + endfu + let F1 = function('Func') + let F2 = function('Func') + call assert_equal(F1, F2) + ]]) + expected_empty() + end) + + it('should not change v:errors when expected is equal to actual', function() + call('assert_equal', '', '') + call('assert_equal', 'string', 'string') + expected_empty() + end) + + it('should change v:errors when expected is not equal to actual', function() + call('assert_equal', 0, {0}) + expected_errors({'Expected 0 but got [0]'}) + end) + + it('should change v:errors when expected is not equal to actual', function() + call('assert_equal', 0, "0") + expected_errors({"Expected 0 but got '0'"}) + end) + + it('should change v:errors when expected is not equal to actual', function() + -- Lua does not tell integer from float. + execute('call assert_equal(1, 1.0)') + expected_errors({'Expected 1 but got 1.0'}) + end) + + it('should change v:errors when expected is not equal to actual', function() + call('assert_equal', 'true', 'false') + expected_errors({"Expected 'true' but got 'false'"}) + end) + end) + + -- assert_false({actual}, [, {msg}]) + describe('assert_false', function() + it('should not change v:errors when actual is false', function() + call('assert_false', 0) + call('assert_false', false) + expected_empty() + end) + + it('should change v:errors when actual is not false', function() + call('assert_false', 1) + expected_errors({'Expected False but got 1'}) + end) + + it('should change v:errors when actual is not false', function() + call('assert_false', {}) + expected_errors({'Expected False but got []'}) + end) + end) + + -- assert_true({actual}, [, {msg}]) + describe('assert_true', function() + it('should not change v:errors when actual is true', function() + call('assert_true', 1) + call('assert_true', -1) -- In Vim script, non-zero Numbers are TRUE. + call('assert_true', true) + expected_empty() + end) + + it('should change v:errors when actual is not true', function() + call('assert_true', 1.5) + expected_errors({'Expected True but got 1.5'}) + end) + end) + + describe('v:errors', function() + it('should be initialized at startup', function() + expected_empty() + end) + + it('should have function names and relative line numbers', function() + source([[ + fu Func_one() + call assert_equal([0], {'0' : 0}) + call assert_false('False') + call assert_true("True") + endfu + fu Func_two() + " for shifting a line number + call assert_true('line two') + endfu + ]]) + call('Func_one') + call('Func_two') + expected_errors({ + "function Func_one line 1: Expected [0] but got {'0': 0}", + "function Func_one line 2: Expected False but got 'False'", + "function Func_one line 3: Expected True but got 'True'", + "function Func_two line 2: Expected True but got 'line two'", + }) + end) + + it('should have file names and passed messages', function() + local tmpname_one = source([[ + call assert_equal(1, 100, 'equal assertion failed') + call assert_false('true', 'true assertion failed') + call assert_true('false', 'false assertion failed') + ]]) + local tmpname_two = source([[ + call assert_true('', 'file two') + ]]) + expected_errors({ + tmpname_one .. " line 1: 'equal assertion failed'", + tmpname_one .. " line 2: 'true assertion failed'", + tmpname_one .. " line 3: 'false assertion failed'", + tmpname_two .. " line 1: 'file two'", + }) + end) + end) +end) diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 855e9c6271..6349371808 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers') local nvim = helpers.meths local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq local curbuf, buf = helpers.curbuf, helpers.bufmeths +local curwin = helpers.curwin +local redir_exec = helpers.redir_exec local source, execute = helpers.source, helpers.execute local function declare_hook_function() @@ -86,7 +88,7 @@ end local function make_buffer() local old_buf = curbuf() - execute('new') + execute('botright new') local new_buf = curbuf() execute('wincmd p') -- move previous window @@ -96,6 +98,19 @@ local function make_buffer() return new_buf end +local function get_new_window_number() + local old_win = curwin() + execute('botright new') + local new_win = curwin() + local new_winnr = redir_exec('echo winnr()') + execute('wincmd p') -- move previous window + + neq(old_win, new_win) + eq(old_win, curwin()) + + return new_winnr:gsub('\n', '') +end + describe('au OptionSet', function() describe('with any opton (*)', function() @@ -248,6 +263,32 @@ describe('au OptionSet', function() end) end) + describe('being set by setwinvar()', function() + it('should not trigger because option name does not match with backup', function() + set_hook('backup') + + execute('call setwinvar(1, "&l:bk", 1)') + expected_empty() + end) + + it('should trigger, use correct option name backup', function() + set_hook('backup') + + execute('call setwinvar(1, "&backup", 1)') + expected_combination({'backup', 0, 1, 'local'}) + end) + + it('should not trigger if the current window is different from the targetted window', function() + set_hook('cursorcolumn') + + local new_winnr = get_new_window_number() + + execute('call setwinvar(' .. new_winnr .. ', "&cursorcolumn", 1)') + -- expected_combination({'cursorcolumn', 0, 1, 'local', {winnr = new_winnr}}) + expected_empty() + end) + end) + describe('being set by neovim api', function() it('should trigger if a boolean option be set globally', function() set_hook('autochdir') diff --git a/test/functional/legacy/backspace_opt_spec.lua b/test/functional/legacy/backspace_opt_spec.lua new file mode 100644 index 0000000000..b40019a410 --- /dev/null +++ b/test/functional/legacy/backspace_opt_spec.lua @@ -0,0 +1,67 @@ +local helpers = require('test.functional.helpers') +local call, clear = helpers.call, helpers.clear +local source, eq, nvim = helpers.source, helpers.eq, helpers.meths + +describe("test 'backspace' settings", function() + before_each(function() + clear() + + source([[ + func Exec(expr) + let str='' + try + exec a:expr + catch /.*/ + let str=v:exception + endtry + return str + endfunc + + func Test_backspace_option() + set backspace= + call assert_equal('', &backspace) + set backspace=indent + call assert_equal('indent', &backspace) + set backspace=eol + call assert_equal('eol', &backspace) + set backspace=start + call assert_equal('start', &backspace) + " Add the value + set backspace= + set backspace=indent + call assert_equal('indent', &backspace) + set backspace+=eol + call assert_equal('indent,eol', &backspace) + set backspace+=start + call assert_equal('indent,eol,start', &backspace) + " Delete the value + set backspace-=indent + call assert_equal('eol,start', &backspace) + set backspace-=start + call assert_equal('eol', &backspace) + set backspace-=eol + call assert_equal('', &backspace) + " Check the error + call assert_equal(0, match(Exec('set backspace=ABC'), '.*E474')) + call assert_equal(0, match(Exec('set backspace+=def'), '.*E474')) + " NOTE: Vim doesn't check following error... + "call assert_equal(0, match(Exec('set backspace-=ghi'), '.*E474')) + + " Check backwards compatibility with version 5.4 and earlier + set backspace=0 + call assert_equal('0', &backspace) + set backspace=1 + call assert_equal('1', &backspace) + set backspace=2 + call assert_equal('2', &backspace) + call assert_false(match(Exec('set backspace=3'), '.*E474')) + call assert_false(match(Exec('set backspace=10'), '.*E474')) + endfunc + ]]) + end) + + it('works', function() + call('Test_backspace_option') + eq({}, nvim.get_vvar('errors')) + end) +end) diff --git a/test/functional/legacy/cdo_spec.lua b/test/functional/legacy/cdo_spec.lua new file mode 100644 index 0000000000..4b313ede3f --- /dev/null +++ b/test/functional/legacy/cdo_spec.lua @@ -0,0 +1,226 @@ +-- Tests for the :cdo, :cfdo, :ldo and :lfdo commands + +local helpers = require('test.functional.helpers') +local nvim, clear = helpers.meths, helpers.clear +local call, feed = helpers.call, helpers.feed +local source, eq = helpers.source, helpers.eq + +local function expected_empty() + eq({}, nvim.get_vvar('errors')) +end + +describe('cdo', function() + before_each(function() + clear() + + call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile1') + call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile2') + call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile3') + + source([=[ + " Returns the current line in '<filename> <linenum>L <column>C' format + function GetRuler() + return expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' + endfunction + + " Tests for the :cdo and :ldo commands + function XdoTests(cchar) + enew + + " Shortcuts for calling the cdo and ldo commands + let Xdo = a:cchar . 'do' + let Xgetexpr = a:cchar . 'getexpr' + let Xprev = a:cchar. 'prev' + let XdoCmd = Xdo . ' call add(l, GetRuler())' + + " Try with an empty list + let l = [] + exe XdoCmd + call assert_equal([], l) + + " Populate the list and then try + exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:3:1:Line3']" + + let l = [] + exe XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + " Run command only on selected error lines + let l = [] + enew + exe "2,3" . XdoCmd + call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + " Boundary condition tests + let l = [] + enew + exe "1,1" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C'], l) + + let l = [] + enew + exe "3" . XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + " Range test commands + let l = [] + enew + exe "%" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + let l = [] + enew + exe "1,$" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + let l = [] + enew + exe Xprev + exe "." . XdoCmd + call assert_equal(['Xtestfile2 2L 2C'], l) + + let l = [] + enew + exe "+" . XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + " Invalid error lines test + let l = [] + enew + exe "silent! 27" . XdoCmd + exe "silent! 4,5" . XdoCmd + call assert_equal([], l) + + " Run commands from an unsaved buffer + let v:errmsg='' + let l = [] + enew + setlocal modified + exe "silent! 2,2" . XdoCmd + if v:errmsg !~# 'No write since last change' + call add(v:errors, 'Unsaved file change test failed') + endif + + " If the executed command fails, then the operation should be aborted + enew! + let subst_count = 0 + exe "silent!" . Xdo . " s/Line/xLine/ | let subst_count += 1" + if subst_count != 1 || getline('.') != 'xLine1' + call add(v:errors, 'Abort command on error test failed') + endif + + let l = [] + exe "2,2" . Xdo . "! call add(l, GetRuler())" + call assert_equal(['Xtestfile2 2L 2C'], l) + + " List with no valid error entries + let l = [] + edit! +2 Xtestfile1 + exe Xgetexpr . " ['non-error 1', 'non-error 2', 'non-error 3']" + exe XdoCmd + call assert_equal([], l) + exe "silent! 2" . XdoCmd + call assert_equal([], l) + let v:errmsg='' + exe "%" . XdoCmd + exe "1,$" . XdoCmd + exe "." . XdoCmd + call assert_equal('', v:errmsg) + + " List with only one valid entry + let l = [] + exe Xgetexpr . " ['Xtestfile3:3:1:Line3']" + exe XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + endfunction + + " Tests for the :cfdo and :lfdo commands + function XfdoTests(cchar) + enew + + " Shortcuts for calling the cfdo and lfdo commands + let Xfdo = a:cchar . 'fdo' + let Xgetexpr = a:cchar . 'getexpr' + let XfdoCmd = Xfdo . ' call add(l, GetRuler())' + let Xpfile = a:cchar. 'pfile' + + " Clear the quickfix/location list + exe Xgetexpr . " []" + + " Try with an empty list + let l = [] + exe XfdoCmd + call assert_equal([], l) + + " Populate the list and then try + exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'Xtestfile1:2:1:Line2', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:2:3:Line2', 'Xtestfile3:3:1:Line3']" + + let l = [] + exe XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + " Run command only on selected error lines + let l = [] + exe "2,3" . XfdoCmd + call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + " Boundary condition tests + let l = [] + exe "3" . XfdoCmd + call assert_equal(['Xtestfile3 2L 3C'], l) + + " Range test commands + let l = [] + exe "%" . XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + let l = [] + exe "1,$" . XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + let l = [] + exe Xpfile + exe "." . XfdoCmd + call assert_equal(['Xtestfile2 2L 2C'], l) + + " List with only one valid entry + let l = [] + exe Xgetexpr . " ['Xtestfile2:2:5:Line2']" + exe XfdoCmd + call assert_equal(['Xtestfile2 2L 5C'], l) + + endfunction + ]=]) + end) + + after_each(function() + os.remove('Xtestfile1') + os.remove('Xtestfile2') + os.remove('Xtestfile3') + end) + + it('works for :cdo', function() + -- call('XdoTests', 'c') + feed(":call XdoTests('c')<CR><C-l>") + expected_empty() + end) + + it('works for :cfdo', function() + -- call('XfdoTests', 'c') + feed(":call XfdoTests('c')<CR><C-l>") + expected_empty() + end) + + it('works for :ldo', function() + -- call('XdoTests', 'l') + feed(":call XdoTests('l')<CR><C-l>") + expected_empty() + end) + + it('works for :lfdo', function() + -- call('XfdoTests', 'l') + feed(":call XfdoTests('l')<CR><C-l>") + expected_empty() + end) +end) diff --git a/test/functional/legacy/charsearch_spec.lua b/test/functional/legacy/charsearch_spec.lua new file mode 100644 index 0000000000..4a83801cfc --- /dev/null +++ b/test/functional/legacy/charsearch_spec.lua @@ -0,0 +1,42 @@ +-- Test for character searches + +local helpers = require('test.functional.helpers') +local feed, insert = helpers.feed, helpers.insert +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('charsearch', function() + setup(clear) + + it('is working', function() + insert([[ + Xabcdefghijkemnopqretuvwxyz + Yabcdefghijkemnopqretuvwxyz + Zabcdefghijkemnokqretkvwxyz]]) + + -- Check that "fe" and ";" work. + execute('/^X') + feed('ylfep;;p,,p') + -- Check that save/restore works. + execute('/^Y') + feed('ylfep') + execute('let csave = getcharsearch()') + feed('fip') + execute('call setcharsearch(csave)') + feed(';p;p') + -- Check that setcharsearch() changes the settings. + execute('/^Z') + feed('ylfep') + execute("call setcharsearch({'char': 'k'})") + feed(';p') + execute("call setcharsearch({'forward': 0})") + feed('$;p') + execute("call setcharsearch({'until': 1})") + feed(';;p') + + -- Assert buffer contents. + expect([[ + XabcdeXfghijkeXmnopqreXtuvwxyz + YabcdeYfghiYjkeYmnopqreYtuvwxyz + ZabcdeZfghijkZZemnokqretkZvwxyz]]) + end) +end) diff --git a/test/functional/legacy/command_count_spec.lua b/test/functional/legacy/command_count_spec.lua new file mode 100644 index 0000000000..d9b4f09263 --- /dev/null +++ b/test/functional/legacy/command_count_spec.lua @@ -0,0 +1,243 @@ +-- Test for user command counts + +local helpers = require('test.functional.helpers') +local clear, source, expect = helpers.clear, helpers.source, helpers.expect +local execute, spawn = helpers.execute, helpers.spawn +local nvim_prog = helpers.nvim_prog + +describe('command_count', function() + setup(clear) + teardown(function() + os.remove('test.out') + end) + + it('is working', function() + -- It is relevant for the test to load a file initially. If this is + -- emulated with :arg the buffer count is wrong as nvim creates an empty + -- buffer if it was started without a filename. + local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', + 'test_command_count.in'}) + helpers.set_session(nvim2) + + source([[ + lang C + let g:lines = [] + com -range=% RangeLines + \ :call add(g:lines, 'RangeLines '.<line1>.' '.<line2>) + com -range -addr=arguments RangeArguments + \ :call add(g:lines, 'RangeArguments '.<line1>.' '.<line2>) + com -range=% -addr=arguments RangeArgumentsAll + \ :call add(g:lines, 'RangeArgumentsAll '.<line1>.' '.<line2>) + com -range -addr=loaded_buffers RangeLoadedBuffers + \ :call add(g:lines, 'RangeLoadedBuffers '.<line1>.' '.<line2>) + com -range=% -addr=loaded_buffers RangeLoadedBuffersAll + \ :call add(g:lines, 'RangeLoadedBuffersAll '.<line1>.' '.<line2>) + com -range -addr=buffers RangeBuffers + \ :call add(g:lines, 'RangeBuffers '.<line1>.' '.<line2>) + com -range=% -addr=buffers RangeBuffersAll + \ :call add(g:lines, 'RangeBuffersAll '.<line1>.' '.<line2>) + com -range -addr=windows RangeWindows + \ :call add(g:lines, 'RangeWindows '.<line1>.' '.<line2>) + com -range=% -addr=windows RangeWindowsAll + \ :call add(g:lines, 'RangeWindowsAll '.<line1>.' '.<line2>) + com -range -addr=tabs RangeTabs + \ :call add(g:lines, 'RangeTabs '.<line1>.' '.<line2>) + com -range=% -addr=tabs RangeTabsAll + \ :call add(g:lines, 'RangeTabsAll '.<line1>.' '.<line2>) + set hidden + arga a b c d + argdo echo "loading buffers" + argu 3 + .-,$-RangeArguments + %RangeArguments + RangeArgumentsAll + N + .RangeArguments + split + split + split + split + 3wincmd w + .,$RangeWindows + %RangeWindows + RangeWindowsAll + only + blast + bd + .,$RangeLoadedBuffers + %RangeLoadedBuffers + RangeLoadedBuffersAll + .,$RangeBuffers + %RangeBuffers + RangeBuffersAll + tabe + tabe + tabe + tabe + normal 2gt + .,$RangeTabs + %RangeTabs + RangeTabsAll + 1tabonly + s/\n/\r\r\r\r\r/ + 2ma< + $-ma> + '<,'>RangeLines + com -range=% -buffer LocalRangeLines + \ :call add(g:lines, 'LocalRangeLines '.<line1>.' '.<line2>) + '<,'>LocalRangeLines + b1 + call add(g:lines, '') + %argd + arga a b c d + ]]) + -- This can not be in the source() call as it will produce errors. + execute([[let v:errmsg = '']]) + execute('5argu') + execute([[call add(g:lines, '5argu ' . v:errmsg)]]) + execute('$argu') + execute([[call add(g:lines, '4argu ' . expand('%:t'))]]) + execute([[let v:errmsg = '']]) + execute('1argu') + execute([[call add(g:lines, '1argu ' . expand('%:t'))]]) + execute([[let v:errmsg = '']]) + execute('100b') + execute([[call add(g:lines, '100b ' . v:errmsg)]]) + execute('split') + execute('split') + execute('split') + execute('split') + execute([[let v:errmsg = '']]) + execute('0close') + execute([[call add(g:lines, '0close ' . v:errmsg)]]) + execute('$wincmd w') + execute('$close') + execute([[call add(g:lines, '$close ' . winnr())]]) + execute([[let v:errmsg = '']]) + execute('$+close') + execute([[call add(g:lines, '$+close ' . v:errmsg)]]) + execute('$tabe') + execute([[call add(g:lines, '$tabe ' . tabpagenr())]]) + execute([[let v:errmsg = '']]) + execute('$+tabe') + execute([[call add(g:lines, '$+tabe ' . v:errmsg)]]) + source([[ + only! + e x + 0tabm + normal 1gt + call add(g:lines, '0tabm ' . expand('%:t')) + tabonly! + only! + e! test.out + call append(0, g:lines) + unlet g:lines + w + bd + b1 + let g:lines = [] + func BufStatus() + call add(g:lines, + \ 'aaa: ' . buflisted(g:buf_aaa) . + \ ' bbb: ' . buflisted(g:buf_bbb) . + \ ' ccc: ' . buflisted(g:buf_ccc)) + endfunc + se nohidden + e aaa + let buf_aaa = bufnr('%') + e bbb + let buf_bbb = bufnr('%') + e ccc + let buf_ccc = bufnr('%') + b1 + call BufStatus() + exe buf_bbb . "," . buf_ccc . "bdelete" + call BufStatus() + exe buf_aaa . "bdelete" + call BufStatus() + e! test.out + call append('$', g:lines) + unlet g:lines + delfunc BufStatus + w + bd + b1 + se hidden + only! + let g:lines = [] + %argd + arga a b c d e f + 3argu + let args = '' + .,$-argdo let args .= ' '.expand('%') + call add(g:lines, 'argdo:' . args) + split + split + split + split + 2wincmd w + let windows = '' + .,$-windo let windows .= ' '.winnr() + call add(g:lines, 'windo:'. windows) + b2 + let buffers = '' + .,$-bufdo let buffers .= ' '.bufnr('%') + call add(g:lines, 'bufdo:' . buffers) + 3bd + let buffers = '' + 3,7bufdo let buffers .= ' '.bufnr('%') + call add(g:lines, 'bufdo:' . buffers) + tabe + tabe + tabe + tabe + normal! 2gt + let tabpages = '' + .,$-tabdo let tabpages .= ' '.tabpagenr() + call add(g:lines, 'tabdo:' . tabpages) + e! test.out + call append('$', g:lines) + ]]) + + -- Assert buffer contents. + expect([[ + RangeArguments 2 4 + RangeArguments 1 5 + RangeArgumentsAll 1 5 + RangeArguments 2 2 + RangeWindows 3 5 + RangeWindows 1 5 + RangeWindowsAll 1 5 + RangeLoadedBuffers 2 4 + RangeLoadedBuffers 1 4 + RangeLoadedBuffersAll 1 4 + RangeBuffers 2 5 + RangeBuffers 1 5 + RangeBuffersAll 1 5 + RangeTabs 2 5 + RangeTabs 1 5 + RangeTabsAll 1 5 + RangeLines 2 5 + LocalRangeLines 2 5 + + 5argu E16: Invalid range + 4argu d + 1argu a + 100b E16: Invalid range + 0close + $close 3 + $+close E16: Invalid range + $tabe 2 + $+tabe E16: Invalid range + 0tabm x + + aaa: 1 bbb: 1 ccc: 1 + aaa: 1 bbb: 0 ccc: 0 + aaa: 0 bbb: 0 ccc: 0 + argdo: c d e + windo: 2 3 4 + bufdo: 2 3 4 5 6 7 8 9 10 15 + bufdo: 4 5 6 7 + tabdo: 2 3 4]]) + end) +end) diff --git a/test/functional/legacy/comparators_spec.lua b/test/functional/legacy/comparators_spec.lua new file mode 100644 index 0000000000..e3fa3eea23 --- /dev/null +++ b/test/functional/legacy/comparators_spec.lua @@ -0,0 +1,14 @@ +-- " Test for expression comparators. + +local helpers = require('test.functional.helpers') +local clear, eq = helpers.clear, helpers.eq +local eval, execute = helpers.eval, helpers.execute + +describe('comparators', function() + before_each(clear) + + it('is working', function() + execute('set isident+=#') + eq(1, eval('1 is#1')) + end) +end) diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua new file mode 100644 index 0000000000..cd18a8f750 --- /dev/null +++ b/test/functional/legacy/delete_spec.lua @@ -0,0 +1,99 @@ +local helpers = require('test.functional.helpers') +local clear, source = helpers.clear, helpers.source +local eq, eval, execute = helpers.eq, helpers.eval, helpers.execute + +describe('Test for delete()', function() + before_each(clear) + + it('file delete', function() + execute('split Xfile') + execute("call setline(1, ['a', 'b'])") + execute('wq') + eq(eval("['a', 'b']"), eval("readfile('Xfile')")) + eq(0, eval("delete('Xfile')")) + eq(-1, eval("delete('Xfile')")) + end) + + it('directory delete', function() + execute("call mkdir('Xdir1')") + eq(1, eval("isdirectory('Xdir1')")) + eq(0, eval("delete('Xdir1', 'd')")) + eq(0, eval("isdirectory('Xdir1')")) + eq(-1, eval("delete('Xdir1', 'd')")) + end) + it('recursive delete', function() + execute("call mkdir('Xdir1')") + execute("call mkdir('Xdir1/subdir')") + execute("call mkdir('Xdir1/empty')") + execute('split Xdir1/Xfile') + execute("call setline(1, ['a', 'b'])") + execute('w') + execute('w Xdir1/subdir/Xfile') + execute('close') + + eq(1, eval("isdirectory('Xdir1')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir1/Xfile')")) + eq(1, eval("isdirectory('Xdir1/subdir')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir1/subdir/Xfile')")) + eq(1, eval("isdirectory('Xdir1/empty')")) + eq(0, eval("delete('Xdir1', 'rf')")) + eq(0, eval("isdirectory('Xdir1')")) + eq(-1, eval("delete('Xdir1', 'd')")) + end) + + it('symlink delete', function() + source([[ + split Xfile + call setline(1, ['a', 'b']) + wq + silent !ln -s Xfile Xlink + ]]) + -- Delete the link, not the file + eq(0, eval("delete('Xlink')")) + eq(-1, eval("delete('Xlink')")) + eq(0, eval("delete('Xfile')")) + end) + + it('symlink directory delete', function() + execute("call mkdir('Xdir1')") + execute("silent !ln -s Xdir1 Xlink") + eq(1, eval("isdirectory('Xdir1')")) + eq(1, eval("isdirectory('Xlink')")) + -- Delete the link, not the directory + eq(0, eval("delete('Xlink')")) + eq(-1, eval("delete('Xlink')")) + eq(0, eval("delete('Xdir1', 'd')")) + end) + + it('symlink recursive delete', function() + source([[ + call mkdir('Xdir3') + call mkdir('Xdir3/subdir') + call mkdir('Xdir4') + split Xdir3/Xfile + call setline(1, ['a', 'b']) + w + w Xdir3/subdir/Xfile + w Xdir4/Xfile + close + silent !ln -s ../Xdir4 Xdir3/Xlink + ]]) + + eq(1, eval("isdirectory('Xdir3')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir3/Xfile')")) + eq(1, eval("isdirectory('Xdir3/subdir')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir3/subdir/Xfile')")) + eq(1, eval("isdirectory('Xdir4')")) + eq(1, eval("isdirectory('Xdir3/Xlink')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) + + eq(0, eval("delete('Xdir3', 'rf')")) + eq(0, eval("isdirectory('Xdir3')")) + eq(-1, eval("delete('Xdir3', 'd')")) + -- symlink is deleted, not the directory it points to + eq(1, eval("isdirectory('Xdir4')")) + eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) + eq(0, eval("delete('Xdir4/Xfile')")) + eq(0, eval("delete('Xdir4', 'd')")) + end) +end) diff --git a/test/functional/legacy/erasebackword_spec.lua b/test/functional/legacy/erasebackword_spec.lua new file mode 100644 index 0000000000..cb3967b763 --- /dev/null +++ b/test/functional/legacy/erasebackword_spec.lua @@ -0,0 +1,24 @@ +-- Test for CTRL-W in Insert mode + +local helpers = require('test.functional.helpers') +local clear, feed, expect = helpers.clear, helpers.feed, helpers.expect + +describe('CTRL-W in Insert mode', function() + setup(clear) + + it('works for multi-byte characters', function() + + for i = 1, 6 do + feed('o wwwã“ã‚“ã«ã¡ã‚世界ワールドvim ' .. string.rep('<C-w>', i) .. '<esc>') + end + + expect([[ + + wwwã“ã‚“ã«ã¡ã‚世界ワールド + wwwã“ã‚“ã«ã¡ã‚世界 + wwwã“ã‚“ã«ã¡ã‚ + www + + ]]) + end) +end) diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua new file mode 100644 index 0000000000..3ff1092a4b --- /dev/null +++ b/test/functional/legacy/eval_spec.lua @@ -0,0 +1,736 @@ +-- Test for various eval features. + +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect +local eq, eval, write_file = helpers.eq, helpers.eval, helpers.write_file + +local function has_clipboard() + clear() + return 1 == eval("has('clipboard')") +end + +describe('eval', function() + setup(function() + write_file('test_eval_setup.vim', [[ + set noswapfile + lang C + + fun AppendRegContents(reg) + call AppendRegParts(a:reg, getregtype(a:reg), getreg(a:reg), string(getreg(a:reg, 0, 1)), getreg(a:reg, 1), string(getreg(a:reg, 1, 1))) + endfun + + fun AppendRegParts(reg, type, cont, strcont, cont1, strcont1) + call append('$', printf('%s: type %s; value: %s (%s), expr: %s (%s)', a:reg, a:type, a:cont, a:strcont, a:cont1, a:strcont1)) + endfun + + command -nargs=? AR :call AppendRegContents(<q-args>) + + fun SetReg(...) + call call('setreg', a:000) + call append('$', printf('{{{2 setreg(%s)', string(a:000)[1:-2])) + call AppendRegContents(a:1) + if a:1 isnot# '=' + execute "silent normal! Go==\n==\e\"".a:1."P" + endif + endfun + ]]) + end) + before_each(clear) + teardown(function() + os.remove('test_eval_setup.vim') + end) + + it(':let', function() + execute('so test_eval_setup.vim') + execute([[let @" = 'abc']]) + execute('AR "') + execute([[let @" = "abc\n"]]) + source('AR "') + execute([[let @" = "abc\<C-m>"]]) + execute('AR "') + execute([[let @= = '"abc"']]) + execute('AR =') + expect([[ + + ": type v; value: abc (['abc']), expr: abc (['abc']) + ": type V; value: abc]].."\000 (['abc']), expr: abc\000"..[[ (['abc']) + ": type V; value: abc]].."\r\000 (['abc\r']), expr: abc\r\000 (['abc\r"..[[']) + =: type v; value: abc (['abc']), expr: "abc" (['"abc"'])]]) + end) + + it('basic setreg() tests', function() + execute('so test_eval_setup.vim') + insert('{{{1 Basic setreg tests') + execute([[call SetReg('a', 'abcA', 'c')]]) + execute([[call SetReg('b', 'abcB', 'v')]]) + execute([[call SetReg('c', 'abcC', 'l')]]) + execute([[call SetReg('d', 'abcD', 'V')]]) + execute([[call SetReg('e', 'abcE', 'b')]]) + execute([[call SetReg('f', 'abcF', "\<C-v>")]]) + execute([[call SetReg('g', 'abcG', 'b10')]]) + execute([[call SetReg('h', 'abcH', "\<C-v>10")]]) + execute([[call SetReg('I', 'abcI')]]) + + feed('Go{{{1 Appending single lines with setreg()<esc>') + execute([[call SetReg('A', 'abcAc', 'c')]]) + execute([[call SetReg('A', 'abcAl', 'l')]]) + execute([[call SetReg('A', 'abcAc2','c')]]) + execute([[call SetReg('b', 'abcBc', 'ca')]]) + execute([[call SetReg('b', 'abcBb', 'ba')]]) + execute([[call SetReg('b', 'abcBc2','ca')]]) + execute([[call SetReg('b', 'abcBb2','b50a')]]) + execute([[call SetReg('C', 'abcCl', 'l')]]) + execute([[call SetReg('C', 'abcCc', 'c')]]) + execute([[call SetReg('D', 'abcDb', 'b')]]) + execute([[call SetReg('E', 'abcEb', 'b')]]) + execute([[call SetReg('E', 'abcEl', 'l')]]) + execute([[call SetReg('F', 'abcFc', 'c')]]) + expect([[ + {{{1 Basic setreg tests + {{{2 setreg('a', 'abcA', 'c') + a: type v; value: abcA (['abcA']), expr: abcA (['abcA']) + == + =abcA= + {{{2 setreg('b', 'abcB', 'v') + b: type v; value: abcB (['abcB']), expr: abcB (['abcB']) + == + =abcB= + {{{2 setreg('c', 'abcC', 'l') + c: type V; value: abcC]].."\000 (['abcC']), expr: abcC\000"..[[ (['abcC']) + == + abcC + == + {{{2 setreg('d', 'abcD', 'V') + d: type V; value: abcD]].."\000 (['abcD']), expr: abcD\000"..[[ (['abcD']) + == + abcD + == + {{{2 setreg('e', 'abcE', 'b') + e: type ]]..'\022'..[[4; value: abcE (['abcE']), expr: abcE (['abcE']) + == + =abcE= + {{{2 setreg('f', 'abcF', ']]..'\022'..[[') + f: type ]]..'\022'..[[4; value: abcF (['abcF']), expr: abcF (['abcF']) + == + =abcF= + {{{2 setreg('g', 'abcG', 'b10') + g: type ]]..'\022'..[[10; value: abcG (['abcG']), expr: abcG (['abcG']) + == + =abcG = + {{{2 setreg('h', 'abcH', ']]..'\022'..[[10') + h: type ]]..'\022'..[[10; value: abcH (['abcH']), expr: abcH (['abcH']) + == + =abcH = + {{{2 setreg('I', 'abcI') + I: type v; value: abcI (['abcI']), expr: abcI (['abcI']) + == + =abcI= + {{{1 Appending single lines with setreg() + {{{2 setreg('A', 'abcAc', 'c') + A: type v; value: abcAabcAc (['abcAabcAc']), expr: abcAabcAc (['abcAabcAc']) + == + =abcAabcAc= + {{{2 setreg('A', 'abcAl', 'l') + A: type V; value: abcAabcAcabcAl]].."\000 (['abcAabcAcabcAl']), expr: abcAabcAcabcAl\000"..[[ (['abcAabcAcabcAl']) + == + abcAabcAcabcAl + == + {{{2 setreg('A', 'abcAc2', 'c') + A: type v; value: abcAabcAcabcAl]].."\000abcAc2 (['abcAabcAcabcAl', 'abcAc2']), expr: abcAabcAcabcAl\000"..[[abcAc2 (['abcAabcAcabcAl', 'abcAc2']) + == + =abcAabcAcabcAl + abcAc2= + {{{2 setreg('b', 'abcBc', 'ca') + b: type v; value: abcBabcBc (['abcBabcBc']), expr: abcBabcBc (['abcBabcBc']) + == + =abcBabcBc= + {{{2 setreg('b', 'abcBb', 'ba') + b: type ]]..'\022'..[[5; value: abcBabcBcabcBb (['abcBabcBcabcBb']), expr: abcBabcBcabcBb (['abcBabcBcabcBb']) + == + =abcBabcBcabcBb= + {{{2 setreg('b', 'abcBc2', 'ca') + b: type v; value: abcBabcBcabcBb]].."\000abcBc2 (['abcBabcBcabcBb', 'abcBc2']), expr: abcBabcBcabcBb\000"..[[abcBc2 (['abcBabcBcabcBb', 'abcBc2']) + == + =abcBabcBcabcBb + abcBc2= + {{{2 setreg('b', 'abcBb2', 'b50a') + b: type ]].."\02250; value: abcBabcBcabcBb\000abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']), expr: abcBabcBcabcBb\000"..[[abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']) + == + =abcBabcBcabcBb = + abcBc2abcBb2 + {{{2 setreg('C', 'abcCl', 'l') + C: type V; value: abcC]].."\000abcCl\000 (['abcC', 'abcCl']), expr: abcC\000abcCl\000"..[[ (['abcC', 'abcCl']) + == + abcC + abcCl + == + {{{2 setreg('C', 'abcCc', 'c') + C: type v; value: abcC]].."\000abcCl\000abcCc (['abcC', 'abcCl', 'abcCc']), expr: abcC\000abcCl\000"..[[abcCc (['abcC', 'abcCl', 'abcCc']) + == + =abcC + abcCl + abcCc= + {{{2 setreg('D', 'abcDb', 'b') + D: type ]].."\0225; value: abcD\000abcDb (['abcD', 'abcDb']), expr: abcD\000"..[[abcDb (['abcD', 'abcDb']) + == + =abcD = + abcDb + {{{2 setreg('E', 'abcEb', 'b') + E: type ]].."\0225; value: abcE\000abcEb (['abcE', 'abcEb']), expr: abcE\000"..[[abcEb (['abcE', 'abcEb']) + == + =abcE = + abcEb + {{{2 setreg('E', 'abcEl', 'l') + E: type V; value: abcE]].."\000abcEb\000abcEl\000 (['abcE', 'abcEb', 'abcEl']), expr: abcE\000abcEb\000abcEl\000"..[[ (['abcE', 'abcEb', 'abcEl']) + == + abcE + abcEb + abcEl + == + {{{2 setreg('F', 'abcFc', 'c') + F: type v; value: abcF]].."\000abcFc (['abcF', 'abcFc']), expr: abcF\000"..[[abcFc (['abcF', 'abcFc']) + == + =abcF + abcFc=]]) + end) + + it('appending NL with setreg()', function() + execute('so test_eval_setup.vim') + + execute([[call setreg('a', 'abcA2', 'c')]]) + execute([[call setreg('b', 'abcB2', 'v')]]) + execute([[call setreg('c', 'abcC2', 'l')]]) + execute([[call setreg('d', 'abcD2', 'V')]]) + execute([[call setreg('e', 'abcE2', 'b')]]) + execute([[call setreg('f', 'abcF2', "\<C-v>")]]) + -- These registers where set like this in the old test_eval.in but never + -- copied to the output buffer with SetReg(). They do not appear in + -- test_eval.ok. Therefore they are commented out. + --execute([[call setreg('g', 'abcG2', 'b10')]]) + --execute([[call setreg('h', 'abcH2', "\<C-v>10")]]) + --execute([[call setreg('I', 'abcI2')]]) + + execute([[call SetReg('A', "\n")]]) + execute([[call SetReg('B', "\n", 'c')]]) + execute([[call SetReg('C', "\n")]]) + execute([[call SetReg('D', "\n", 'l')]]) + execute([[call SetReg('E', "\n")]]) + execute([[call SetReg('F', "\n", 'b')]]) + expect([[ + + {{{2 setreg('A', ']]..'\000'..[[') + A: type V; value: abcA2]].."\000 (['abcA2']), expr: abcA2\000"..[[ (['abcA2']) + == + abcA2 + == + {{{2 setreg('B', ']]..'\000'..[[', 'c') + B: type v; value: abcB2]].."\000 (['abcB2', '']), expr: abcB2\000"..[[ (['abcB2', '']) + == + =abcB2 + = + {{{2 setreg('C', ']]..'\000'..[[') + C: type V; value: abcC2]].."\000\000 (['abcC2', '']), expr: abcC2\000\000"..[[ (['abcC2', '']) + == + abcC2 + + == + {{{2 setreg('D', ']]..'\000'..[[', 'l') + D: type V; value: abcD2]].."\000\000 (['abcD2', '']), expr: abcD2\000\000"..[[ (['abcD2', '']) + == + abcD2 + + == + {{{2 setreg('E', ']]..'\000'..[[') + E: type V; value: abcE2]].."\000\000 (['abcE2', '']), expr: abcE2\000\000"..[[ (['abcE2', '']) + == + abcE2 + + == + {{{2 setreg('F', ']]..'\000'..[[', 'b') + F: type ]].."\0220; value: abcF2\000 (['abcF2', '']), expr: abcF2\000"..[[ (['abcF2', '']) + == + =abcF2= + ]]) + end) + + it('setting and appending list with setreg()', function() + execute('so test_eval_setup.vim') + + execute([[$put ='{{{1 Setting lists with setreg()']]) + execute([=[call SetReg('a', ['abcA3'], 'c')]=]) + execute([=[call SetReg('b', ['abcB3'], 'l')]=]) + execute([=[call SetReg('c', ['abcC3'], 'b')]=]) + execute([=[call SetReg('d', ['abcD3'])]=]) + execute([=[call SetReg('e', [1, 2, 'abc', 3])]=]) + execute([=[call SetReg('f', [1, 2, 3])]=]) + + execute([[$put ='{{{1 Appending lists with setreg()']]) + execute([=[call SetReg('A', ['abcA3c'], 'c')]=]) + execute([=[call SetReg('b', ['abcB3l'], 'la')]=]) + execute([=[call SetReg('C', ['abcC3b'], 'lb')]=]) + execute([=[call SetReg('D', ['abcD32'])]=]) + execute([=[call SetReg('A', ['abcA32'])]=]) + execute([=[call SetReg('B', ['abcB3c'], 'c')]=]) + execute([=[call SetReg('C', ['abcC3l'], 'l')]=]) + execute([=[call SetReg('D', ['abcD3b'], 'b')]=]) + expect([[ + + {{{1 Setting lists with setreg() + {{{2 setreg('a', ['abcA3'], 'c') + a: type v; value: abcA3 (['abcA3']), expr: abcA3 (['abcA3']) + == + =abcA3= + {{{2 setreg('b', ['abcB3'], 'l') + b: type V; value: abcB3]].."\000 (['abcB3']), expr: abcB3\000"..[[ (['abcB3']) + == + abcB3 + == + {{{2 setreg('c', ['abcC3'], 'b') + c: type ]]..'\022'..[[5; value: abcC3 (['abcC3']), expr: abcC3 (['abcC3']) + == + =abcC3= + {{{2 setreg('d', ['abcD3']) + d: type V; value: abcD3]].."\000 (['abcD3']), expr: abcD3\000"..[[ (['abcD3']) + == + abcD3 + == + {{{2 setreg('e', [1, 2, 'abc', 3]) + e: type V; value: 1]].."\0002\000abc\0003\000 (['1', '2', 'abc', '3']), expr: 1\0002\000abc\0003\000"..[[ (['1', '2', 'abc', '3']) + == + 1 + 2 + abc + 3 + == + {{{2 setreg('f', [1, 2, 3]) + f: type V; value: 1]].."\0002\0003\000 (['1', '2', '3']), expr: 1\0002\0003\000"..[[ (['1', '2', '3']) + == + 1 + 2 + 3 + == + {{{1 Appending lists with setreg() + {{{2 setreg('A', ['abcA3c'], 'c') + A: type v; value: abcA3]].."\000abcA3c (['abcA3', 'abcA3c']), expr: abcA3\000"..[[abcA3c (['abcA3', 'abcA3c']) + == + =abcA3 + abcA3c= + {{{2 setreg('b', ['abcB3l'], 'la') + b: type V; value: abcB3]].."\000abcB3l\000 (['abcB3', 'abcB3l']), expr: abcB3\000abcB3l\000"..[[ (['abcB3', 'abcB3l']) + == + abcB3 + abcB3l + == + {{{2 setreg('C', ['abcC3b'], 'lb') + C: type ]].."\0226; value: abcC3\000abcC3b (['abcC3', 'abcC3b']), expr: abcC3\000"..[[abcC3b (['abcC3', 'abcC3b']) + == + =abcC3 = + abcC3b + {{{2 setreg('D', ['abcD32']) + D: type V; value: abcD3]].."\000abcD32\000 (['abcD3', 'abcD32']), expr: abcD3\000abcD32\000"..[[ (['abcD3', 'abcD32']) + == + abcD3 + abcD32 + == + {{{2 setreg('A', ['abcA32']) + A: type V; value: abcA3]].."\000abcA3c\000abcA32\000 (['abcA3', 'abcA3c', 'abcA32']), expr: abcA3\000abcA3c\000abcA32\000"..[[ (['abcA3', 'abcA3c', 'abcA32']) + == + abcA3 + abcA3c + abcA32 + == + {{{2 setreg('B', ['abcB3c'], 'c') + B: type v; value: abcB3]].."\000abcB3l\000abcB3c (['abcB3', 'abcB3l', 'abcB3c']), expr: abcB3\000abcB3l\000"..[[abcB3c (['abcB3', 'abcB3l', 'abcB3c']) + == + =abcB3 + abcB3l + abcB3c= + {{{2 setreg('C', ['abcC3l'], 'l') + C: type V; value: abcC3]].."\000abcC3b\000abcC3l\000 (['abcC3', 'abcC3b', 'abcC3l']), expr: abcC3\000abcC3b\000abcC3l\000"..[[ (['abcC3', 'abcC3b', 'abcC3l']) + == + abcC3 + abcC3b + abcC3l + == + {{{2 setreg('D', ['abcD3b'], 'b') + D: type ]].."\0226; value: abcD3\000abcD32\000abcD3b (['abcD3', 'abcD32', 'abcD3b']), expr: abcD3\000abcD32\000"..[[abcD3b (['abcD3', 'abcD32', 'abcD3b']) + == + =abcD3 = + abcD32 + abcD3b]]) + + -- From now on we delete the buffer contents after each expect() to make + -- the next expect() easier to write. This is neccessary because null + -- bytes on a line by itself don't play well together with the dedent + -- function used in expect(). + execute('%delete') + execute([[$put ='{{{1 Appending lists with NL with setreg()']]) + execute([=[call SetReg('A', ["\n", 'abcA3l2'], 'l')]=]) + expect( + '\n'.. + '{{{1 Appending lists with NL with setreg()\n'.. + "{{{2 setreg('A', ['\000', 'abcA3l2'], 'l')\n".. + "A: type V; value: abcA3\000abcA3c\000abcA32\000\000\000abcA3l2\000 (['abcA3', 'abcA3c', 'abcA32', '\000', 'abcA3l2']), expr: abcA3\000abcA3c\000abcA32\000\000\000abcA3l2\000 (['abcA3', 'abcA3c', 'abcA32', '\000', 'abcA3l2'])\n".. + '==\n'.. + 'abcA3\n'.. + 'abcA3c\n'.. + 'abcA32\n'.. + '\000\n'.. + 'abcA3l2\n'.. + '==') + execute('%delete') + execute([=[call SetReg('B', ["\n", 'abcB3c2'], 'c')]=]) + expect( + '\n'.. + "{{{2 setreg('B', ['\000', 'abcB3c2'], 'c')\n".. + "B: type v; value: abcB3\000abcB3l\000abcB3c\000\000\000abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\000', 'abcB3c2']), expr: abcB3\000abcB3l\000abcB3c\000\000\000abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\000', 'abcB3c2'])\n".. + '==\n'.. + '=abcB3\n'.. + 'abcB3l\n'.. + 'abcB3c\n'.. + '\000\n'.. + 'abcB3c2=') + execute('%delete') + execute([=[call SetReg('C', ["\n", 'abcC3b2'], 'b')]=]) + expect( + '\n'.. + "{{{2 setreg('C', ['\000', 'abcC3b2'], 'b')\n".. + "C: type \0227; value: abcC3\000abcC3b\000abcC3l\000\000\000abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\000', 'abcC3b2']), expr: abcC3\000abcC3b\000abcC3l\000\000\000abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\000', 'abcC3b2'])\n".. + '==\n'.. + '=abcC3 =\n'.. + ' abcC3b\n'.. + ' abcC3l\n'.. + ' \000\n'.. + ' abcC3b2') + execute('%delete') + execute([=[call SetReg('D', ["\n", 'abcD3b50'],'b50')]=]) + expect( + '\n'.. + "{{{2 setreg('D', ['\000', 'abcD3b50'], 'b50')\n".. + "D: type \02250; value: abcD3\000abcD32\000abcD3b\000\000\000abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\000', 'abcD3b50']), expr: abcD3\000abcD32\000abcD3b\000\000\000abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\000', 'abcD3b50'])\n".. + '==\n'.. + '=abcD3 =\n'.. + ' abcD32\n'.. + ' abcD3b\n'.. + ' \000\n'.. + ' abcD3b50') + end) + + -- The tests for setting lists with NLs are split into seperate it() blocks + -- to make the expect() calls easier to write. Otherwise the null byte can + -- make trouble on a line on its own. + it('setting lists with NLs with setreg(), part 1', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('a', ['abcA4-0', "\n", "abcA4-2\n", "\nabcA4-3", "abcA4-4\nabcA4-4-2"])]=]) + expect( + '\n'.. + "{{{2 setreg('a', ['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2'])\n".. + "a: type V; value: abcA4-0\000\000\000abcA4-2\000\000\000abcA4-3\000abcA4-4\000abcA4-4-2\000 (['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2']), expr: abcA4-0\000\000\000abcA4-2\000\000\000abcA4-3\000abcA4-4\000abcA4-4-2\000 (['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2'])\n".. + '==\n'.. + 'abcA4-0\n'.. + '\000\n'.. + 'abcA4-2\000\n'.. + '\000abcA4-3\n'.. + 'abcA4-4\000abcA4-4-2\n'.. + '==') + end) + + it('setting lists with NLs with setreg(), part 2', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('b', ['abcB4c-0', "\n", "abcB4c-2\n", "\nabcB4c-3", "abcB4c-4\nabcB4c-4-2"], 'c')]=]) + expect( + '\n'.. + "{{{2 setreg('b', ['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2'], 'c')\n".. + "b: type v; value: abcB4c-0\000\000\000abcB4c-2\000\000\000abcB4c-3\000abcB4c-4\000abcB4c-4-2 (['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2']), expr: abcB4c-0\000\000\000abcB4c-2\000\000\000abcB4c-3\000abcB4c-4\000abcB4c-4-2 (['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2'])\n".. + '==\n'.. + '=abcB4c-0\n'.. + '\000\n'.. + 'abcB4c-2\000\n'.. + '\000abcB4c-3\n'.. + 'abcB4c-4\000abcB4c-4-2=') + end) + + it('setting lists with NLs with setreg(), part 3', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('c', ['abcC4l-0', "\n", "abcC4l-2\n", "\nabcC4l-3", "abcC4l-4\nabcC4l-4-2"], 'l')]=]) + expect( + '\n'.. + "{{{2 setreg('c', ['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2'], 'l')\n".. + "c: type V; value: abcC4l-0\000\000\000abcC4l-2\000\000\000abcC4l-3\000abcC4l-4\000abcC4l-4-2\000 (['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2']), expr: abcC4l-0\000\000\000abcC4l-2\000\000\000abcC4l-3\000abcC4l-4\000abcC4l-4-2\000 (['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2'])\n".. + '==\n'.. + 'abcC4l-0\n'.. + '\000\n'.. + 'abcC4l-2\000\n'.. + '\000abcC4l-3\n'.. + 'abcC4l-4\000abcC4l-4-2\n'.. + '==') + end) + it('setting lists with NLs with setreg(), part 4', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('d', ['abcD4b-0', "\n", "abcD4b-2\n", "\nabcD4b-3", "abcD4b-4\nabcD4b-4-2"], 'b')]=]) + expect( + '\n'.. + "{{{2 setreg('d', ['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2'], 'b')\n".. + "d: type \02219; value: abcD4b-0\000\000\000abcD4b-2\000\000\000abcD4b-3\000abcD4b-4\000abcD4b-4-2 (['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2']), expr: abcD4b-0\000\000\000abcD4b-2\000\000\000abcD4b-3\000abcD4b-4\000abcD4b-4-2 (['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2'])\n".. + '==\n'.. + '=abcD4b-0 =\n'.. + ' \000\n'.. + ' abcD4b-2\000\n'.. + ' \000abcD4b-3\n'.. + ' abcD4b-4\000abcD4b-4-2') + end) + it('setting lists with NLs with setreg(), part 5', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('e', ['abcE4b10-0', "\n", "abcE4b10-2\n", "\nabcE4b10-3", "abcE4b10-4\nabcE4b10-4-2"], 'b10')]=]) + expect( + '\n'.. + "{{{2 setreg('e', ['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2'], 'b10')\n".. + "e: type \02210; value: abcE4b10-0\000\000\000abcE4b10-2\000\000\000abcE4b10-3\000abcE4b10-4\000abcE4b10-4-2 (['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2']), expr: abcE4b10-0\000\000\000abcE4b10-2\000\000\000abcE4b10-3\000abcE4b10-4\000abcE4b10-4-2 (['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2'])\n".. + '==\n'.. + '=abcE4b10-0=\n'.. + ' \000\n'.. + ' abcE4b10-2\000\n'.. + ' \000abcE4b10-3\n'.. + ' abcE4b10-4\000abcE4b10-4-2') + end) + + it('getreg("a",1,1) returns a valid list when "a is unset', function() + -- Precondition: "a is actually unset and "0 is nonempty + eq('', eval("getregtype('a')")) + eq('', eval("getreg('a')")) + execute("call setreg('0','text')") + + -- This used to return a NULL list + -- which setreg didn't handle + execute("let x = getreg('a',1,1)") + execute("call setreg('0',x)") + + -- nvim didn't crash and "0 was emptied + eq(2, eval("1+1")) + eq({}, eval("getreg('0',1,1)")) + + -- x is a mutable list + execute("let y = x") + eq({}, eval("y")) + execute("call add(x, 'item')") + eq({'item'}, eval("y")) + end) + + it('search and expressions', function() + execute('so test_eval_setup.vim') + execute([=[call SetReg('/', ['abc/'])]=]) + execute([=[call SetReg('/', ["abc/\n"])]=]) + execute([=[call SetReg('=', ['"abc/"'])]=]) + execute([=[call SetReg('=', ["\"abc/\n\""])]=]) + expect([[ + + {{{2 setreg('/', ['abc/']) + /: type v; value: abc/ (['abc/']), expr: abc/ (['abc/']) + == + =abc/= + {{{2 setreg('/', ['abc/]]..'\000'..[[']) + /: type v; value: abc/]].."\000 (['abc/\000']), expr: abc/\000 (['abc/\000"..[[']) + == + =abc/]]..'\000'..[[= + {{{2 setreg('=', ['"abc/"']) + =: type v; value: abc/ (['abc/']), expr: "abc/" (['"abc/"']) + {{{2 setreg('=', ['"abc/]]..'\000'..[["']) + =: type v; value: abc/]].."\000 (['abc/\000"..[[']), expr: "abc/]]..'\000'..[[" (['"abc/]]..'\000'..[["'])]]) + end) + + if has_clipboard() then + it('system clipboard', function() + insert([[ + Some first line (this text was at the top of the old test_eval.in). + + Note: system clipboard is saved, changed and restored. + + clipboard contents + something else]]) + execute('so test_eval_setup.vim') + -- Save and restore system clipboard. + execute("let _clipreg = ['*', getreg('*'), getregtype('*')]") + execute('let _clipopt = &cb') + execute("let &cb='unnamed'") + execute('5y') + execute('AR *') + execute('tabdo :windo :echo "hi"') + execute('6y') + execute('AR *') + execute('let &cb=_clipopt') + execute("call call('setreg', _clipreg)") + expect([[ + Some first line (this text was at the top of the old test_eval.in). + + Note: system clipboard is saved, changed and restored. + + clipboard contents + something else + *: type V; value: clipboard contents]]..'\00'..[[ (['clipboard contents']), expr: clipboard contents]]..'\00'..[[ (['clipboard contents']) + *: type V; value: something else]]..'\00'..[[ (['something else']), expr: something else]]..'\00'..[[ (['something else'])]]) + end) + else + pending('system clipboard not available', function() end) + end + + it('errors', function() + source([[ + fun ErrExe(str) + call append('$', 'Executing '.a:str) + try + execute a:str + catch + $put =v:exception + endtry + endfun]]) + execute([[call ErrExe('call setreg()')]]) + execute([[call ErrExe('call setreg(1)')]]) + execute([[call ErrExe('call setreg(1, 2, 3, 4)')]]) + execute([=[call ErrExe('call setreg([], 2)')]=]) + execute([[call ErrExe('call setreg(1, {})')]]) + execute([=[call ErrExe('call setreg(1, 2, [])')]=]) + execute([=[call ErrExe('call setreg("/", ["1", "2"])')]=]) + execute([=[call ErrExe('call setreg("=", ["1", "2"])')]=]) + execute([=[call ErrExe('call setreg(1, ["", "", [], ""])')]=]) + expect([[ + + Executing call setreg() + Vim(call):E119: Not enough arguments for function: setreg + Executing call setreg(1) + Vim(call):E119: Not enough arguments for function: setreg + Executing call setreg(1, 2, 3, 4) + Vim(call):E118: Too many arguments for function: setreg + Executing call setreg([], 2) + Vim(call):E730: using List as a String + Executing call setreg(1, {}) + Vim(call):E731: using Dictionary as a String + Executing call setreg(1, 2, []) + Vim(call):E730: using List as a String + Executing call setreg("/", ["1", "2"]) + Vim(call):E883: search pattern and expression register may not contain two or more lines + Executing call setreg("=", ["1", "2"]) + Vim(call):E883: search pattern and expression register may not contain two or more lines + Executing call setreg(1, ["", "", [], ""]) + Vim(call):E730: using List as a String]]) + end) + + it('function name not starting with a capital', function() + execute('try') + execute(' func! g:test()') + execute(' echo "test"') + execute(' endfunc') + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(function):E128: Function name must start with a capital or "s:": g:test()', eval('tmp')) + end) + + it('Function name followed by #', function() + execute('try') + execute(' func! test2() "#') + execute(' echo "test2"') + execute(' endfunc') + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(function):E128: Function name must start with a capital or "s:": test2() "#', eval('tmp')) + end) + + it('function name includes a colon', function() + execute('try') + execute(' func! b:test()') + execute(' echo "test"') + execute(' endfunc') + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(function):E128: Function name must start with a capital or "s:": b:test()', eval('tmp')) + end) + + it('function name starting with/without "g:", buffer-local funcref', function() + execute('function! g:Foo(n)') + execute(" $put ='called Foo(' . a:n . ')'") + execute('endfunction') + execute("let b:my_func = function('Foo')") + execute('call b:my_func(1)') + execute('echo g:Foo(2)') + execute('echo Foo(3)') + expect([[ + + called Foo(1) + called Foo(2) + called Foo(3)]]) + end) + + it('script-local function used in Funcref must exist', function() + source([[ + " Vim script used in test_eval.in. Needed for script-local function. + + func! s:Testje() + return "foo" + endfunc + + let Bar = function('s:Testje') + + $put ='s:Testje exists: ' . exists('s:Testje') + $put ='func s:Testje exists: ' . exists('*s:Testje') + $put ='Bar exists: ' . exists('Bar') + $put ='func Bar exists: ' . exists('*Bar') + ]]) + expect([[ + + s:Testje exists: 0 + func s:Testje exists: 1 + Bar exists: 1 + func Bar exists: 1]]) + end) + + it("using $ instead of '$' must give an error", function() + execute('try') + execute(" call append($, 'foobar')") + execute('catch') + execute(' let tmp = v:exception') + execute('endtry') + eq('Vim(call):E116: Invalid arguments for function append', eval('tmp')) + end) + + it('getcurpos/setpos', function() + insert([[ + 012345678 + 012345678 + + start:]]) + execute('/^012345678') + feed('6l') + execute('let sp = getcurpos()') + feed('0') + execute("call setpos('.', sp)") + feed('jyl') + execute('$put') + expect([[ + 012345678 + 012345678 + + start: + 6]]) + end) + + it('substring and variable name', function() + execute("let str = 'abcdef'") + execute('let n = 3') + eq('def', eval('str[n:]')) + eq('abcd', eval('str[:n]')) + eq('d', eval('str[n:n]')) + execute('unlet n') + execute('let nn = 3') + eq('def', eval('str[nn:]')) + eq('abcd', eval('str[:nn]')) + eq('d', eval('str[nn:nn]')) + execute('unlet nn') + execute('let b:nn = 4') + eq('ef', eval('str[b:nn:]')) + eq('abcde', eval('str[:b:nn]')) + eq('e', eval('str[b:nn:b:nn]')) + end) +end) diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua new file mode 100644 index 0000000000..2d1824c8cd --- /dev/null +++ b/test/functional/legacy/fixeol_spec.lua @@ -0,0 +1,72 @@ +-- Tests for 'fixeol' + +local helpers = require('test.functional.helpers') +local feed = helpers.feed +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('fixeol', function() + local function rmtestfiles() + os.remove('test.out') + os.remove('XXEol') + os.remove('XXNoEol') + os.remove('XXTestEol') + os.remove('XXTestNoEol') + end + setup(function() + clear() + rmtestfiles() + end) + teardown(function() + rmtestfiles() + end) + + it('is working', function() + -- First write two test files – with and without trailing EOL. + -- Use Unix fileformat for consistency. + execute('set ff=unix') + execute('enew!') + feed('awith eol<esc>:w! XXEol<cr>') + execute('enew!') + execute('set noeol nofixeol') + feed('awithout eol<esc>:w! XXNoEol<cr>') + execute('set eol fixeol') + execute('bwipe XXEol XXNoEol') + + -- Try editing files with 'fixeol' disabled. + execute('e! XXEol') + feed('ostays eol<esc>:set nofixeol<cr>') + execute('w! XXTestEol') + execute('e! XXNoEol') + feed('ostays without<esc>:set nofixeol<cr>') + execute('w! XXTestNoEol') + execute('bwipe XXEol XXNoEol XXTestEol XXTestNoEol') + execute('set fixeol') + + -- Append "END" to each file so that we can see what the last written char was. + feed('ggdGaEND<esc>:w >>XXEol<cr>') + execute('w >>XXNoEol') + execute('w >>XXTestEol') + execute('w >>XXTestNoEol') + + -- Concatenate the results. + execute('e! test.out') + feed('a0<esc>:$r XXEol<cr>') + execute('$r XXNoEol') + feed('Go1<esc>:$r XXTestEol<cr>') + execute('$r XXTestNoEol') + execute('w') + + -- Assert buffer contents. + expect([=[ + 0 + with eol + END + without eolEND + 1 + with eol + stays eol + END + without eol + stays withoutEND]=]) + end) +end) diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua new file mode 100644 index 0000000000..2a32aea127 --- /dev/null +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -0,0 +1,75 @@ +-- Test filename modifiers. + +local helpers = require('test.functional.helpers') +local clear, source = helpers.clear, helpers.source +local call, eq, nvim = helpers.call, helpers.eq, helpers.meths + +local function expected_empty() + eq({}, nvim.get_vvar('errors')) +end + +describe('filename modifiers', function() + before_each(function() + clear() + + source([=[ + func Test_fnamemodify() + let tmpdir = resolve('/tmp') + execute 'cd '. tmpdir + set shell=sh + set shellslash + let $HOME=fnamemodify('.', ':p:h:h:h') + call assert_equal('/', fnamemodify('.', ':p')[-1:]) + call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) + call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) + call assert_equal('test.out', fnamemodify('test.out', ':.')) + call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) + call assert_equal('test.out', fnamemodify('test.out', ':~')) + call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) + call assert_equal('a', fnamemodify('../testdir/a', ':t')) + call assert_equal('', fnamemodify('.', ':p:t')) + call assert_equal('test.out', fnamemodify('test.out', ':p:t')) + call assert_equal('out', fnamemodify('test.out', ':p:e')) + call assert_equal('out', fnamemodify('test.out', ':p:t:e')) + call assert_equal('abc.fb2.tar', fnamemodify('abc.fb2.tar.gz', ':r')) + call assert_equal('abc.fb2', fnamemodify('abc.fb2.tar.gz', ':r:r')) + call assert_equal('abc', fnamemodify('abc.fb2.tar.gz', ':r:r:r')) + call assert_equal(tmpdir .'/abc.fb2', substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '')) + call assert_equal('gz', fnamemodify('abc.fb2.tar.gz', ':e')) + call assert_equal('tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')) + call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r')) + call assert_equal('''abc def''', fnamemodify('abc def', ':S')) + call assert_equal('''abc" "def''', fnamemodify('abc" "def', ':S')) + call assert_equal('''abc"%"def''', fnamemodify('abc"%"def', ':S')) + call assert_equal('''abc''\'''' ''\''''def''', fnamemodify('abc'' ''def', ':S')) + call assert_equal('''abc''\''''%''\''''def''', fnamemodify('abc''%''def', ':S')) + new foo.txt + call assert_equal(expand('%:r:S'), shellescape(expand('%:r'))) + call assert_equal('foo,''foo'',foo.txt', join([expand('%:r'), expand('%:r:S'), expand('%')], ',')) + quit + + call assert_equal("'abc\ndef'", fnamemodify("abc\ndef", ':S')) + set shell=tcsh + call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + endfunc + + func Test_expand() + new + call assert_equal("", expand('%:S')) + quit + endfunc + ]=]) + end) + + it('is working', function() + call('Test_fnamemodify') + expected_empty() + end) + + it('works for :S in an unnamed buffer', function() + call('Test_expand') + expected_empty() + end) +end) diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua new file mode 100644 index 0000000000..9083911021 --- /dev/null +++ b/test/functional/legacy/function_sort_spec.lua @@ -0,0 +1,29 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval + +describe('sort', function() + before_each(clear) + + it('numbers compared as strings', function() + eq({1, 2, 3}, eval('sort([3, 2, 1])')) + eq({13, 28, 3}, eval('sort([3, 28, 13])')) + end) + + it('numbers compared as numeric', function() + eq({1, 2, 3}, eval("sort([3, 2, 1], 'n')")) + eq({3, 13, 28}, eval("sort([3, 28, 13], 'n')")) + -- Strings are not sorted. + eq({'13', '28', '3'}, eval("sort(['13', '28', '3'], 'n')")) + end) + + it('numbers compared as numbers', function() + eq({3, 13, 28}, eval("sort([13, 28, 3], 'N')")) + eq({'3', '13', '28'}, eval("sort(['13', '28', '3'], 'N')")) + end) + + it('numbers compared as float', function() + eq({0.28, 3, 13.5}, eval("sort([13.5, 0.28, 3], 'f')")) + end) +end) diff --git a/test/functional/legacy/glob2regpat_spec.lua b/test/functional/legacy/glob2regpat_spec.lua new file mode 100644 index 0000000000..0492143616 --- /dev/null +++ b/test/functional/legacy/glob2regpat_spec.lua @@ -0,0 +1,22 @@ +-- Tests for signs + +local helpers = require('test.functional.helpers') +local clear, execute = helpers.clear, helpers.execute +local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval + +describe('glob2regpat()', function() + before_each(clear) + + it('handles invalid input', function() + execute('call glob2regpat(1.33)') + helpers.feed('<cr>') + neq(nil, string.find(eval('v:errmsg'), '^E806:')) + end) + it('returns ^$ for empty input', function() + eq('^$', eval("glob2regpat('')")) + end) + it('handles valid input', function() + eq('^foo\\.', eval("glob2regpat('foo.*')")) + eq('\\.vim$', eval("glob2regpat('*.vim')")) + end) +end) diff --git a/test/functional/legacy/increment_spec.lua b/test/functional/legacy/increment_spec.lua new file mode 100644 index 0000000000..4aa24c0d53 --- /dev/null +++ b/test/functional/legacy/increment_spec.lua @@ -0,0 +1,748 @@ +-- Tests for using Ctrl-A/Ctrl-X on visual selections + +local helpers = require('test.functional.helpers') +local source, execute = helpers.source, helpers.execute +local call, clear = helpers.call, helpers.clear +local eq, nvim = helpers.eq, helpers.meths + +describe('Ctrl-A/Ctrl-X on visual selections', function() + + before_each(function() + clear() + source([=[ + " 1) Ctrl-A on visually selected number + " Text: + " foobar-10 + " Expected: + " 1) Ctrl-A on start of line: + " foobar-9 + " 2) Ctrl-A on visually selected "-10": + " foobar-9 + " 3) Ctrl-A on visually selected "10": + " foobar-11 + " 4) Ctrl-X on visually selected "-10" + " foobar-11 + " 5) Ctrl-X on visually selected "10" + " foobar-9 + func Test_visual_increment_01() + call setline(1, repeat(["foobaar-10"], 5)) + + call cursor(1, 1) + exec "norm! \<C-A>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 1, 9, 0], getpos('.')) + + call cursor(2, 1) + exec "norm! f-v$\<C-A>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 2, 8, 0], getpos('.')) + + call cursor(3, 1) + exec "norm! f1v$\<C-A>" + call assert_equal("foobaar-11", getline('.')) + call assert_equal([0, 3, 9, 0], getpos('.')) + + call cursor(4, 1) + exec "norm! f-v$\<C-X>" + call assert_equal("foobaar-11", getline('.')) + call assert_equal([0, 4, 8, 0], getpos('.')) + + call cursor(5, 1) + exec "norm! f1v$\<C-X>" + call assert_equal("foobaar-9", getline('.')) + call assert_equal([0, 5, 9, 0], getpos('.')) + endfunc + + " 2) Ctrl-A on visually selected lines + " Text: + " 10 + " 20 + " 30 + " 40 + " + " Expected: + " 1) Ctrl-A on visually selected lines: + " 11 + " 21 + " 31 + " 41 + " + " 2) Ctrl-X on visually selected lines: + " 9 + " 19 + " 29 + " 39 + func Test_visual_increment_02() + call setline(1, ["10", "20", "30", "40"]) + exec "norm! GV3k$\<C-A>" + call assert_equal(["11", "21", "31", "41"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["10", "20", "30", "40"]) + exec "norm! GV3k$\<C-X>" + call assert_equal(["9", "19", "29", "39"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 3) g Ctrl-A on visually selected lines, with non-numbers in between + " Text: + " 10 + " + " 20 + " + " 30 + " + " 40 + " + " Expected: + " 1) 2 g Ctrl-A on visually selected lines: + " 12 + " + " 24 + " + " 36 + " + " 48 + " 2) 2 g Ctrl-X on visually selected lines + " 8 + " + " 16 + " + " 24 + " + " 32 + func Test_visual_increment_03() + call setline(1, ["10", "", "20", "", "30", "", "40"]) + exec "norm! GV6k2g\<C-A>" + call assert_equal(["12", "", "24", "", "36", "", "48"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["10", "", "20", "", "30", "", "40"]) + exec "norm! GV6k2g\<C-X>" + call assert_equal(["8", "", "16", "", "24", "", "32"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 4) Ctrl-A on non-number + " Text: + " foobar-10 + " Expected: + " 1) visually select foobar: + " foobar-10 + func Test_visual_increment_04() + call setline(1, ["foobar-10"]) + exec "norm! vf-\<C-A>" + call assert_equal(["foobar-10"], getline(1, '$')) + " NOTE: I think this is correct behavior... + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 5) g<Ctrl-A> on letter + " Test: + " a + " a + " a + " a + " Expected: + " 1) g Ctrl-A on visually selected lines + " b + " c + " d + " e + func Test_visual_increment_05() + set nrformats+=alpha + call setline(1, repeat(["a"], 4)) + exec "norm! GV3kg\<C-A>" + call assert_equal(["b", "c", "d", "e"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 6) g<Ctrl-A> on letter + " Test: + " z + " z + " z + " z + " Expected: + " 1) g Ctrl-X on visually selected lines + " y + " x + " w + " v + func Test_visual_increment_06() + set nrformats+=alpha + call setline(1, repeat(["z"], 4)) + exec "norm! GV3kg\<C-X>" + call assert_equal(["y", "x", "w", "v"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 7) <Ctrl-A> on letter + " Test: + " 2 + " 1 + " 0 + " -1 + " -2 + " + " Expected: + " 1) Ctrl-A on visually selected lines + " 3 + " 2 + " 1 + " 0 + " -1 + " + " 2) Ctrl-X on visually selected lines + " 1 + " 0 + " -1 + " -2 + " -3 + func Test_visual_increment_07() + call setline(1, ["2", "1", "0", "-1", "-2"]) + exec "norm! GV4k\<C-A>" + call assert_equal(["3", "2", "1", "0", "-1"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["2", "1", "0", "-1", "-2"]) + exec "norm! GV4k\<C-X>" + call assert_equal(["1", "0", "-1", "-2", "-3"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 8) Block increment on 0x9 + " Text: + " 0x9 + " 0x9 + " Expected: + " 1) Ctrl-A on visually block selected region (cursor at beginning): + " 0xa + " 0xa + " 2) Ctrl-A on visually block selected region (cursor at end) + " 0xa + " 0xa + func Test_visual_increment_08() + call setline(1, repeat(["0x9"], 2)) + exec "norm! \<C-V>j$\<C-A>" + call assert_equal(["0xa", "0xa"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, repeat(["0x9"], 2)) + exec "norm! gg$\<C-V>+\<C-A>" + call assert_equal(["0xa", "0xa"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 9) Increment and redo + " Text: + " 2 + " 2 + " + " 3 + " 3 + " + " Expected: + " 1) 2 Ctrl-A on first 2 visually selected lines + " 4 + " 4 + " 2) redo (.) on 3 + " 5 + " 5 + func Test_visual_increment_09() + call setline(1, ["2", "2", "", "3", "3", ""]) + exec "norm! ggVj2\<C-A>" + call assert_equal(["4", "4", "", "3", "3", ""], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! 3j." + call assert_equal(["4", "4", "", "5", "5", ""], getline(1, '$')) + call assert_equal([0, 4, 1, 0], getpos('.')) + endfunc + + " 10) sequentially decrement 1 + " Text: + " 1 + " 1 + " 1 + " 1 + " Expected: + " 1) g Ctrl-X on visually selected lines + " 0 + " -1 + " -2 + " -3 + func Test_visual_increment_10() + call setline(1, repeat(["1"], 4)) + exec "norm! GV3kg\<C-X>" + call assert_equal(["0", "-1", "-2", "-3"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 11) visually block selected indented lines + " Text: + " 1 + " 1 + " 1 + " 1 + " Expexted: + " 1) g Ctrl-A on block selected indented lines + " 2 + " 1 + " 3 + " 4 + func Test_visual_increment_11() + call setline(1, [" 1", "1", " 1", " 1"]) + exec "norm! f1\<C-V>3jg\<C-A>" + call assert_equal([" 2", "1", " 3", " 4"], getline(1, '$')) + call assert_equal([0, 1, 5, 0], getpos('.')) + endfunc + + " 12) visually selected several columns + " Text: + " 0 0 + " 0 0 + " 0 0 + " Expected: + " 1) 'v' select last zero and first zeroes + " 0 1 + " 1 0 + " 1 0 + func Test_visual_increment_12() + call setline(1, repeat(["0 0"], 3)) + exec "norm! $v++\<C-A>" + call assert_equal(["0 1", "1 0", "1 0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " 13) visually selected part of columns + " Text: + " max: 100px + " max: 200px + " max: 300px + " max: 400px + " Expected: + " 1) 'v' on first two numbers Ctrl-A + " max: 110px + " max: 220px + " max: 330px + " max: 400px + " 2) 'v' on first two numbers Ctrl-X + " max: 90px + " max: 190px + " max: 290px + " max: 400px + func Test_visual_increment_13() + call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) + exec "norm! f1\<C-V>l2j\<C-A>" + call assert_equal(["max: 110px", "max: 210px", "max: 310px", "max: 400px"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + + call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) + exec "norm! ggf1\<C-V>l2j\<C-X>" + call assert_equal(["max: 90px", "max: 190px", "max: 290px", "max: 400px"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + endfunc + + " 14) redo in block mode + " Text: + " 1 1 + " 1 1 + " Expected: + " 1) Ctrl-a on first column, redo on second column + " 2 2 + " 2 2 + func Test_visual_increment_14() + call setline(1, repeat(["1 1"], 2)) + exec "norm! G\<C-V>k\<C-A>w." + call assert_equal(["2 2", "2 2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " 15) block select single numbers + " Text: + " 101 + " Expected: + " 1) Ctrl-a on visually selected zero + " 111 + func Test_visual_increment_15() + call setline(1, ["101"]) + exec "norm! lv\<C-A>" + call assert_equal(["111"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " 16) increment right aligned numbers + " Text: + " 1 + " 19 + " 119 + " Expected: + " 1) Ctrl-a on line selected region + " 2 + " 20 + " 120 + func Test_visual_increment_16() + call setline(1, [" 1", " 19", " 119"]) + exec "norm! VG\<C-A>" + call assert_equal([" 2", " 20", " 120"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 17) block-wise increment and redo + " Text: + " 100 + " 1 + " + " 100 + " 1 + " + " Expected: + " 1) Ctrl-V j $ on first block, afterwards '.' on second + " 101 + " 2 + " + " 101 + " 2 + func Test_visual_increment_17() + call setline(1, [" 100", " 1", "", " 100", " 1"]) + exec "norm! \<C-V>j$\<C-A>2j." + call assert_equal([" 101", " 2", "", " 101", " 1"], getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos('.')) + endfunc + + " 18) repeat of g<Ctrl-a> + " Text: + " 0 + " 0 + " 0 + " 0 + " + " Expected: + " 1) V 4j g<ctrl-a>, repeat twice afterwards with . + " 3 + " 6 + " 9 + " 12 + func Test_visual_increment_18() + call setline(1, repeat(["0"], 4)) + exec "norm! GV3kg\<C-A>" + exec "norm! .." + call assert_equal(["3", "6", "9", "12"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 19) increment on number with nrformat including alpha + " Text: + " 1 + " 1a + " + " Expected: + " 1) <Ctrl-V>j$ <ctrl-a> + " 2 + " 2a + func Test_visual_increment_19() + set nrformats+=alpha + call setline(1, ["1", "1a"]) + exec "norm! \<C-V>G$\<C-A>" + call assert_equal(["2", "2a"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 20) increment a single letter + " Text: + " a + " + " Expected: + " 1) <Ctrl-a> and cursor is on a + " b + func Test_visual_increment_20() + set nrformats+=alpha + call setline(1, ["a"]) + exec "norm! \<C-A>" + call assert_equal(["b"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 21) block-wise increment on part of hexadecimal + " Text: + " 0x123456 + " + " Expected: + " 1) Ctrl-V f3 <ctrl-a> + " 0x124456 + func Test_visual_increment_21() + call setline(1, ["0x123456"]) + exec "norm! \<C-V>f3\<C-A>" + call assert_equal(["0x124456"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 22) Block increment on 0b0 + " Text: + " 0b1 + " 0b1 + " Expected: + " 1) Ctrl-A on visually block selected region (cursor at beginning): + " 0b10 + " 0b10 + " 2) Ctrl-A on visually block selected region (cursor at end) + " 0b10 + " 0b10 + func Test_visual_increment_22() + call setline(1, repeat(["0b1"], 2)) + exec "norm! \<C-V>j$\<C-A>" + call assert_equal(repeat(["0b10"], 2), getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, repeat(["0b1"], 2)) + exec "norm! $\<C-V>+\<C-A>" + call assert_equal(repeat(["0b10"], 2), getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 23) block-wise increment on part of binary + " Text: + " 0b1001 + " + " Expected: + " 1) Ctrl-V 5l <ctrl-a> + " 0b1011 + func Test_visual_increment_23() + call setline(1, ["0b1001"]) + exec "norm! \<C-V>4l\<C-A>" + call assert_equal(["0b1011"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 24) increment hexadecimal + " Text: + " 0x0b1001 + " + " Expected: + " 1) <ctrl-a> + " 0x0b1002 + func Test_visual_increment_24() + call setline(1, ["0x0b1001"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0x0b1002"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 25) increment binary with nrformats including alpha + " Text: + " 0b1001a + " + " Expected: + " 1) <ctrl-a> + " 0b1010a + func Test_visual_increment_25() + set nrformats+=alpha + call setline(1, ["0b1001a"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0b1010a"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " 26) increment binary with 32 bits + " Text: + " 0b11111111111111111111111111111110 + " + " Expected: + " 1) <ctrl-a> + " 0b11111111111111111111111111111111 + func Test_visual_increment_26() + set nrformats+=alpha + call setline(1, ["0b11111111111111111111111111111110"]) + exec "norm! \<C-V>$\<C-A>" + call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + set nrformats-=alpha + endfunc + + " 27) increment with 'rightreft', if supported + func Test_visual_increment_27() + if exists('+rightleft') + set rightleft + call setline(1, ["1234 56"]) + + exec "norm! $\<C-A>" + call assert_equal(["1234 57"], getline(1, '$')) + call assert_equal([0, 1, 7, 0], getpos('.')) + + exec "norm! \<C-A>" + call assert_equal(["1234 58"], getline(1, '$')) + call assert_equal([0, 1, 7, 0], getpos('.')) + set norightleft + endif + endfunc + + " Tab code and linewise-visual inc/dec + func Test_visual_increment_28() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! Vj\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggVj\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and linewise-visual inc/dec with 'nrformats'+=alpha + func Test_visual_increment_29() + set nrformats+=alpha + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! Vj\<C-A>" + call assert_equal(["y\<TAB>10", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggVj\<C-X>" + call assert_equal(["w\<TAB>10", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and character-visual inc/dec + func Test_visual_increment_30() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! f1vjf1\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggf1vjf1\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual inc/dec + func Test_visual_increment_31() + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! f1\<C-V>jl\<C-A>" + call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + + call setline(1, ["x\<TAB>10", "\<TAB>-1"]) + exec "norm! ggf1\<C-V>jl\<C-X>" + call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 3, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual decrement with 'linebreak' and 'showbreak' + func Test_visual_increment_32() + 28vnew dummy_31 + set linebreak showbreak=+ + call setline(1, ["x\<TAB>\<TAB>\<TAB>10", "\<TAB>\<TAB>\<TAB>\<TAB>-1"]) + exec "norm! ggf0\<C-V>jg_\<C-X>" + call assert_equal(["x\<TAB>\<TAB>\<TAB>1-1", "\<TAB>\<TAB>\<TAB>\<TAB>-2"], getline(1, '$')) + call assert_equal([0, 1, 6, 0], getpos('.')) + bwipe! + endfunc + + " Tab code and blockwise-visual increment with $ + func Test_visual_increment_33() + call setline(1, ["\<TAB>123", "456"]) + exec "norm! gg0\<C-V>j$\<C-A>" + call assert_equal(["\<TAB>124", "457"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code and blockwise-visual increment and redo + func Test_visual_increment_34() + call setline(1, ["\<TAB>123", " 456789"]) + exec "norm! gg0\<C-V>j\<C-A>" + call assert_equal(["\<TAB>123", " 457789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! .." + call assert_equal(["\<TAB>123", " 459789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " Tab code, spaces and character-visual increment and redo + func Test_visual_increment_35() + call setline(1, ["\<TAB>123", " 123", "\<TAB>123", "\<TAB>123"]) + exec "norm! ggvjf3\<C-A>..." + call assert_equal(["\<TAB>127", " 127", "\<TAB>123", "\<TAB>123"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " Tab code, spaces and blockwise-visual increment and redo + func Test_visual_increment_36() + call setline(1, [" 123", "\<TAB>456789"]) + exec "norm! G0\<C-V>kl\<C-A>" + call assert_equal([" 123", "\<TAB>556789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + + exec "norm! ..." + call assert_equal([" 123", "\<TAB>856789"], getline(1, '$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + endfunc + + " block-wise increment and dot-repeat + " Text: + " 1 23 + " 4 56 + " + " Expected: + " 1) f2 Ctrl-V jl <ctrl-a>, repeat twice afterwards with . + " 1 26 + " 4 59 + " + " Try with and without indent. + func Test_visual_increment_37() + call setline(1, [" 1 23", " 4 56"]) + exec "norm! ggf2\<C-V>jl\<C-A>.." + call assert_equal([" 1 26", " 4 59"], getline(1, 2)) + + call setline(1, ["1 23", "4 56"]) + exec "norm! ggf2\<C-V>jl\<C-A>.." + call assert_equal(["1 26", "4 59"], getline(1, 2)) + endfunc + + " Check redo after the normal mode increment + func Test_visual_increment_38() + exec "norm! i10\<ESC>5\<C-A>." + call assert_equal(["20"], getline(1, '$')) + call assert_equal([0, 1, 2, 0], getpos('.')) + endfunc + + " Test what patch 7.3.414 fixed. Ctrl-A on "000" drops the leading zeros. + func Test_normal_increment_01() + call setline(1, "000") + exec "norm! gg0\<C-A>" + call assert_equal("001", getline(1)) + + call setline(1, "000") + exec "norm! gg$\<C-A>" + call assert_equal("001", getline(1)) + + call setline(1, "001") + exec "norm! gg0\<C-A>" + call assert_equal("002", getline(1)) + + call setline(1, "001") + exec "norm! gg$\<C-A>" + call assert_equal("002", getline(1)) + endfunc + ]=]) + end) + + for i = 1, 38 do + local id = string.format('%02d', i) + + it('works on Test ' .. id, function() + execute('set nrformats&vi') -- &vi makes Vim compatible + call('Test_visual_increment_' .. id) + eq({}, nvim.get_vvar('errors')) + end) + end + + it('does not drop leading zeroes', function() + execute('set nrformats&vi') -- &vi makes Vim compatible + call('Test_normal_increment_01') + eq({}, nvim.get_vvar('errors')) + end) +end) diff --git a/test/functional/legacy/lispwords_spec.lua b/test/functional/legacy/lispwords_spec.lua new file mode 100644 index 0000000000..48df4de55e --- /dev/null +++ b/test/functional/legacy/lispwords_spec.lua @@ -0,0 +1,25 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local execute = helpers.execute +local source = helpers.source + +describe('lispwords', function() + before_each(clear) + + it('should be set global-local',function() + source([[ + setglobal lispwords=foo,bar,baz + setlocal lispwords-=foo + setlocal lispwords+=quux]]) + eq('foo,bar,baz', eval('&g:lispwords')) + eq('bar,baz,quux', eval('&l:lispwords')) + eq('bar,baz,quux', eval('&lispwords')) + + execute('setlocal lispwords<') + eq('foo,bar,baz', eval('&g:lispwords')) + eq('foo,bar,baz', eval('&l:lispwords')) + eq('foo,bar,baz', eval('&lispwords')) + end) +end) diff --git a/test/functional/legacy/listlbr_utf8_spec.lua b/test/functional/legacy/listlbr_utf8_spec.lua index 69e7b87a21..df0e817533 100644 --- a/test/functional/legacy/listlbr_utf8_spec.lua +++ b/test/functional/legacy/listlbr_utf8_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers') local source = helpers.source +local feed = helpers.feed local clear, expect = helpers.clear, helpers.expect describe('linebreak', function() @@ -32,16 +33,19 @@ describe('linebreak', function() $put =g:line wincmd p endfu + " let g:test ="Test 1: set linebreak + set list + fancy listchars" exe "set linebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6" redraw! let line=ScreenChar(winwidth(0),4) call DoRecordScreen() + " let g:test ="Test 2: set nolinebreak list" set list nolinebreak redraw! let line=ScreenChar(winwidth(0),4) call DoRecordScreen() + " let g:test ="Test 3: set linebreak nolist" $put =\"\t*mask = nil;\" $ @@ -50,6 +54,7 @@ describe('linebreak', function() redraw! let line=ScreenChar(winwidth(0),4) call DoRecordScreen() + " let g:test ="Test 4: set linebreak list listchars and concealing" let c_defines=['#define ABCDE 1','#define ABCDEF 1','#define ABCDEFG 1','#define ABCDEFGH 1', '#define MSG_MODE_FILE 1','#define MSG_MODE_CONSOLE 2','#define MSG_MODE_FILE_AND_CONSOLE 3','#define MSG_MODE_FILE_THEN_CONSOLE 4'] call append('$', c_defines) @@ -61,6 +66,84 @@ describe('linebreak', function() redraw! let line=ScreenChar(winwidth(0),7) call DoRecordScreen() + " + let g:test ="Test 5: set linebreak list listchars and concealing part2" + let c_defines=['bbeeeeee ; some text'] + call append('$', c_defines) + $ + norm! zt + set nowrap ts=2 list linebreak listchars=tab:>- cole=2 concealcursor=n + syn clear + syn match meaning /;\s*\zs.*/ + syn match hasword /^\x\{8}/ contains=word + syn match word /\<\x\{8}\>/ contains=beginword,endword contained + syn match beginword /\<\x\x/ contained conceal + syn match endword /\x\{6}\>/ contained + hi meaning guibg=blue + hi beginword guibg=green + hi endword guibg=red + redraw! + let line=ScreenChar(winwidth(0),1) + call DoRecordScreen() + " + let g:test ="Test 6: Screenattributes for comment" + $put =g:test + call append('$', ' /* and some more */') + exe "set ft=c ts=7 linebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6" + syntax on + hi SpecialKey term=underline ctermfg=red guifg=red + let attr=[] + nnoremap <expr> GG ":let attr += ['".screenattr(screenrow(),screencol())."']\n" + $ + norm! zt0 + ]]) + feed('GGlGGlGGlGGlGGlGGlGGlGGlGGlGGl') + source([[ + call append('$', ['ScreenAttributes for test6:']) + if attr[0] != attr[1] && attr[1] != attr[3] && attr[3] != attr[5] + call append('$', "Attribut 0 and 1 and 3 and 5 are different!") + else + call append('$', "Not all attributes are different") + endif + set cpo&vim linebreak selection=exclusive + let g:test ="Test 8: set linebreak with visual block mode and v_b_A and selection=exclusive and multibyte char" + $put =g:test + ]]) + feed("Golong line: <Esc>40afoobar <Esc>aTARGETÃ' at end<Esc>") + source([[ + exe "norm! $3B\<C-v>eAx\<Esc>" + " + let g:test ="Test 9: a multibyte sign and colorcolumn" + let attr=[] + let attr2=[] + $put ='' + $put ='a b c' + $put ='a b c' + set list nolinebreak cc=3 + ]]) + feed(':sign define foo text=<C-v>uff0b<CR>') + source([[ + sign place 1 name=foo line=50 buffer=2 + norm! 2kztj + let line1=line('.') + ]]) + feed('0GGlGGlGGlGGl') + source([[ + let line2=line('.') + let attr2=attr + let attr=[] + ]]) + feed('0GGlGGlGGlGGl') + source([[ + redraw! + let line=ScreenChar(winwidth(0),3) + call DoRecordScreen() + " expected: attr[2] is different because of colorcolumn + if attr[0] != attr2[0] || attr[1] != attr2[1] || attr[2] != attr2[2] + call append('$', "Screen attributes are different!") + else + call append('$', "Screen attributes are the same!") + endif ]]) -- Assert buffer contents. @@ -102,6 +185,25 @@ describe('linebreak', function() #define >CDEFGH>----1 #define >_FILE>--------->--->---1 #define >_CONSOLE>---------->---2 - #define >_FILE_AND_CONSOLE>---------3 ]]) + #define >_FILE_AND_CONSOLE>---------3 + bbeeeeee ; some text + + Test 5: set linebreak list listchars and concealing part2 + eeeeee>--->-;>some text + Test 6: Screenattributes for comment + /* and some more */ + ScreenAttributes for test6: + Attribut 0 and 1 and 3 and 5 are different! + Test 8: set linebreak with visual block mode and v_b_A and selection=exclusive and multibyte char + long line: foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar TARGETÃx' at end + + a b c + a b c + + Test 9: a multibyte sign and colorcolumn + ¶ + +a b c¶ + a b c¶ + Screen attributes are the same!]]) end) end) diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 4d377904f9..a0d19926cb 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -2,12 +2,12 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local execute, expect = helpers.execute, helpers.expect +local execute, expect, wait = helpers.execute, helpers.expect, helpers.wait describe('mapping', function() before_each(clear) - it('is working', function() + it('abbreviations with Ñ€ (0x80)', function() insert([[ test starts here: ]]) @@ -16,6 +16,46 @@ describe('mapping', function() execute('inoreab чкпр vim') feed('GAчкпр <esc>') + expect([[ + test starts here: + vim ]]) + end) + + it('Ctrl-c works in Insert mode', function() + -- Mapping of ctrl-c in insert mode + execute('set cpo-=< cpo-=k') + execute('inoremap <c-c> <ctrl-c>') + execute('cnoremap <c-c> dummy') + execute('cunmap <c-c>') + feed('GA<cr>') + feed('TEST2: CTRL-C |') + wait() + feed('<c-c>A|<cr><esc>') + wait() + execute('unmap <c-c>') + execute('unmap! <c-c>') + + expect([[ + + TEST2: CTRL-C |<ctrl-c>A| + ]]) + end) + + it('Ctrl-c works in Visual mode', function() + execute([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]]) + feed('GV') + -- XXX: For some reason the mapping is only triggered + -- when <C-c> is in a separate feed command. + wait() + feed('<c-c>') + execute('vunmap <c-c>') + + expect([[ + + vmap works]]) + end) + + it('langmap', function() -- langmap should not get remapped in insert mode. execute('inoremap { FAIL_ilangmap') execute('set langmap=+{ langnoremap') @@ -37,14 +77,30 @@ describe('mapping', function() -- Assert buffer contents. expect([[ - test starts here: - vim + + + + +]]) end) + it('feedkeys', function() + insert([[ + a b c d + a b c d + ]]) + + -- Vim's issue #212 (feedkeys insert mapping at current position) + execute('nnoremap . :call feedkeys(".", "in")<cr>') + feed('/^a b<cr>') + feed('0qqdw.ifoo<esc>qj0@q<esc>') + execute('unmap .') + expect([[ + fooc d + fooc d + ]]) + end) + it('i_CTRL-G_U', function() -- <c-g>U<cursor> works only within a single line execute('imapclear') diff --git a/test/functional/legacy/marks_spec.lua b/test/functional/legacy/marks_spec.lua new file mode 100644 index 0000000000..8e9ceb1653 --- /dev/null +++ b/test/functional/legacy/marks_spec.lua @@ -0,0 +1,53 @@ +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('marks', function() + before_each(function() + clear() + end) + + it('restores a deleted mark after delete-undo-redo-undo', function() + insert([[ + + textline A + textline B + textline C + + Results:]]) + + execute([[:/^\t/+1]]) + feed([[maddu<C-R>u]]) + source([[ + let g:a = string(getpos("'a")) + $put ='Mark after delete-undo-redo-undo: '.g:a + ]]) + + expect([=[ + + textline A + textline B + textline C + + Results: + Mark after delete-undo-redo-undo: [0, 3, 2, 0]]=]) + end) + + it("CTRL-A and CTRL-X updates last changed mark '[, ']", function() + insert([[ + CTRL-A CTRL-X: + 123 123 123 + 123 123 123 + 123 123 123]]) + + source([[ + /^123/ + execute "normal! \<C-A>`[v`]rAjwvjw\<C-X>`[v`]rX"]]) + + expect([=[ + CTRL-A CTRL-X: + AAA 123 123 + 123 XXXXXXX + XXX 123 123]=]) + end) +end) diff --git a/test/functional/legacy/match_conceal_spec.lua b/test/functional/legacy/match_conceal_spec.lua new file mode 100644 index 0000000000..0ffa3cae7a --- /dev/null +++ b/test/functional/legacy/match_conceal_spec.lua @@ -0,0 +1,228 @@ +-- Test for matchadd() and conceal feature + +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local expect = helpers.expect +local source = helpers.source + +describe('match_conceal', function() + before_each(function() + clear() + + source([[ + set wildchar=^E + 10new + vsp + vert resize 20 + put =\"\#\ This\ is\ a\ Test\" + norm! mazt + + fu! ScreenChar(width, lines) + let c='' + for j in range(1,a:lines) + for i in range(1,a:width) + let c.=nr2char(screenchar(j, i)) + endfor + let c.="\n" + endfor + return c + endfu + + fu! ScreenAttr(line, pos, eval) + let g:attr=[] + for col in a:pos + call add(g:attr, screenattr(a:line,col)) + endfor + " In case all values are zero, probably the terminal + " isn't set correctly, so catch that case + let null = (eval(join(g:attr, '+')) == 0) + let str=substitute(a:eval, '\d\+', 'g:attr[&]', 'g') + if null || eval(str) + let g:attr_test="OK: ". str + else + let g:attr_test="FAILED: ".str + let g:attr_test.="\n". join(g:attr, ' ') + let g:attr_test.="\n TERM: ". &term + endif + endfu + + fu! DoRecordScreen() + wincmd l + $put =printf(\"\n%s\", g:test) + $put =g:line + $put =g:attr_test + wincmd p + endfu + ]]) + end) + + it('is working', function() + source([=[ + let g:test ="Test 1: simple addmatch()" + call matchadd('Conceal', '\%2l ') + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 2: simple addmatch() and conceal (should be: #XThisXisXaXTest)" + norm! 'azt + call clearmatches() + syntax on + set concealcursor=n conceallevel=1 + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 3: addmatch() and conceallevel=3 (should be: #ThisisaTest)" + norm! 'azt + set conceallevel=3 + call clearmatches() + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0==1 && 1==2 && 1==3 && 1==4 && 0!=5") + call DoRecordScreen() + + let g:test ="Test 4: more match() (should be: #Thisisa Test)" + norm! 'azt + call matchadd('ErrorMsg', '\%2l Test', 20, -1, {'conceal': 'X'}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0==1 && 1==2 && 0!=3 && 3==4 && 0!=5 && 3!=5") + call DoRecordScreen() + + let g:test ="Test 5/1: default conceal char (should be: # This is a Test)" + norm! 'azt + call clearmatches() + set conceallevel=1 + call matchadd('Conceal', '\%2l ', 10, -1, {}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + let g:test ="Test 5/2: default conceal char (should be: #+This+is+a+Test)" + norm! 'azt + set listchars=conceal:+ + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + set listchars&vi + + let g:test ="Test 6/1: syn and match conceal (should be: #ZThisZisZaZTest)" + norm! 'azt + call clearmatches() + set conceallevel=1 + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + syn match MyConceal /\%2l / conceal containedin=ALL cchar=* + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + let g:test ="Test 6/2: syn and match conceal (should be: #*This*is*a*Test)" + norm! 'azt + call clearmatches() + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 7/1: clear matches" + norm! 'azt + syn on + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + let a=getmatches() + call clearmatches() + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0==1 && 0==2 && 0==3 && 0==4 && 0==5") + call DoRecordScreen() + $put =a + call setmatches(a) + norm! 'azt + let g:test ="Test 7/2: reset match using setmatches()" + norm! 'azt + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + + let g:test ="Test 8: using matchaddpos() (should be #Pis a Test" + norm! 'azt + call clearmatches() + call matchaddpos('Conceal', [[2,2,6]], 10, -1, {'conceal': 'P'}) + let a=getmatches() + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1!=2 && 0==2 && 0==3 && 0!=4 && 0!=5 && 4==5") + call DoRecordScreen() + $put =a + + let g:test ="Test 9: match using multibyte conceal char (should be: #Ë‘ThisË‘isË‘aË‘Test)" + norm! 'azt + call clearmatches() + call matchadd('Conceal', '\%2l ', 20, -1, {'conceal': "\u02d1"}) + redraw! + let line=ScreenChar(winwidth(0),1) + call ScreenAttr(1,[1,2,7,10,12,16], "0!=1 && 1==2 && 1==3 && 1==4 && 0==5") + call DoRecordScreen() + ]=]) + + expect([=[ + + # This is a Test + + Test 1: simple addmatch() + # This is a Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 2: simple addmatch() and conceal (should be: #XThisXisXaXTest) + #XThisXisXaXTest + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 3: addmatch() and conceallevel=3 (should be: #ThisisaTest) + #ThisisaTest + OK: g:attr[0]==g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]!=g:attr[5] + + Test 4: more match() (should be: #Thisisa Test) + #Thisisa Test + OK: g:attr[0]==g:attr[1] && g:attr[1]==g:attr[2] && g:attr[0]!=g:attr[3] && g:attr[3]==g:attr[4] && g:attr[0]!=g:attr[5] && g:attr[3]!=g:attr[5] + + Test 5/1: default conceal char (should be: # This is a Test) + # This is a Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 5/2: default conceal char (should be: #+This+is+a+Test) + #+This+is+a+Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 6/1: syn and match conceal (should be: #ZThisZisZaZTest) + #ZThisZisZaZTest + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 6/2: syn and match conceal (should be: #*This*is*a*Test) + #*This*is*a*Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 7/1: clear matches + # This is a Test + OK: g:attr[0]==g:attr[1] && g:attr[0]==g:attr[2] && g:attr[0]==g:attr[3] && g:attr[0]==g:attr[4] && g:attr[0]==g:attr[5] + {'group': 'Conceal', 'pattern': '\%2l ', 'priority': 10, 'id': 10, 'conceal': 'Z'} + + Test 7/2: reset match using setmatches() + #ZThisZisZaZTest + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5] + + Test 8: using matchaddpos() (should be #Pis a Test + #Pis a Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]!=g:attr[2] && g:attr[0]==g:attr[2] && g:attr[0]==g:attr[3] && g:attr[0]!=g:attr[4] && g:attr[0]!=g:attr[5] && g:attr[4]==g:attr[5] + {'group': 'Conceal', 'id': 11, 'priority': 10, 'pos1': [2, 2, 6], 'conceal': 'P'} + + Test 9: match using multibyte conceal char (should be: #Ë‘ThisË‘isË‘aË‘Test) + #Ë‘ThisË‘isË‘aË‘Test + OK: g:attr[0]!=g:attr[1] && g:attr[1]==g:attr[2] && g:attr[1]==g:attr[3] && g:attr[1]==g:attr[4] && g:attr[0]==g:attr[5]]=]) + end) +end) diff --git a/test/functional/legacy/options_spec.lua b/test/functional/legacy/options_spec.lua index 773acb9663..21e99c4aa1 100644 --- a/test/functional/legacy/options_spec.lua +++ b/test/functional/legacy/options_spec.lua @@ -1,13 +1,27 @@ --- Test if ":options" throws any exception. The options window seems to mess --- other tests, so restart nvim in the teardown hook - local helpers = require('test.functional.helpers') local command, clear = helpers.command, helpers.clear +local source, expect = helpers.source, helpers.expect describe('options', function() setup(clear) - it('is working', function() + it('should not throw any exception', function() command('options') end) end) + +describe('set', function() + setup(clear) + + it("should keep two comma when 'path' is changed", function() + source([[ + set path=foo,,bar + set path-=bar + set path+=bar + $put =&path]]) + + expect([[ + + foo,,bar]]) + end) +end) diff --git a/test/functional/legacy/quickfix_spec.lua b/test/functional/legacy/quickfix_spec.lua new file mode 100644 index 0000000000..88f86815b3 --- /dev/null +++ b/test/functional/legacy/quickfix_spec.lua @@ -0,0 +1,17 @@ +-- Test for the quickfix commands. + +local helpers = require('test.functional.helpers') +local source, clear = helpers.source, helpers.clear + +describe('helpgrep', function() + before_each(clear) + + it('works', function() + source([[ + helpgrep quickfix + copen + " This wipes out the buffer, make sure that doesn't cause trouble. + cclose + ]]) + end) +end) diff --git a/test/functional/legacy/search_mbyte_spec.lua b/test/functional/legacy/search_mbyte_spec.lua new file mode 100644 index 0000000000..075b24b897 --- /dev/null +++ b/test/functional/legacy/search_mbyte_spec.lua @@ -0,0 +1,26 @@ +local helpers = require('test.functional.helpers') +local insert = helpers.insert +local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect + +describe('search_mbyte', function() + before_each(clear) + + it("search('multi-byte char', 'bce')", function() + insert([=[ + Results: + + Test bce: + A]=]) + + execute('/^Test bce:/+1') + execute([[$put =search('A', 'bce', line('.'))]]) + + -- Assert buffer contents. + expect([=[ + Results: + + Test bce: + A + 4]=]) + end) +end) diff --git a/test/functional/legacy/searchpos_spec.lua b/test/functional/legacy/searchpos_spec.lua new file mode 100644 index 0000000000..1c9b1ccee6 --- /dev/null +++ b/test/functional/legacy/searchpos_spec.lua @@ -0,0 +1,35 @@ +local helpers = require('test.functional.helpers') +local call = helpers.call +local clear = helpers.clear +local execute = helpers.execute +local eq = helpers.eq +local eval = helpers.eval +local insert = helpers.insert + +describe('searchpos', function() + before_each(clear) + + it('is working', function() + insert([[ + 1a3 + 123xyz]]) + + call('cursor', 1, 1) + eq({1, 1, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + call('cursor', 1, 2) + eq({2, 1, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + + execute('set cpo-=c') + call('cursor', 1, 2) + eq({1, 2, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + call('cursor', 1, 3) + eq({1, 3, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + + -- Now with \zs, first match is in column 0, "a" is matched. + call('cursor', 1, 3) + eq({2, 4, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcW')]])) + -- With z flag start at cursor column, don't see the "a". + call('cursor', 1, 3) + eq({2, 4, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcWz')]])) + end) +end) diff --git a/test/functional/legacy/set_spec.lua b/test/functional/legacy/set_spec.lua new file mode 100644 index 0000000000..f81fcd3700 --- /dev/null +++ b/test/functional/legacy/set_spec.lua @@ -0,0 +1,15 @@ +-- Tests for :set + +local helpers = require('test.functional.helpers') +local clear, execute, eval, eq = + helpers.clear, helpers.execute, helpers.eval, helpers.eq + +describe(':set', function() + before_each(clear) + + it('recognizes a trailing comma with +=', function() + execute('set wildignore=*.png,') + execute('set wildignore+=*.jpg') + eq('*.png,*.jpg', eval('&wildignore')) + end) +end) diff --git a/test/functional/legacy/tagcase_spec.lua b/test/functional/legacy/tagcase_spec.lua new file mode 100644 index 0000000000..9a8c6fbe42 --- /dev/null +++ b/test/functional/legacy/tagcase_spec.lua @@ -0,0 +1,150 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local exc_exec = helpers.exc_exec +local expect = helpers.expect +local insert = helpers.insert +local source = helpers.source +local write_file = helpers.write_file + +describe("'tagcase' option", function() + setup(function() + write_file('Xtags', [[ + Bar Xtext 3 + Foo Xtext 2 + foo Xtext 4]]) + end) + + before_each(function() + clear() + source([[ + lang mess C + set tags=Xtags]]) + end) + + teardown(function() + os.remove('Xtags') + end) + + it('should have correct default values', function() + source([[ + set ic& + setg tc& + setl tc& + ]]) + + eq(0, eval('&ic')) + eq('followic', eval('&g:tc')) + eq('followic', eval('&l:tc')) + eq('followic', eval('&tc')) + end) + + it('should accept <empty> only for setlocal', function() + -- Verify that the local setting accepts <empty> but that the global setting + -- does not. The first of these (setting the local value to <empty>) should + -- succeed; the other two should fail. + eq(0, exc_exec('setl tc=')) + eq('Vim(setglobal):E474: Invalid argument: tc=', exc_exec('setg tc=')) + eq('Vim(set):E474: Invalid argument: tc=', exc_exec('set tc=')) + end) + + it("should work with 'ignorecase' correctly in all combinations", function() + -- Verify that the correct number of matching tags is found for all values of + -- 'ignorecase' and global and local values 'tagcase', in all combinations. + insert([[ + + Foo + Bar + foo + + end text]]) + + source([[ + for &ic in [0, 1] + for &g:tc in ["followic", "ignore", "match"] + for &l:tc in ["", "followic", "ignore", "match"] + call append('$', "ic=".&ic." g:tc=".&g:tc." l:tc=".&l:tc." tc=".&tc) + call append('$', len(taglist("^foo$"))) + call append('$', len(taglist("^Foo$"))) + endfor + endfor + endfor + + 1,/^end text$/d]]) + + expect([[ + ic=0 g:tc=followic l:tc= tc=followic + 1 + 1 + ic=0 g:tc=followic l:tc=followic tc=followic + 1 + 1 + ic=0 g:tc=followic l:tc=ignore tc=ignore + 2 + 2 + ic=0 g:tc=followic l:tc=match tc=match + 1 + 1 + ic=0 g:tc=ignore l:tc= tc=ignore + 2 + 2 + ic=0 g:tc=ignore l:tc=followic tc=followic + 1 + 1 + ic=0 g:tc=ignore l:tc=ignore tc=ignore + 2 + 2 + ic=0 g:tc=ignore l:tc=match tc=match + 1 + 1 + ic=0 g:tc=match l:tc= tc=match + 1 + 1 + ic=0 g:tc=match l:tc=followic tc=followic + 1 + 1 + ic=0 g:tc=match l:tc=ignore tc=ignore + 2 + 2 + ic=0 g:tc=match l:tc=match tc=match + 1 + 1 + ic=1 g:tc=followic l:tc= tc=followic + 2 + 2 + ic=1 g:tc=followic l:tc=followic tc=followic + 2 + 2 + ic=1 g:tc=followic l:tc=ignore tc=ignore + 2 + 2 + ic=1 g:tc=followic l:tc=match tc=match + 1 + 1 + ic=1 g:tc=ignore l:tc= tc=ignore + 2 + 2 + ic=1 g:tc=ignore l:tc=followic tc=followic + 2 + 2 + ic=1 g:tc=ignore l:tc=ignore tc=ignore + 2 + 2 + ic=1 g:tc=ignore l:tc=match tc=match + 1 + 1 + ic=1 g:tc=match l:tc= tc=match + 1 + 1 + ic=1 g:tc=match l:tc=followic tc=followic + 2 + 2 + ic=1 g:tc=match l:tc=ignore tc=ignore + 2 + 2 + ic=1 g:tc=match l:tc=match tc=match + 1 + 1]]) + end) +end) diff --git a/test/functional/legacy/textobjects_spec.lua b/test/functional/legacy/textobjects_spec.lua new file mode 100644 index 0000000000..1e8e0b0bcb --- /dev/null +++ b/test/functional/legacy/textobjects_spec.lua @@ -0,0 +1,61 @@ +local helpers = require('test.functional.helpers') +local call = helpers.call +local clear = helpers.clear +local execute = helpers.execute +local expect = helpers.expect +local source = helpers.source + +describe('Text object', function() + before_each(function() + clear() + execute('set shada=') + source([[ + function SelectionOut(data) + new + call setline(1, a:data) + call setreg('"', '') + normal! ggfrmavi)y + $put =getreg('\"') + call setreg('"', '') + normal! `afbmavi)y + $put =getreg('\"') + call setreg('"', '') + normal! `afgmavi)y + $put =getreg('\"') + endfunction + ]]) + end) + + it('Test for vi) without cpo-M', function() + execute('set cpo-=M') + call('SelectionOut', '(red \\(blue) green)') + + expect([[ + (red \(blue) green) + red \(blue + red \(blue + ]]) + end) + + it('Test for vi) with cpo-M #1', function() + execute('set cpo+=M') + call('SelectionOut', '(red \\(blue) green)') + + expect([[ + (red \(blue) green) + red \(blue) green + blue + red \(blue) green]]) + end) + + it('Test for vi) with cpo-M #2', function() + execute('set cpo+=M') + call('SelectionOut', '(red (blue\\) green)') + + expect([[ + (red (blue\) green) + red (blue\) green + blue\ + red (blue\) green]]) + end) +end) diff --git a/test/functional/legacy/undolevels_spec.lua b/test/functional/legacy/undolevels_spec.lua new file mode 100644 index 0000000000..41274b3a04 --- /dev/null +++ b/test/functional/legacy/undolevels_spec.lua @@ -0,0 +1,58 @@ +local helpers = require('test.functional.helpers') +local source, clear = helpers.source, helpers.clear +local eq, nvim = helpers.eq, helpers.meths + +describe('undolevel', function() + setup(clear) + + it('is working', function() + source([[ + func FillBuffer() + for i in range(1,13) + put=i + " Set 'undolevels' to split undo. + exe "setg ul=" . &g:ul + endfor + endfunc + + func Test_global_local_undolevels() + new one + set undolevels=5 + call FillBuffer() + " will only undo the last 5 changes, end up with 13 - (5 + 1) = 7 lines + earlier 10 + call assert_equal(5, &g:undolevels) + call assert_equal(-123456, &l:undolevels) + call assert_equal('7', getline('$')) + + new two + setlocal undolevels=2 + call FillBuffer() + " will only undo the last 2 changes, end up with 13 - (2 + 1) = 10 lines + earlier 10 + call assert_equal(5, &g:undolevels) + call assert_equal(2, &l:undolevels) + call assert_equal('10', getline('$')) + + setlocal ul=10 + call assert_equal(5, &g:undolevels) + call assert_equal(10, &l:undolevels) + + " Setting local value in "two" must not change local value in "one" + wincmd p + call assert_equal(5, &g:undolevels) + call assert_equal(-123456, &l:undolevels) + + new three + setglobal ul=50 + call assert_equal(50, &g:undolevels) + call assert_equal(-123456, &l:undolevels) + + endfunc + + call Test_global_local_undolevels() + ]]) + + eq({}, nvim.get_vvar('errors')) + end) +end) diff --git a/test/functional/legacy/utf8_spec.lua b/test/functional/legacy/utf8_spec.lua index ef717042d0..d33ba6b5fd 100644 --- a/test/functional/legacy/utf8_spec.lua +++ b/test/functional/legacy/utf8_spec.lua @@ -3,9 +3,11 @@ local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local execute, expect = helpers.execute, helpers.expect +local eq, eval = helpers.eq, helpers.eval +local source = helpers.source describe('utf8', function() - setup(clear) + before_each(clear) it('is working', function() insert('start:') @@ -27,4 +29,55 @@ describe('utf8', function() xã‚ã‚ã‚ bxbb]]) end) + + it('strchars()', function() + eq(1, eval('strchars("a")')) + eq(1, eval('strchars("a", 0)')) + eq(1, eval('strchars("a", 1)')) + + eq(3, eval('strchars("ã‚ã„a")')) + eq(3, eval('strchars("ã‚ã„a", 0)')) + eq(3, eval('strchars("ã‚ã„a", 1)')) + + eq(2, eval('strchars("A\\u20dd")')) + eq(2, eval('strchars("A\\u20dd", 0)')) + eq(1, eval('strchars("A\\u20dd", 1)')) + + eq(3, eval('strchars("A\\u20dd\\u20dd")')) + eq(3, eval('strchars("A\\u20dd\\u20dd", 0)')) + eq(1, eval('strchars("A\\u20dd\\u20dd", 1)')) + + eq(1, eval('strchars("\\u20dd")')) + eq(1, eval('strchars("\\u20dd", 0)')) + eq(1, eval('strchars("\\u20dd", 1)')) + end) + + it('customlist completion', function() + source([[ + function! CustomComplete1(lead, line, pos) + return ['ã‚', 'ã„'] + endfunction + command -nargs=1 -complete=customlist,CustomComplete1 Test1 echo]]) + feed(":Test1 <C-L>'<C-B>$put='<CR>") + + source([[ + function! CustomComplete2(lead, line, pos) + return ['ã‚ãŸã—', 'ã‚ãŸã¾', 'ã‚ãŸã‚Šã‚'] + endfunction + command -nargs=1 -complete=customlist,CustomComplete2 Test2 echo]]) + feed(":Test2 <C-L>'<C-B>$put='<CR>") + + source([[ + function! CustomComplete3(lead, line, pos) + return ['Nã“', 'Nã‚“', 'Nã¶'] + endfunction + command -nargs=1 -complete=customlist,CustomComplete3 Test3 echo]]) + feed(":Test3 <C-L>'<C-B>$put='<CR>") + + expect([[ + + Test1 + Test2 ã‚㟠+ Test3 N]]) + end) end) diff --git a/test/functional/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua new file mode 100644 index 0000000000..ba7be8f21b --- /dev/null +++ b/test/functional/legacy/wordcount_spec.lua @@ -0,0 +1,171 @@ +-- Test for wordcount() function + +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute = helpers.clear, helpers.execute +local eq, eval = helpers.eq, helpers.eval + +describe('wordcount', function() + before_each(clear) + + it('is working', function() + execute('set selection=inclusive') + execute('fileformat=unix') + execute('fileformats=unix') + + insert([=[ + RESULT test:]=]) + + execute('new') + source([=[ + function DoRecordWin(...) + wincmd k + if exists("a:1") + call cursor(a:1) + endif + let result=[] + call add(result, getline(1, '$')) + call add(result, wordcount()) + wincmd j + return result + endfunction + ]=]) + + source([=[ + function PutInWindow(args) + wincmd k + %d _ + call append(1, a:args) + wincmd j + endfunction + ]=]) + + source([=[ + function! STL() + if mode() =~? 'V' + let g:visual_stat=wordcount() + endif + return string(wordcount()) + endfunction + ]=]) + + -- Test 1: empty window + eq(eval('DoRecordWin()'), + eval([=[ + [[''], {'chars': 0, 'cursor_chars': 0, 'words': 0, 'cursor_words': 0, 'bytes': 0, 'cursor_bytes': 0}] + ]=]) + ) + + -- Test 2: some words, cursor at start + execute([[call PutInWindow('one two three')]]) + eq(eval('DoRecordWin([1, 1, 0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 1, 'words': 3, 'cursor_words': 0, 'bytes': 15, 'cursor_bytes': 1}] + ]=]) + ) + + -- Test 3: some words, cursor at end + execute([[call PutInWindow('one two three')]]) + eq(eval('DoRecordWin([2, 99, 0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 14}] + ]=]) + ) + + -- Test 4: some words, cursor at end, ve=all + execute('set ve=all') + execute([[call PutInWindow('one two three')]]) + eq(eval('DoRecordWin([2,99,0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 15, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 15}] + ]=]) + ) + execute('set ve=') + + -- Test 5: several lines with words + execute([=[call PutInWindow(['one two three', 'one two three', 'one two three'])]=]) + eq(eval('DoRecordWin([4,99,0])'), + eval([=[ + [['', 'one two three', 'one two three', 'one two three'], {'chars': 43, 'cursor_chars': 42, 'words': 9, 'cursor_words': 9, 'bytes': 43, 'cursor_bytes': 42}] + ]=]) + ) + + -- Test 6: one line with BOM set + execute([[call PutInWindow('one two three')]]) + execute('wincmd k') + execute('set bomb') + execute('wincmd j') + eq(eval('DoRecordWin([2,99,0])'), + eval([=[ + [['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 18, 'cursor_bytes': 14}] + ]=]) + ) + execute('wincmd k') + execute('set nobomb') + execute('wincmd j') + + -- Test 7: one line with multibyte words + execute([=[call PutInWindow(['Äne M¤ne Müh'])]=]) + eq(eval('DoRecordWin([2,99,0])'), + eval([=[ + [['', 'Äne M¤ne Müh'], {'chars': 14, 'cursor_chars': 13, 'words': 3, 'cursor_words': 3, 'bytes': 17, 'cursor_bytes': 16}] + ]=]) + ) + + -- Test 8: several lines with multibyte words + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + eq(eval('DoRecordWin([3,99,0])'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'cursor_chars': 31, 'words': 7, 'cursor_words': 7, 'bytes': 36, 'cursor_bytes': 35}] + ]=]) + ) + + -- Test 9: visual mode, complete buffer + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + execute('wincmd k') + execute('set ls=2 stl=%{STL()}') + -- -- Start visual mode quickly and select complete buffer. + execute('0') + feed('V2jy<cr>') + execute('set stl= ls=1') + execute('let log=DoRecordWin([3,99,0])') + execute('let log[1]=g:visual_stat') + eq(eval('log'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 32, 'visual_words': 7, 'visual_bytes': 36}] + ]=]) + ) + + -- Test 10: visual mode (empty) + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + execute('wincmd k') + execute('set ls=2 stl=%{STL()}') + -- Start visual mode quickly and select complete buffer. + execute('0') + feed('v$y<cr>') + execute('set stl= ls=1') + execute('let log=DoRecordWin([3,99,0])') + execute('let log[1]=g:visual_stat') + eq(eval('log'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 1, 'visual_words': 0, 'visual_bytes': 1}] + ]=]) + ) + + -- Test 11: visual mode, single line + execute([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=]) + execute('wincmd k') + execute('set ls=2 stl=%{STL()}') + -- Start visual mode quickly and select complete buffer. + execute('2') + feed('0v$y<cr>') + execute('set stl= ls=1') + execute('let log=DoRecordWin([3,99,0])') + execute('let log[1]=g:visual_stat') + eq(eval('log'), + eval([=[ + [['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 13, 'visual_words': 3, 'visual_bytes': 16}] + ]=]) + ) + end) +end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua new file mode 100644 index 0000000000..d4c3267997 --- /dev/null +++ b/test/functional/options/defaults_spec.lua @@ -0,0 +1,84 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local execute = helpers.execute + +local function init_session(...) + local args = { helpers.nvim_prog, '-i', 'NONE', '--embed', + '--cmd', 'set shortmess+=I background=light noswapfile noautoindent', + '--cmd', 'set laststatus=1 undodir=. directory=. viewdir=. backupdir=.' + } + for _, v in ipairs({...}) do + table.insert(args, v) + end + helpers.set_session(helpers.spawn(args)) +end + +describe('startup defaults', function() + before_each(function() + clear() + end) + + describe(':filetype', function() + local function expect_filetype(expected) + local screen = Screen.new(48, 4) + screen:attach() + execute('filetype') + screen:expect([[ + ^ | + ~ | + ~ | + ]]..expected + ) + end + + it('enabled by `-u NORC`', function() + init_session('-u', 'NORC') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') + end) + + it('disabled by `-u NONE`', function() + init_session('-u', 'NONE') + expect_filetype( + 'filetype detection:OFF plugin:OFF indent:OFF |') + end) + + it('overridden by early `filetype on`', function() + init_session('-u', 'NORC', '--cmd', 'filetype on') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:OFF |') + end) + + it('overridden by early `filetype plugin on`', function() + init_session('-u', 'NORC', '--cmd', 'filetype plugin on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:OFF |') + end) + + it('overridden by early `filetype indent on`', function() + init_session('-u', 'NORC', '--cmd', 'filetype indent on') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:ON |') + end) + end) + + describe('syntax', function() + it('enabled by `-u NORC`', function() + init_session('-u', 'NORC') + eq(1, eval('g:syntax_on')) + end) + + it('disabled by `-u NONE`', function() + init_session('-u', 'NONE') + eq(0, eval('exists("g:syntax_on")')) + end) + + it('overridden by early `syntax off`', function() + init_session('-u', 'NORC', '--cmd', 'syntax off') + eq(0, eval('exists("g:syntax_on")')) + end) + end) +end) + + diff --git a/test/functional/options/shortmess_spec.lua b/test/functional/options/shortmess_spec.lua new file mode 100644 index 0000000000..4455ef663f --- /dev/null +++ b/test/functional/options/shortmess_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, execute = helpers.clear, helpers.execute + +describe("'shortmess'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + describe('"F" flag', function() + it('hides messages about the files read', function() + execute('e test') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "test" is a directory | + ]]) + execute('set shortmess=F') + execute('e test') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + :e test | + ]]) + end) + end) +end) diff --git a/test/functional/plugin/helpers.lua b/test/functional/plugin/helpers.lua index cc76794267..5b6ea88c34 100644 --- a/test/functional/plugin/helpers.lua +++ b/test/functional/plugin/helpers.lua @@ -25,7 +25,7 @@ local session = nil local reset = function(...) if session then - session:exit(0) + session:close() end session = spawn(nvim_argv(...)) set_session(session) diff --git a/test/functional/plugin/matchparen_spec.lua b/test/functional/plugin/matchparen_spec.lua new file mode 100644 index 0000000000..d8c1f2d392 --- /dev/null +++ b/test/functional/plugin/matchparen_spec.lua @@ -0,0 +1,36 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute + +describe('matchparen', function() + local screen + + before_each(function() + clear() + screen = Screen.new(20,5) + screen:attach() + screen:set_default_attr_ignore( {{bold=true, foreground=Screen.colors.Blue}} ) + end) + + it('uses correct column after i_<Up>. Vim patch 7.4.1296', function() + execute('set noai nosi nocin') + execute('runtime plugin/matchparen.vim') + feed('ivoid f_test()<cr>') + feed('{<cr>') + feed('}') + + -- critical part: up + cr should result in an empty line inbetween the + -- brackets... if the bug is there, the empty line will be before the '{' + feed('<up>') + feed('<cr>') + + screen:expect([[ + void f_test() | + { | + ^ | + } | + {1:-- INSERT --} | + ]], {[1] = {bold = true}}) + + end) +end) diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua index 18ff0f5156..60ba88e55b 100644 --- a/test/functional/plugin/msgpack_spec.lua +++ b/test/functional/plugin/msgpack_spec.lua @@ -1,6 +1,9 @@ local helpers = require('test.functional.helpers') +local meths = helpers.meths local eq, nvim_eval, nvim_command, exc_exec = helpers.eq, helpers.eval, helpers.command, helpers.exc_exec +local ok = helpers.ok +local NIL = helpers.NIL local plugin_helpers = require('test.functional.plugin.helpers') local reset = plugin_helpers.reset @@ -21,10 +24,8 @@ describe('In autoload/msgpack.vim', function() end local nan = -(1.0/0.0-1.0/0.0) - local minus_nan = 1.0/0.0-1.0/0.0 local inf = 1.0/0.0 local minus_inf = -(1.0/0.0) - local has_minus_nan = tostring(nan) ~= tostring(minus_nan) describe('function msgpack#equal', function() local msgpack_eq = function(expected, a, b) @@ -160,9 +161,9 @@ describe('In autoload/msgpack.vim', function() it('compares raw floats correctly', function() msgpack_eq(1, '0.0', '0.0') msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '(1.0/0.0-1.0/0.0)') - if has_minus_nan then - msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') - end + -- both (1.0/0.0-1.0/0.0) and -(1.0/0.0-1.0/0.0) now return + -- str2float('nan'). ref: @18d1ba3422d + msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') msgpack_eq(1, '-(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') msgpack_eq(1, '1.0/0.0', '1.0/0.0') msgpack_eq(1, '-(1.0/0.0)', '-(1.0/0.0)') @@ -178,10 +179,8 @@ describe('In autoload/msgpack.vim', function() it('compares float specials with raw floats correctly', function() msgpack_eq(1, sp('float', '0.0'), '0.0') msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') - if has_minus_nan then - msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') - msgpack_eq(0, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') - end + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') msgpack_eq(1, sp('float', '1.0/0.0'), '1.0/0.0') msgpack_eq(1, sp('float', '-(1.0/0.0)'), '-(1.0/0.0)') @@ -207,10 +206,8 @@ describe('In autoload/msgpack.vim', function() msgpack_eq(0, sp('float', '0.0'), sp('float', '-(1.0/0.0)')) msgpack_eq(0, sp('float', '1.0/0.0'), sp('float', '-(1.0/0.0)')) msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0)')) - if has_minus_nan then - msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), - sp('float', '-(1.0/0.0-1.0/0.0)')) - end + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), + sp('float', '-(1.0/0.0-1.0/0.0)')) msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0-1.0/0.0)')) msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '1.0/0.0')) @@ -392,9 +389,7 @@ describe('In autoload/msgpack.vim', function() string_eq('0.0', sp('float', '0.0')) string_eq('inf', sp('float', '(1.0/0.0)')) string_eq('-inf', sp('float', '-(1.0/0.0)')) - if has_minus_nan then - string_eq('-nan', sp('float', '(1.0/0.0-1.0/0.0)')) - end + string_eq('nan', sp('float', '(1.0/0.0-1.0/0.0)')) string_eq('nan', sp('float', '-(1.0/0.0-1.0/0.0)')) string_eq('FALSE', sp('boolean', '0')) string_eq('TRUE', sp('boolean', '1')) @@ -413,11 +408,15 @@ describe('In autoload/msgpack.vim', function() string_eq('0.0', '0.0') string_eq('inf', '(1.0/0.0)') string_eq('-inf', '-(1.0/0.0)') - if has_minus_nan then - string_eq('-nan', '(1.0/0.0-1.0/0.0)') - end + string_eq('nan', '(1.0/0.0-1.0/0.0)') string_eq('nan', '-(1.0/0.0-1.0/0.0)') end) + + it('works for special v: values like v:true', function() + string_eq('TRUE', 'v:true') + string_eq('FALSE', 'v:false') + string_eq('NIL', 'v:null') + end) end) describe('function msgpack#deepcopy', function() @@ -532,6 +531,20 @@ describe('In autoload/msgpack.vim', function() eq(2.0, nvim_eval('flt2')) eq('abc', nvim_eval('bin2')) end) + + it('works for special v: values like v:true', function() + meths.set_var('true', true) + meths.set_var('false', false) + meths.set_var('nil', NIL) + + nvim_command('let true2 = msgpack#deepcopy(true)') + nvim_command('let false2 = msgpack#deepcopy(false)') + nvim_command('let nil2 = msgpack#deepcopy(nil)') + + eq(true, meths.get_var('true')) + eq(false, meths.get_var('false')) + eq(NIL, meths.get_var('nil')) + end) end) describe('function msgpack#eval', function() @@ -547,8 +560,11 @@ describe('In autoload/msgpack.vim', function() end if expected_val_full == expected_val_full then eq(expected_val_full, nvim_eval('g:__val')) - else - eq(tostring(expected_val_full), tostring(nvim_eval('g:__val'))) + else -- NaN + local nvim_nan = tostring(nvim_eval('g:__val')) + -- -NaN is a hardware-specific detail, there's no need to test for it. + -- Accept ether 'nan' or '-nan' as the response. + ok(nvim_nan == 'nan' or nvim_nan == '-nan') end nvim_command('unlet g:__val') end @@ -615,7 +631,6 @@ describe('In autoload/msgpack.vim', function() eval_eq('float', inf, 'inf') eval_eq('float', minus_inf, '-inf') eval_eq('float', nan, 'nan') - eval_eq('float', minus_nan, '-nan') eval_eq('float', 1.0e10, '1.0e10') eval_eq('float', 1.0e10, '1.0e+10') eval_eq('float', -1.0e10, '-1.0e+10') diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 4100a30452..aad0e366bf 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -4,7 +4,7 @@ local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf = helpers.funcs, helpers.feed, helpers.curbuf local neq = helpers.neq -local msgpack = require('MessagePack') +local mpack = require('mpack') local plugin_helpers = require('test.functional.plugin.helpers') local reset = plugin_helpers.reset @@ -15,13 +15,13 @@ local get_shada_rw = shada_helpers.get_shada_rw local mpack_eq = function(expected, mpack_result) local mpack_keys = {'type', 'timestamp', 'length', 'value'} - local unpacker = msgpack.unpacker(mpack_result) + local unpack = mpack.Unpacker() local actual = {} - local cur + local cur, val local i = 0 - while true do - local off, val = unpacker() - if not off then break end + local off = 1 + while off <= #mpack_result do + val, off = unpack(mpack_result, off) if i % 4 == 0 then cur = {} actual[#actual + 1] = cur @@ -78,36 +78,6 @@ describe('In autoload/shada.vim', function() return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val) end - local st_meta = { - __pairs=function(table) - local ret = {} - local next_key = nil - local num_keys = 0 - while true do - next_key = next(table, next_key) - if next_key == nil then - break - end - num_keys = num_keys + 1 - ret[num_keys] = {next_key, table[next_key]} - end - table.sort(ret, function(a, b) - return a[1] < b[1] - end) - local state = {i=0} - return (function(state_, _) - state_.i = state_.i + 1 - if ret[state_.i] then - return table.unpack(ret[state_.i]) - end - end), state - end - } - - local st = function(table) - return setmetatable(table, st_meta) - end - describe('function shada#mpack_to_sd', function() local mpack2sd = function(arg) return ('shada#mpack_to_sd(%s)'):format(arg) @@ -184,7 +154,7 @@ describe('In autoload/shada.vim', function() ' + b 2', ' + c column 3', ' + d 4', - }, {{type=1, timestamp=0, data=st({a=1, b=2, c=3, d=4})}}) + }, {{type=1, timestamp=0, data={a=1, b=2, c=3, d=4}}}) sd2strings_eq({ 'Header with timestamp ' .. epoch .. ':', ' % Key Value', @@ -2215,7 +2185,7 @@ describe('In plugin/shada.vim', function() describe('event BufWriteCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2271,7 +2241,7 @@ describe('In plugin/shada.vim', function() describe('event FileWriteCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2310,7 +2280,7 @@ describe('In plugin/shada.vim', function() describe('event FileAppendCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2512,7 +2482,7 @@ describe('syntax/shada.vim', function() it('works', function() nvim_command('syntax on') nvim_command('setlocal syntax=shada') - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Header with timestamp ' .. epoch .. ':', ' % Key Value', ' + t "test"', @@ -2848,3 +2818,4 @@ describe('syntax/shada.vim', function() eq(exp, act) end) end) + diff --git a/test/functional/preload.lua b/test/functional/preload.lua index 5f34f7fa6e..1971ef77cc 100644 --- a/test/functional/preload.lua +++ b/test/functional/preload.lua @@ -1,5 +1,4 @@ -- Modules loaded here will not be cleared and reloaded by Busted. -- Busted started doing this to help provide more isolation. See issue #62 -- for more information about this. -local ffi = require('ffi') local helpers = require('test.functional.helpers') diff --git a/test/functional/provider/define_spec.lua b/test/functional/provider/define_spec.lua index 6e8a3b89cd..c30ad6d8c2 100644 --- a/test/functional/provider/define_spec.lua +++ b/test/functional/provider/define_spec.lua @@ -64,153 +64,137 @@ local function command_specs_for(fn, sync, first_arg_factory, init) args = args..', "RpcCommand"' end) - describe('without options', function() - it('ok', function() - call(fn, args..', {}') - local function on_setup() - command('RpcCommand') - end + it('without options', function() + call(fn, args..', {}') + local function on_setup() + command('RpcCommand') + end - local function handler(method) - eq('test-handler', method) - return '' - end + local function handler(method) + eq('test-handler', method) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs', function() - it('ok', function() - call(fn, args..', {"nargs": "*"}') - local function on_setup() - command('RpcCommand arg1 arg2 arg3') - end + it('with nargs', function() + call(fn, args..', {"nargs": "*"}') + local function on_setup() + command('RpcCommand arg1 arg2 arg3') + end - local function handler(method, arguments) - eq('test-handler', method) - eq({'arg1', 'arg2', 'arg3'}, arguments[1]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg1', 'arg2', 'arg3'}, arguments[1]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with range', function() - it('ok', function() - call(fn,args..', {"range": ""}') - local function on_setup() - command('1,1RpcCommand') - end + it('with range', function() + call(fn,args..', {"range": ""}') + local function on_setup() + command('1,1RpcCommand') + end - local function handler(method, arguments) - eq('test-handler', method) - eq({1, 1}, arguments[1]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({1, 1}, arguments[1]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/range', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": ""}') - local function on_setup() - command('1,1RpcCommand arg') - end + it('with nargs/range', function() + call(fn, args..', {"nargs": "1", "range": ""}') + local function on_setup() + command('1,1RpcCommand arg') + end - local function handler(method, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq({1, 1}, arguments[2]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq({1, 1}, arguments[2]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5"}') - local function on_setup() - command('5RpcCommand arg') - end + it('with nargs/count', function() + call(fn, args..', {"nargs": "1", "range": "5"}') + local function on_setup() + command('5RpcCommand arg') + end - local function handler(method, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq(5, arguments[2]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count/bang', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": ""}') - local function on_setup() - command('5RpcCommand! arg') - end + it('with nargs/count/bang', function() + call(fn, args..', {"nargs": "1", "range": "5", "bang": ""}') + local function on_setup() + command('5RpcCommand! arg') + end - local function handler(method, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq(5, arguments[2]) - eq(1, arguments[3]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + eq(1, arguments[3]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count/bang/register', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. - ' "register": ""}') - local function on_setup() - command('5RpcCommand! b arg') - end + it('with nargs/count/bang/register', function() + call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. + ' "register": ""}') + local function on_setup() + command('5RpcCommand! b arg') + end - local function handler(method, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq(5, arguments[2]) - eq(1, arguments[3]) - eq('b', arguments[4]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + eq(1, arguments[3]) + eq('b', arguments[4]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with nargs/count/bang/register/eval', function() - it('ok', function() - call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. - ' "register": "", "eval": "@<reg>"}') - local function on_setup() - command('let @b = "regb"') - command('5RpcCommand! b arg') - end + it('with nargs/count/bang/register/eval', function() + call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. + ' "register": "", "eval": "@<reg>"}') + local function on_setup() + command('let @b = "regb"') + command('5RpcCommand! b arg') + end - local function handler(method, arguments) - eq('test-handler', method) - eq({'arg'}, arguments[1]) - eq(5, arguments[2]) - eq(1, arguments[3]) - eq('b', arguments[4]) - eq('regb', arguments[5]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({'arg'}, arguments[1]) + eq(5, arguments[2]) + eq(1, arguments[3]) + eq('b', arguments[4]) + eq('regb', arguments[5]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) end) end) @@ -236,37 +220,33 @@ local function autocmd_specs_for(fn, sync, first_arg_factory, init) args = args..', "BufEnter"' end) - describe('without options', function() - it('ok', function() - call(fn, args..', {}') - local function on_setup() - command('doautocmd BufEnter x.c') - end + it('without options', function() + call(fn, args..', {}') + local function on_setup() + command('doautocmd BufEnter x.c') + end - local function handler(method) - eq('test-handler', method) - return '' - end + local function handler(method) + eq('test-handler', method) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with eval', function() - it('ok', function() - call(fn, args..[[, {'eval': 'expand("<afile>")'}]]) - local function on_setup() - command('doautocmd BufEnter x.c') - end + it('with eval', function() + call(fn, args..[[, {'eval': 'expand("<afile>")'}]]) + local function on_setup() + command('doautocmd BufEnter x.c') + end - local function handler(method, arguments) - eq('test-handler', method) - eq('x.c', arguments[1]) - return '' - end + local function handler(method, arguments) + eq('test-handler', method) + eq('x.c', arguments[1]) + return '' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) end) end) @@ -292,46 +272,77 @@ local function function_specs_for(fn, sync, first_arg_factory, init) args = args..', "TestFunction"' end) - describe('without options', function() - it('ok', function() - call(fn, args..', {}') - local function on_setup() - if sync then - eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) - else - eq(1, eval('TestFunction(1, "a", ["b", "c"])')) - end + it('without options', function() + call(fn, args..', {}') + local function on_setup() + if sync then + eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) + else + eq(1, eval('TestFunction(1, "a", ["b", "c"])')) end + end - local function handler(method, arguments) - eq('test-handler', method) - eq({{1, 'a', {'b', 'c'}}}, arguments) - return 'rv' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}}, arguments) + return 'rv' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) - describe('with eval', function() - it('ok', function() - call(fn, args..[[, {'eval': '2 + 2'}]]) - local function on_setup() - if sync then - eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) - else - eq(1, eval('TestFunction(1, "a", ["b", "c"])')) - end + it('with eval', function() + call(fn, args..[[, {'eval': '2 + 2'}]]) + local function on_setup() + if sync then + eq('rv', eval('TestFunction(1, "a", ["b", "c"])')) + else + eq(1, eval('TestFunction(1, "a", ["b", "c"])')) end + end - local function handler(method, arguments) - eq('test-handler', method) - eq({{1, 'a', {'b', 'c'}}, 4}, arguments) - return 'rv' - end + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}, 4}, arguments) + return 'rv' + end + + runx(sync, handler, on_setup) + end) + + it('with range', function() + helpers.insert([[ + foo + bar + baz + zub]]) + call(fn, args..[[, {'range': ''}]]) + local function on_setup() + command('2,3call TestFunction(1, "a", ["b", "c"])') + end + + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}, {2, 3}}, arguments) + return 'rv' + end + + runx(sync, handler, on_setup) + end) + + it('with eval/range', function() + call(fn, args..[[, {'eval': '4', 'range': ''}]]) + local function on_setup() + command('%call TestFunction(1, "a", ["b", "c"])') + end + + local function handler(method, arguments) + eq('test-handler', method) + eq({{1, 'a', {'b', 'c'}}, {1, 1}, 4}, arguments) + return 'rv' + end - runx(sync, handler, on_setup) - end) + runx(sync, handler, on_setup) end) end) end) diff --git a/test/functional/shada/compatibility_spec.lua b/test/functional/shada/compatibility_spec.lua index 2ca0b16e75..1fa88c58e5 100644 --- a/test/functional/shada/compatibility_spec.lua +++ b/test/functional/shada/compatibility_spec.lua @@ -270,9 +270,7 @@ describe('ShaDa forward compatibility support code', function() it('works with register item with type 10', function() wshada('\005\001\019\132\161na\162rX\194\162rc\145\196\001-\162rt\010') eq(0, exc_exec(sdrcmd(true))) - -- getreg may return empty list as list with NULL pointer which API - -- translates into nil for some reason. - eq({}, funcs.getreg('a', 1, 1) or {}) + eq({}, funcs.getreg('a', 1, 1)) eq('', funcs.getregtype('a')) nvim_command('wshada ' .. shada_fname) local found = 0 diff --git a/test/functional/shada/errors_spec.lua b/test/functional/shada/errors_spec.lua index 62b9e6c84d..e7951ee74c 100644 --- a/test/functional/shada/errors_spec.lua +++ b/test/functional/shada/errors_spec.lua @@ -1,6 +1,7 @@ -- ShaDa errors handling support local helpers = require('test.functional.helpers') -local nvim_command, eq, exc_exec = helpers.command, helpers.eq, helpers.exc_exec +local nvim_command, eq, exc_exec, redir_exec = + helpers.command, helpers.eq, helpers.exc_exec, helpers.redir_exec local shada_helpers = require('test.functional.shada.helpers') local reset, clear, get_shada_rw = @@ -492,4 +493,21 @@ $ eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 47 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) eq(0, exc_exec('wshada! ' .. shada_fname)) end) + + it('errors when a funcref is stored in a variable', function() + nvim_command('let F = function("tr")') + nvim_command('set shada+=!') + eq('\nE951: Error while dumping variable g:F, itself: attempt to dump function reference' + .. '\nE574: Failed to write variable F', + redir_exec('wshada')) + end) + + it('errors when a self-referencing list is stored in a variable', function() + nvim_command('let L = []') + nvim_command('call add(L, L)') + nvim_command('set shada+=!') + eq('\nE952: Unable to dump variable g:L: container references itself in index 0' + .. '\nE574: Failed to write variable L', + redir_exec('wshada')) + end) end) diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/helpers.lua index 146ae8d51e..d4eb7f57bd 100644 --- a/test/functional/shada/helpers.lua +++ b/test/functional/shada/helpers.lua @@ -3,7 +3,7 @@ local spawn, set_session, meths, nvim_prog = helpers.spawn, helpers.set_session, helpers.meths, helpers.nvim_prog local write_file, merge_args = helpers.write_file, helpers.merge_args -local msgpack = require('MessagePack') +local mpack = require('mpack') local tmpname = os.tmpname() local additional_cmd = '' @@ -20,14 +20,8 @@ local function nvim_argv() end end -local session = nil - local reset = function() - if session then - session:exit(0) - end - session = spawn(nvim_argv()) - set_session(session) + set_session(spawn(nvim_argv())) meths.set_var('tmpname', tmpname) end @@ -66,13 +60,13 @@ local read_shada_file = function(fname) local fd = io.open(fname, 'r') local mstring = fd:read('*a') fd:close() - local unpacker = msgpack.unpacker(mstring) + local unpack = mpack.Unpacker() local ret = {} - local cur + local cur, val local i = 0 - while true do - local off, val = unpacker() - if not off then break end + local off = 1 + while off <= #mstring do + val, off = unpack(mstring, off) if i % 4 == 0 then cur = {} ret[#ret + 1] = cur diff --git a/test/functional/shada/registers_spec.lua b/test/functional/shada/registers_spec.lua index f0133b1086..4043d94a69 100644 --- a/test/functional/shada/registers_spec.lua +++ b/test/functional/shada/registers_spec.lua @@ -43,9 +43,9 @@ describe('ShaDa support code', function() setreg('b', {'bca', 'abc', 'cba'}, 'b3') nvim_command('qall') reset() - eq({nil, ''}, getreg('c')) - eq({nil, ''}, getreg('l')) - eq({nil, ''}, getreg('b')) + eq({{}, ''}, getreg('c')) + eq({{}, ''}, getreg('l')) + eq({{}, ''}, getreg('b')) end) it('does restore registers with zero <', function() @@ -67,9 +67,9 @@ describe('ShaDa support code', function() setreg('b', {'bca', 'abc', 'cba'}, 'b3') nvim_command('qall') reset() - eq({nil, ''}, getreg('c')) - eq({nil, ''}, getreg('l')) - eq({nil, ''}, getreg('b')) + eq({{}, ''}, getreg('c')) + eq({{}, ''}, getreg('l')) + eq({{}, ''}, getreg('b')) end) it('does restore registers with zero "', function() @@ -103,7 +103,7 @@ describe('ShaDa support code', function() nvim_command('qall') reset() eq({{'d'}, 'v'}, getreg('o')) - eq({nil, ''}, getreg('t')) + eq({{}, ''}, getreg('t')) end) it('does limit number of lines according to "', function() @@ -113,7 +113,7 @@ describe('ShaDa support code', function() nvim_command('qall') reset() eq({{'d'}, 'v'}, getreg('o')) - eq({nil, ''}, getreg('t')) + eq({{}, ''}, getreg('t')) end) it('does limit number of lines according to < rather then "', function() @@ -125,7 +125,7 @@ describe('ShaDa support code', function() reset() eq({{'d'}, 'v'}, getreg('o')) eq({{'a', 'b', 'cde'}, 'V'}, getreg('t')) - eq({nil, ''}, getreg('h')) + eq({{}, ''}, getreg('h')) end) it('dumps and loads register correctly when &encoding is not UTF-8', diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index 2bc855a239..683d520627 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -8,7 +8,7 @@ local write_file, spawn, set_session, nvim_prog, exc_exec = local lfs = require('lfs') local paths = require('test.config.paths') -local msgpack = require('MessagePack') +local mpack = require('mpack') local shada_helpers = require('test.functional.shada.helpers') local reset, clear, get_shada_rw = @@ -107,7 +107,7 @@ describe('ShaDa support code', function() end) it('reads correctly various timestamps', function() - local mpack = { + local msgpack = { '\100', -- Positive fixnum 100 '\204\255', -- uint 8 255 '\205\010\003', -- uint 16 2563 @@ -116,23 +116,23 @@ describe('ShaDa support code', function() } local s = '\100' local e = '\001\192' - wshada(s .. table.concat(mpack, e .. s) .. e) + wshada(s .. table.concat(msgpack, e .. s) .. e) eq(0, exc_exec('wshada ' .. shada_fname)) local found = 0 - local typ = select(2, msgpack.unpacker(s)()) + local typ = mpack.unpack(s) for _, v in ipairs(read_shada_file(shada_fname)) do if v.type == typ then found = found + 1 - eq(select(2, msgpack.unpacker(mpack[found])()), v.timestamp) + eq(mpack.unpack(msgpack[found]), v.timestamp) end end - eq(#mpack, found) + eq(#msgpack, found) end) it('does not write NONE file', function() local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', '--cmd', 'qall'}, true) - session:exit(0) + session:close() eq(nil, lfs.attributes('NONE')) eq(nil, lfs.attributes('NONE.tmp.a')) end) @@ -143,7 +143,7 @@ describe('ShaDa support code', function() true) set_session(session) eq('', funcs.getreg('a')) - session:exit(0) + session:close() os.remove('NONE') end) @@ -174,6 +174,7 @@ describe('ShaDa support code', function() nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) local readme_fname = paths.test_source_path .. '/README.md' + readme_fname = helpers.eval( 'resolve("' .. readme_fname .. '")' ) eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r~') nvim_command('wshada! ' .. shada_fname) diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua index 6225971e5f..7ceeafdc71 100644 --- a/test/functional/shada/variables_spec.lua +++ b/test/functional/shada/variables_spec.lua @@ -1,7 +1,7 @@ -- ShaDa variables saving/reading support local helpers = require('test.functional.helpers') -local meths, funcs, nvim_command, eq = - helpers.meths, helpers.funcs, helpers.command, helpers.eq +local meths, funcs, nvim_command, eq, exc_exec = + helpers.meths, helpers.funcs, helpers.command, helpers.eq, helpers.exc_exec local shada_helpers = require('test.functional.shada.helpers') local reset, set_additional_cmd, clear = @@ -22,12 +22,17 @@ describe('ShaDa support code', function() eq('foo', meths.get_var('STRVAR')) end) - local autotest = function(tname, varname, varval) + local autotest = function(tname, varname, varval, val_is_expr) it('is able to dump and read back ' .. tname .. ' variable automatically', function() set_additional_cmd('set shada+=!') reset() - meths.set_var(varname, varval) + if val_is_expr then + nvim_command('let g:' .. varname .. ' = ' .. varval) + varval = meths.get_var(varname) + else + meths.set_var(varname, varval) + end -- Exit during `reset` is not a regular exit: it does not write shada -- automatically nvim_command('qall') @@ -41,6 +46,10 @@ describe('ShaDa support code', function() autotest('float', 'FLTVAR', 42.5) autotest('dictionary', 'DCTVAR', {a=10}) autotest('list', 'LSTVAR', {{a=10}, {b=10.5}, {c='str'}}) + autotest('true', 'TRUEVAR', true) + autotest('false', 'FALSEVAR', false) + autotest('null', 'NULLVAR', 'v:null', true) + autotest('ext', 'EXTVAR', '{"_TYPE": v:msgpack_types.ext, "_VAL": [2, ["", ""]]}', true) it('does not read back variables without `!` in &shada', function() meths.set_var('STRVAR', 'foo') @@ -136,4 +145,31 @@ describe('ShaDa support code', function() eq({['\171']={{'\171'}, {['\171']='\171'}, {a='Test'}}}, meths.get_var('NESTEDVAR')) end) + + it('errors and writes when a funcref is stored in a variable', + function() + nvim_command('let F = function("tr")') + meths.set_var('U', '10') + nvim_command('set shada+=!') + set_additional_cmd('set shada+=!') + eq('Vim(wshada):E951: Error while dumping variable g:F, itself: attempt to dump function reference', + exc_exec('wshada')) + meths.set_option('shada', '') + reset() + eq('10', meths.get_var('U')) + end) + + it('errors and writes when a self-referencing list is stored in a variable', + function() + meths.set_var('L', {}) + nvim_command('call add(L, L)') + meths.set_var('U', '10') + nvim_command('set shada+=!') + eq('Vim(wshada):E952: Unable to dump variable g:L: container references itself in index 0', + exc_exec('wshada')) + meths.set_option('shada', '') + set_additional_cmd('set shada+=!') + reset() + eq('10', meths.get_var('U')) + end) end) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 55ef254a63..cefb603a7e 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -158,8 +158,7 @@ describe('terminal buffer', function() end) it('handles loss of focus gracefully', function() - -- Temporarily change the statusline to avoid printing the file name, which - -- varies be where the test is run. + -- Change the statusline to avoid printing the file name, which varies. nvim('set_option', 'statusline', '==========') execute('set laststatus=0') @@ -195,5 +194,15 @@ describe('terminal buffer', function() execute('set laststatus=1') -- Restore laststatus to the default. end) + + it('term_close() use-after-free #4393', function() + if eval("executable('yes')") == 0 then + pending('missing "yes" command') + return + end + execute('terminal yes') + feed([[<C-\><C-n>]]) + execute('bdelete!') + end) end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index e9cb010003..c15da2f760 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -59,7 +59,7 @@ describe('terminal cursor', function() ]]) end) - it('is positioned correctly when focused', function() + pending('is positioned correctly when focused', function() feed('i') screen:expect([[ 1 tty ready | diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua new file mode 100644 index 0000000000..dcc4a54610 --- /dev/null +++ b/test/functional/terminal/edit_spec.lua @@ -0,0 +1,75 @@ +local helpers = require('test.functional.helpers') +local screen = require('test.functional.ui.screen') + +local curbufmeths = helpers.curbufmeths +local curwinmeths = helpers.curwinmeths +local nvim_dir = helpers.nvim_dir +local command = helpers.command +local meths = helpers.meths +local clear = helpers.clear +local eq = helpers.eq + +describe(':edit term://*', function() + local get_screen = function(columns, lines) + local scr = screen.new(columns, lines) + scr:attach(false) + return scr + end + + before_each(function() + clear() + meths.set_option('shell', nvim_dir .. '/shell-test') + meths.set_option('shellcmdflag', 'EXE') + end) + + it('runs TermOpen event', function() + meths.set_var('termopen_runs', {}) + command('autocmd TermOpen * :call add(g:termopen_runs, expand("<amatch>"))') + command('edit term://') + local termopen_runs = meths.get_var('termopen_runs') + eq(1, #termopen_runs) + eq(termopen_runs[1], termopen_runs[1]:match('^term://.//%d+:$')) + end) + + it('runs TermOpen early enough to respect terminal_scrollback_buffer_size', function() + local columns, lines = 20, 4 + local scr = get_screen(columns, lines) + local rep = 'a' + meths.set_option('shellcmdflag', 'REP ' .. rep) + local rep_size = rep:byte() + local sb = 10 + local gsb = 20 + meths.set_var('terminal_scrollback_buffer_size', gsb) + command('autocmd TermOpen * :let b:terminal_scrollback_buffer_size = ' + .. tostring(sb)) + command('edit term://foobar') + local bufcontents = {} + local winheight = curwinmeths.get_height() + -- I have no idea why there is + 4 needed. But otherwise it works fine with + -- different scrollbacks. + local shift = -4 + local buf_cont_start = rep_size - 1 - sb - winheight - shift + local bufline = function(i) return ('%d: foobar'):format(i) end + for i = buf_cont_start,(rep_size - 1) do + bufcontents[#bufcontents + 1] = bufline(i) + end + bufcontents[#bufcontents + 1] = '' + bufcontents[#bufcontents + 1] = '[Process exited 0]' + -- Do not ask me why displayed screen is one line *before* buffer + -- contents: buffer starts with 87:, screen with 86:. + local exp_screen = '\n' + local did_cursor = false + for i = 0,(winheight - 1) do + local line = bufline(buf_cont_start + i - 1) + exp_screen = (exp_screen + .. (did_cursor and '' or '^') + .. line + .. (' '):rep(columns - #line) + .. '|\n') + did_cursor = true + end + exp_screen = exp_screen .. (' '):rep(columns) .. '|\n' + scr:expect(exp_screen) + eq(bufcontents, curbufmeths.get_lines(1, -1, true)) + end) +end) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 493539b4d3..d89092ff27 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -1,15 +1,15 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local clear, wait, nvim = helpers.clear, helpers.wait, helpers.nvim -local nvim_dir = helpers.nvim_dir -local execute = helpers.execute +local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq +local execute, eval = helpers.execute, helpers.eval describe(':terminal', function() local screen before_each(function() clear() - screen = Screen.new(50, 7) + screen = Screen.new(50, 4) screen:attach(false) nvim('set_option', 'shell', nvim_dir..'/shell-test') nvim('set_option', 'shellcmdflag', 'EXE') @@ -23,9 +23,6 @@ describe(':terminal', function() ready $ | [Process exited 0] | | - | - | - | -- TERMINAL -- | ]]) end) @@ -37,9 +34,6 @@ describe(':terminal', function() ready $ echo hi | | [Process exited 0] | - | - | - | -- TERMINAL -- | ]]) end) @@ -51,10 +45,15 @@ describe(':terminal', function() ready $ echo 'hello' \ "world" | | [Process exited 0] | - | - | - | -- TERMINAL -- | ]]) end) + + it('ex_terminal() double-free #4554', function() + source([[ + autocmd BufNew * set shell=foo + terminal]]) + -- Verify that BufNew actually fired (else the test is invalid). + eq('foo', eval('&shell')) + end) end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 14700a2622..364ca327a4 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -41,7 +41,7 @@ describe('tui', function() -- INSERT -- | -- TERMINAL -- | ]]) - feed('\x1b') + feed('\027') screen:expect([[ abc | test1 | @@ -53,11 +53,11 @@ describe('tui', function() ]]) end) - it('interprets leading esc byte as the alt modifier', function() + it('interprets leading <Esc> byte as ALT modifier in normal-mode', function() local keys = 'dfghjkl' for c in keys:gmatch('.') do execute('nnoremap <a-'..c..'> ialt-'..c..'<cr><esc>') - feed('\x1b'..c) + feed('\027'..c) end screen:expect([[ alt-j | @@ -80,11 +80,30 @@ describe('tui', function() ]]) end) + it('does not mangle unmapped ALT-key chord', function() + -- Vim represents ALT/META by setting the "high bit" of the modified key; + -- we do _not_. #3982 + -- + -- Example: for input ALT+j: + -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê". + -- * Nvim (after #3982) inserts "j". + feed('i\027j') + screen:expect([[ + j{1: } | + ~ | + ~ | + ~ | + [No Name] [+] | + -- INSERT -- | + -- TERMINAL -- | + ]]) + end) + it('accepts ascii control sequences', function() feed('i') - feed('\x16\x07') -- ctrl+g - feed('\x16\x16') -- ctrl+v - feed('\x16\x0d') -- ctrl+m + feed('\022\007') -- ctrl+g + feed('\022\022') -- ctrl+v + feed('\022\013') -- ctrl+m screen:expect([[ {3:^G^V^M}{1: } | ~ | @@ -97,7 +116,7 @@ describe('tui', function() end) it('automatically sends <Paste> for bracketed paste sequences', function() - feed('i\x1b[200~') + feed('i\027[200~') screen:expect([[ {1: } | ~ | @@ -117,7 +136,7 @@ describe('tui', function() -- INSERT (paste) -- | -- TERMINAL -- | ]]) - feed('\x1b[201~') + feed('\027[201~') screen:expect([[ pasted from terminal{1: } | ~ | @@ -135,9 +154,7 @@ describe('tui', function() for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end - feed('i\x1b[200~') - feed(table.concat(t, '\n')) - feed('\x1b[201~') + feed('i\027[200~'..table.concat(t, '\n')..'\027[201~') screen:expect([[ item 2997 | item 2998 | @@ -185,7 +202,7 @@ describe('tui focus event handling', function() end) it('can handle focus events in normal mode', function() - feed('\x1b[I') + feed('\027[I') screen:expect([[ {1: } | ~ | @@ -196,7 +213,7 @@ describe('tui focus event handling', function() -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ {1: } | ~ | @@ -211,7 +228,7 @@ describe('tui focus event handling', function() it('can handle focus events in insert mode', function() execute('set noshowmode') feed('i') - feed('\x1b[I') + feed('\027[I') screen:expect([[ {1: } | ~ | @@ -221,7 +238,7 @@ describe('tui focus event handling', function() gained | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ {1: } | ~ | @@ -235,7 +252,7 @@ describe('tui focus event handling', function() it('can handle focus events in cmdline mode', function() feed(':') - feed('\x1b[I') + feed('\027[I') screen:expect([[ | ~ | @@ -245,7 +262,7 @@ describe('tui focus event handling', function() g{1:a}ined | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ | ~ | @@ -262,7 +279,7 @@ describe('tui focus event handling', function() execute('set laststatus=0') execute('set noshowmode') execute('terminal') - feed('\x1b[I') + feed('\027[I') screen:expect([[ ready $ | [Process exited 0]{1: } | @@ -272,7 +289,7 @@ describe('tui focus event handling', function() gained | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ ready $ | [Process exited 0]{1: } | diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua new file mode 100644 index 0000000000..58f5b11de0 --- /dev/null +++ b/test/functional/ui/bufhl_spec.lua @@ -0,0 +1,261 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local execute, request, neq = helpers.execute, helpers.request, helpers.neq + + +describe('Buffer highlighting', function() + local screen + local curbuf + + local hl_colors = { + NonText = Screen.colors.Blue, + Question = Screen.colors.SeaGreen, + String = Screen.colors.Fuchsia, + Statement = Screen.colors.Brown, + Special = Screen.colors.SlateBlue, + Identifier = Screen.colors.DarkCyan + } + + before_each(function() + clear() + execute("syntax on") + screen = Screen.new(40, 8) + screen:attach() + screen:set_default_attr_ignore( {{bold=true, foreground=hl_colors.NonText}} ) + screen:set_default_attr_ids({ + [1] = {foreground = hl_colors.String}, + [2] = {foreground = hl_colors.Statement, bold = true}, + [3] = {foreground = hl_colors.Special}, + [4] = {bold = true, foreground = hl_colors.Special}, + [5] = {foreground = hl_colors.Identifier}, + [6] = {bold = true}, + [7] = {underline = true, bold = true, foreground = hl_colors.Special}, + [8] = {foreground = hl_colors.Special, underline = true} + }) + curbuf = request('vim_get_current_buffer') + end) + + after_each(function() + screen:detach() + end) + + local function add_hl(...) + return request('buffer_add_highlight', curbuf, ...) + end + + local function clear_hl(...) + return request('buffer_clear_highlight', curbuf, ...) + end + + + it('works', function() + insert([[ + these are some lines + with colorful text]]) + feed('+') + + screen:expect([[ + these are some lines | + with colorful tex^t | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + add_hl(-1, "String", 0 , 10, 14) + add_hl(-1, "Statement", 1 , 5, -1) + + screen:expect([[ + these are {1:some} lines | + with {2:colorful tex^t} | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + feed("ggo<esc>") + screen:expect([[ + these are {1:some} lines | + ^ | + with {2:colorful text} | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + clear_hl(-1, 0 , -1) + screen:expect([[ + these are some lines | + ^ | + with colorful text | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) + + describe('support adding multiple sources', function() + local id1, id2 + before_each(function() + insert([[ + a longer example + in order to demonstrate + combining highlights + from different sources]]) + + execute("hi ImportantWord gui=bold cterm=bold") + id1 = add_hl(0, "ImportantWord", 0, 2, 8) + add_hl(id1, "ImportantWord", 1, 12, -1) + add_hl(id1, "ImportantWord", 2, 0, 9) + add_hl(id1, "ImportantWord", 3, 5, 14) + + id2 = add_hl(0, "Special", 0, 2, 8) + add_hl(id2, "Identifier", 1, 3, 8) + add_hl(id2, "Special", 1, 14, 20) + add_hl(id2, "Underlined", 2, 6, 12) + add_hl(id2, "Underlined", 3, 0, 9) + neq(id1, id2) + + screen:expect([[ + a {4:longer} example | + in {5:order} to {6:de}{4:monstr}{6:ate} | + {6:combin}{7:ing}{8: hi}ghlights | + {8:from }{7:diff}{6:erent} source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and clearing the first added', function() + clear_hl(id1, 0, -1) + screen:expect([[ + a {3:longer} example | + in {5:order} to de{3:monstr}ate | + combin{8:ing hi}ghlights | + {8:from diff}erent source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and clearing the second added', function() + clear_hl(id2, 0, -1) + screen:expect([[ + a {6:longer} example | + in order to {6:demonstrate} | + {6:combining} highlights | + from {6:different} source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and clearing line ranges', function() + clear_hl(-1, 0, 1) + clear_hl(id1, 1, 2) + clear_hl(id2, 2, -1) + screen:expect([[ + a longer example | + in {5:order} to de{3:monstr}ate | + {6:combining} highlights | + from {6:different} source^s | + ~ | + ~ | + ~ | + :hi ImportantWord gui=bold cterm=bold | + ]]) + end) + + it('and renumbering lines', function() + feed('3Gddggo<esc>') + screen:expect([[ + a {4:longer} example | + ^ | + in {5:order} to {6:de}{4:monstr}{6:ate} | + {8:from }{7:diff}{6:erent} sources | + ~ | + ~ | + ~ | + | + ]]) + + execute(':3move 4') + screen:expect([[ + a {4:longer} example | + | + {8:from }{7:diff}{6:erent} sources | + ^in {5:order} to {6:de}{4:monstr}{6:ate} | + ~ | + ~ | + ~ | + ::3move 4 | + ]]) + end) + end) + + it('prioritizes latest added highlight', function() + insert([[ + three overlapping colors]]) + add_hl(0, "Identifier", 0, 6, 17) + add_hl(0, "String", 0, 14, 23) + local id = add_hl(0, "Special", 0, 0, 9) + + screen:expect([[ + {3:three ove}{5:rlapp}{1:ing color}^s | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + + clear_hl(id, 0, 1) + screen:expect([[ + three {5:overlapp}{1:ing color}^s | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) + + it('works with multibyte text', function() + insert([[ + Ta bÃ¥ten över sjön!]]) + add_hl(-1, "Identifier", 0, 3, 9) + add_hl(-1, "String", 0, 16, 21) + + screen:expect([[ + Ta {5:bÃ¥ten} över {1:sjön}^! | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + end) +end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index f9b112e464..06139277b2 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') +local os = require('os') local clear, feed = helpers.clear, helpers.feed local execute, request, eq = helpers.execute, helpers.request, helpers.eq @@ -14,14 +15,72 @@ describe('color scheme compatibility', function() request('vim_set_option', 't_Co', '88') eq('88', request('vim_eval', '&t_Co')) end) +end) + +describe('manual syntax highlight', function() + -- When using manual syntax highlighting, it should be preserved even when + -- switching buffers... bug did only occur without :set hidden + -- Ref: vim patch 7.4.1236 + local screen - it('emulates gui_running when a rgb UI is attached', function() - eq(0, request('vim_eval', 'has("gui_running")')) - local screen = Screen.new() + before_each(function() + clear() + screen = Screen.new(20,5) screen:attach() - eq(1, request('vim_eval', 'has("gui_running")')) + --ignore highligting of ~-lines + screen:set_default_attr_ignore( {{bold=true, foreground=Screen.colors.Blue}} ) + --syntax highlight for vimcscripts "echo" + screen:set_default_attr_ids( {[1] = {bold=true, foreground=Screen.colors.Brown}} ) + end) + + after_each(function() screen:detach() - eq(0, request('vim_eval', 'has("gui_running")')) + os.remove('Xtest-functional-ui-highlight.tmp.vim') + end) + + -- test with "set hidden" even if the bug did not occur this way + it("works with buffer switch and 'hidden'", function() + execute('e tmp1.vim') + execute('e Xtest-functional-ui-highlight.tmp.vim') + execute('filetype on') + execute('syntax manual') + execute('set ft=vim') + execute('set syntax=ON') + feed('iecho 1<esc>0') + + execute('set hidden') + execute('w') + execute('bn') + execute('bp') + screen:expect([[ + {1:^echo} 1 | + ~ | + ~ | + ~ | + <f 1 --100%-- col 1 | + ]]) + end) + + it("works with buffer switch and 'nohidden'", function() + execute('e tmp1.vim') + execute('e Xtest-functional-ui-highlight.tmp.vim') + execute('filetype on') + execute('syntax manual') + execute('set ft=vim') + execute('set syntax=ON') + feed('iecho 1<esc>0') + + execute('set nohidden') + execute('w') + execute('bn') + execute('bp') + screen:expect([[ + {1:^echo} 1 | + ~ | + ~ | + ~ | + <ht.tmp.vim" 1L, 7C | + ]]) end) end) @@ -47,6 +106,7 @@ describe('Default highlight groups', function() after_each(function() screen:detach() end) + it('window status bar', function() screen:set_default_attr_ids({ [1] = {reverse = true, bold = true}, -- StatusLine @@ -202,4 +262,44 @@ describe('Default highlight groups', function() ]], {[1] = {bold = true, foreground = hlgroup_colors.Question}}) feed('<cr>') -- skip the "Press ENTER..." state or tests will hang end) + it('can be cleared and linked to other highlight groups', function() + execute('highlight clear ModeMsg') + feed('i') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + -- INSERT -- | + ]], {}) + feed('<esc>') + execute('highlight CustomHLGroup guifg=red guibg=green') + execute('highlight link ModeMsg CustomHLGroup') + feed('i') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + {1:-- INSERT --} | + ]], {[1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}}) + end) end) diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 4818830940..6f5cadaf81 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -25,6 +25,9 @@ describe('mappings', function() add_mapping('<s-up>', '<s-up>') add_mapping('<c-s-up>', '<c-s-up>') add_mapping('<c-s-a-up>', '<c-s-a-up>') + add_mapping('<c-s-a-d-up>', '<c-s-a-d-up>') + add_mapping('<c-d-a>', '<c-d-a>') + add_mapping('<d-1>', '<d-1>') end) it('ok', function() @@ -37,6 +40,12 @@ describe('mappings', function() check_mapping('<s-a-c-up>', '<c-s-a-up>') check_mapping('<a-c-s-up>', '<c-s-a-up>') check_mapping('<a-s-c-up>', '<c-s-a-up>') + check_mapping('<c-s-a-d-up>', '<c-s-a-d-up>') + check_mapping('<s-a-d-c-up>', '<c-s-a-d-up>') + check_mapping('<d-s-a-c-up>', '<c-s-a-d-up>') + check_mapping('<c-d-a>', '<c-d-a>') + check_mapping('<d-c-a>', '<c-d-a>') + check_mapping('<d-1>', '<d-1>') end) end) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 30f37a7463..d0d791308b 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1,7 +1,8 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') -local clear, feed, nvim = helpers.clear, helpers.feed, helpers.nvim +local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths local insert, execute = helpers.insert, helpers.execute +local eq, funcs = helpers.eq, helpers.funcs describe('Mouse input', function() local screen @@ -13,10 +14,11 @@ describe('Mouse input', function() before_each(function() clear() - nvim('set_option', 'mouse', 'a') + meths.set_option('mouse', 'a') + meths.set_option('listchars', 'eol:$') -- set mouset to very high value to ensure that even in valgrind/travis, -- nvim will still pick multiple clicks - nvim('set_option', 'mouset', 5000) + meths.set_option('mouset', 5000) screen = Screen.new(25, 5) screen:attach() screen:set_default_attr_ids({ @@ -57,31 +59,149 @@ describe('Mouse input', function() ]]) end) - it('left click in tabline switches to tab', function() + describe('tabline', function() local tab_attrs = { tab = { background=Screen.colors.LightGrey, underline=true }, sel = { bold=true }, fill = { reverse=true } } - execute('%delete') - insert('this is foo') - execute('silent file foo | tabnew | file bar') - insert('this is bar') - screen:expect([[ - {tab: + foo }{sel: + bar }{fill: }{tab:X}| - this is ba^r | - ~ | - ~ | - | - ]], tab_attrs) - feed('<LeftMouse><4,0>') - screen:expect([[ - {sel: + foo }{tab: + bar }{fill: }{tab:X}| - this is fo^o | - ~ | - ~ | - | - ]], tab_attrs) + + it('left click in default tabline (position 4) switches to tab', function() + execute('%delete') + insert('this is foo') + execute('silent file foo | tabnew | file bar') + insert('this is bar') + screen:expect([[ + {tab: + foo }{sel: + bar }{fill: }{tab:X}| + this is ba^r | + ~ | + ~ | + | + ]], tab_attrs) + feed('<LeftMouse><4,0>') + screen:expect([[ + {sel: + foo }{tab: + bar }{fill: }{tab:X}| + this is fo^o | + ~ | + ~ | + | + ]], tab_attrs) + end) + + it('left click in default tabline (position 24) closes tab', function() + meths.set_option('hidden', true) + execute('%delete') + insert('this is foo') + execute('silent file foo | tabnew | file bar') + insert('this is bar') + screen:expect([[ + {tab: + foo }{sel: + bar }{fill: }{tab:X}| + this is ba^r | + ~ | + ~ | + | + ]], tab_attrs) + feed('<LeftMouse><24,0>') + screen:expect([[ + this is fo^o | + ~ | + ~ | + ~ | + | + ]], tab_attrs) + end) + + it('double click in default tabline (position 4) opens new tab', function() + meths.set_option('hidden', true) + execute('%delete') + insert('this is foo') + execute('silent file foo | tabnew | file bar') + insert('this is bar') + screen:expect([[ + {tab: + foo }{sel: + bar }{fill: }{tab:X}| + this is ba^r | + ~ | + ~ | + | + ]], tab_attrs) + feed('<2-LeftMouse><4,0>') + screen:expect([[ + {sel: Name] }{tab: + foo + bar }{fill: }{tab:X}| + ^ | + ~ | + ~ | + | + ]], tab_attrs) + end) + + describe('%@ label', function() + before_each(function() + execute([[ + function Test(...) + let g:reply = a:000 + return copy(a:000) " Check for memory leaks: return should be freed + endfunction + ]]) + execute([[ + function Test2(...) + return call('Test', a:000 + [2]) + endfunction + ]]) + meths.set_option('tabline', '%@Test@test%X-%5@Test2@test2') + meths.set_option('showtabline', 2) + screen:expect([[ + {fill:test-test2 }| + mouse | + support and selectio^n | + ~ | + | + ]], tab_attrs) + meths.set_var('reply', {}) + end) + + local check_reply = function(expected) + eq(expected, meths.get_var('reply')) + meths.set_var('reply', {}) + end + + local test_click = function(name, click_str, click_num, mouse_button, + modifiers) + it(name .. ' works', function() + eq(1, funcs.has('tablineat')) + feed(click_str .. '<3,0>') + check_reply({0, click_num, mouse_button, modifiers}) + feed(click_str .. '<4,0>') + check_reply({}) + feed(click_str .. '<6,0>') + check_reply({5, click_num, mouse_button, modifiers, 2}) + feed(click_str .. '<13,0>') + check_reply({5, click_num, mouse_button, modifiers, 2}) + end) + end + + test_click('single left click', '<LeftMouse>', 1, 'l', ' ') + test_click('shifted single left click', '<S-LeftMouse>', 1, 'l', 's ') + test_click('shifted single left click with alt modifier', + '<S-A-LeftMouse>', 1, 'l', 's a ') + test_click('shifted single left click with alt and ctrl modifiers', + '<S-C-A-LeftMouse>', 1, 'l', 'sca ') + -- <C-RightMouse> does not work + test_click('shifted single right click with alt modifier', + '<S-A-RightMouse>', 1, 'r', 's a ') + -- Modifiers do not work with MiddleMouse + test_click('shifted single middle click with alt and ctrl modifiers', + '<MiddleMouse>', 1, 'm', ' ') + -- Modifiers do not work with N-*Mouse + test_click('double left click', '<2-LeftMouse>', 2, 'l', ' ') + test_click('triple left click', '<3-LeftMouse>', 3, 'l', ' ') + test_click('quadruple left click', '<4-LeftMouse>', 4, 'l', ' ') + test_click('double right click', '<2-RightMouse>', 2, 'r', ' ') + test_click('triple right click', '<3-RightMouse>', 3, 'r', ' ') + test_click('quadruple right click', '<4-RightMouse>', 4, 'r', ' ') + test_click('double middle click', '<2-MiddleMouse>', 2, 'm', ' ') + test_click('triple middle click', '<3-MiddleMouse>', 3, 'm', ' ') + test_click('quadruple middle click', '<4-MiddleMouse>', 4, 'm', ' ') + end) end) it('left drag changes visual selection', function() @@ -210,7 +330,7 @@ describe('Mouse input', function() end) it('ctrl + left click will search for a tag', function() - nvim('set_option', 'tags', './non-existent-tags-file') + meths.set_option('tags', './non-existent-tags-file') feed('<C-LeftMouse><0,0>') screen:expect([[ E433: No tags file | @@ -306,4 +426,35 @@ describe('Mouse input', function() | ]]) end) + + it('horizontal scrolling', function() + feed("<esc>:set nowrap<cr>") + + feed("a <esc>20Ab<esc>") + screen:expect([[ + | + | + bbbbbbbbbbbbbbb^b | + ~ | + | + ]]) + + feed("<ScrollWheelLeft><0,0>") + screen:expect([[ + | + | + n bbbbbbbbbbbbbbbbbbb^b | + ~ | + | + ]]) + + feed("^<ScrollWheelRight><0,0>") + screen:expect([[ + g | + | + ^t and selection bbbbbbbbb| + ~ | + | + ]]) + end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index e1c2d14759..a11fab18a2 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -119,7 +119,7 @@ if os.getenv('VALGRIND') then default_screen_timeout = default_screen_timeout * 3 end -if os.getenv('CI_TARGET') then +if os.getenv('CI') then default_screen_timeout = default_screen_timeout * 3 end @@ -138,7 +138,7 @@ do -- this is just a helper to get any canonical name of a color colornames[rgb] = name end - session:exit(0) + session:close() Screen.colors = colors Screen.colornames = colornames end @@ -219,12 +219,23 @@ function Screen:expect(expected, attr_ids, attr_ignore) local ids = attr_ids or self._default_attr_ids local ignore = attr_ignore or self._default_attr_ignore self:wait(function() + local actual_rows = {} for i = 1, self._height do - local expected_row = expected_rows[i] - local actual_row = self:_row_repr(self._rows[i], ids, ignore) - if expected_row ~= actual_row then - return 'Row '..tostring(i)..' didn\'t match.\nExpected: "'.. - expected_row..'"\nActual: "'..actual_row..'"' + actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore) + end + for i = 1, self._height do + if expected_rows[i] ~= actual_rows[i] then + local msg_expected_rows = {} + for j = 1, #expected_rows do + msg_expected_rows[j] = expected_rows[j] + end + msg_expected_rows[i] = '*' .. msg_expected_rows[i] + actual_rows[i] = '*' .. actual_rows[i] + return ( + 'Row ' .. tostring(i) .. ' didn\'t match.\n' + .. 'Expected:\n|' .. table.concat(msg_expected_rows, '|\n|') .. '|\n' + .. 'Actual:\n|' .. table.concat(actual_rows, '|\n|') .. '|' + ) end end end) diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index de2f3e469d..c57d4abcbf 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute +local funcs = helpers.funcs describe("'wildmode'", function() local screen @@ -30,3 +31,34 @@ describe("'wildmode'", function() end) end) end) + +describe('command line completion', function() + local screen + + before_each(function() + clear() + screen = Screen.new(40, 5) + screen:attach() + screen:set_default_attr_ignore({{bold=true, foreground=Screen.colors.Blue}}) + end) + + after_each(function() + os.remove('Xtest-functional-viml-compl-dir') + end) + + it('lists directories with empty PATH', function() + local tmp = funcs.tempname() + execute('e '.. tmp) + execute('cd %:h') + execute("call mkdir('Xtest-functional-viml-compl-dir')") + execute('let $PATH=""') + feed(':!<tab><bs>') + screen:expect([[ + | + ~ | + ~ | + ~ | + :!Xtest-functional-viml-compl-dir^ | + ]]) + end) +end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 12f542de7f..2b3844bf6d 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -1,12 +1,26 @@ - local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq -local execute, source = helpers.execute, helpers.source +local execute, source, expect = helpers.execute, helpers.source, helpers.expect describe('completion', function() + local screen + before_each(function() clear() + screen = Screen.new(60, 8) + screen:attach() + screen:set_default_attr_ignore({{bold=true, foreground=Screen.colors.Blue}}) + screen:set_default_attr_ids({ + [1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.Grey}, + [3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [5] = {foreground = Screen.colors.Red}, + [6] = {background = Screen.colors.Black}, + [7] = {foreground = Screen.colors.White, background = Screen.colors.Red}, + }) end) describe('v:completed_item', function() @@ -14,18 +28,40 @@ describe('completion', function() eq({}, eval('v:completed_item')) end) it('is empty dict if the candidate is not inserted', function() - feed('ifoo<ESC>o<C-x><C-n><C-e><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + foo^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('<C-e>') + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<ESC>') eq({}, eval('v:completed_item')) end) it('returns expected dict in normal completion', function() - feed('ifoo<ESC>o<C-x><C-n><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') eq('foo', eval('getline(2)')) eq({word = 'foo', abbr = '', menu = '', info = '', kind = ''}, eval('v:completed_item')) end) it('is readonly', function() + screen:try_resize(80, 8) feed('ifoo<ESC>o<C-x><C-n><ESC>') - execute('let v:completed_item.word = "bar"') neq(nil, string.find(eval('v:errmsg'), '^E46: ')) execute('let v:errmsg = ""') @@ -50,17 +86,29 @@ describe('completion', function() source([[ function! TestOmni(findstart, base) abort return a:findstart ? 0 : [{'word': 'foo', 'abbr': 'bar', - \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}] + \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}, + \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu', 'info': 'info', 'kind': 'kind'}] endfunction setlocal omnifunc=TestOmni ]]) - feed('i<C-x><C-o><ESC>') + feed('i<C-x><C-o>') eq('foo', eval('getline(1)')) + screen:expect([[ + foo^ | + {2:bar foobaz baz } | + {1:abbr kind menu } | + ~ | + ~ | + ~ | + ~ | + {3:-- Omni completion (^O^N^P) }{4:match 1 of 2} | + ]]) eq({word = 'foo', abbr = 'bar', menu = 'baz', info = 'foobar', kind = 'foobaz'}, eval('v:completed_item')) end) end) + describe('completeopt', function() before_each(function() source([[ @@ -73,31 +121,644 @@ describe('completion', function() it('inserts the first candidate if default', function() execute('set completeopt+=menuone') - feed('ifoo<ESC>o<C-x><C-n>bar<ESC>') + feed('ifoo<ESC>o') + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<C-x>') + -- the ^X prompt, only test this once + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)} | + ]]) + feed('<C-n>') + screen:expect([[ + foo | + foo^ | + {2:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('bar<ESC>') eq('foobar', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR><ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + foobar | + foo^ | + {2:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) eq('foo', eval('getline(3)')) end) it('selects the first candidate if noinsert', function() execute('set completeopt+=menuone,noinsert') - feed('ifoo<ESC>o<C-x><C-n><C-y><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {2:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('<C-y>') + screen:expect([[ + foo | + foo^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<ESC>') eq('foo', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR><C-y><ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + foo | + ^ | + {2:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<C-y><ESC>') eq('foo', eval('getline(3)')) end) it('does not insert the first candidate if noselect', function() execute('set completeopt+=menuone,noselect') - feed('ifoo<ESC>o<C-x><C-n>bar<ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('b') + screen:expect([[ + foo | + b^ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('ar<ESC>') eq('bar', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR>bar<ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + bar | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('bar<ESC>') eq('bar', eval('getline(3)')) end) it('does not select/insert the first candidate if noselect and noinsert', function() execute('set completeopt+=menuone,noselect,noinsert') - feed('ifoo<ESC>o<C-x><C-n><ESC>') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + ~ | + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('<ESC>') + screen:expect([[ + foo | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) eq('', eval('getline(2)')) - feed('o<C-r>=TestComplete()<CR><ESC>') + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + | + ^ | + {1:foo } | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + feed('<ESC>') + screen:expect([[ + foo | + | + ^ | + ~ | + ~ | + ~ | + ~ | + | + ]]) eq('', eval('getline(3)')) end) + it('does not change modified state if noinsert', function() + execute('set completeopt+=menuone,noinsert') + execute('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) + it('does not change modified state if noselect', function() + execute('set completeopt+=menuone,noselect') + execute('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) + end) + + describe("refresh:always", function() + before_each(function() + source([[ + function! TestCompletion(findstart, base) abort + if a:findstart + let line = getline('.') + let start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\a' + let start -= 1 + endwhile + return start + else + let ret = [] + for m in split("January February March April May June July August September October November December") + if m =~ a:base " match by regex + call add(ret, m) + endif + endfor + return {'words':ret, 'refresh':'always'} + endif + endfunction + + set completeopt=menuone,noselect + set completefunc=TestCompletion + ]]) + end ) + + it('completes on each input char', function () + feed('i<C-x><C-u>') + screen:expect([[ + ^ | + {1:January }{6: } | + {1:February }{6: } | + {1:March }{6: } | + {1:April }{2: } | + {1:May }{2: } | + {1:June }{2: } | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('u') + screen:expect([[ + u^ | + {1:January } | + {1:February } | + {1:June } | + {1:July } | + {1:August } | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('g') + screen:expect([[ + ug^ | + {1:August } | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<Down>') + screen:expect([[ + ug^ | + {2:August } | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) The only match} | + ]]) + feed('<C-y>') + screen:expect([[ + August^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + expect('August') + end) + it("repeats correctly after backspace #2674", function () + feed('o<C-x><C-u>Ja') + screen:expect([[ + | + Ja^ | + {1:January } | + ~ | + ~ | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<BS>') + screen:expect([[ + | + J^ | + {1:January } | + {1:June } | + {1:July } | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<C-n>') + screen:expect([[ + | + January^ | + {2:January } | + {1:June } | + {1:July } | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{4:match 1 of 3} | + ]]) + feed('<C-n>') + screen:expect([[ + | + June^ | + {1:January } | + {2:June } | + {1:July } | + ~ | + ~ | + {3:-- User defined completion (^U^N^P) }{4:match 2 of 3} | + ]]) + feed('<Esc>') + screen:expect([[ + | + Jun^e | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + feed('.') + screen:expect([[ + | + June | + Jun^e | + ~ | + ~ | + ~ | + ~ | + | + ]]) + expect([[ + + June + June]]) + end) + end) + + describe('with a lot of items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, map(range(0,100), "string(v:val)")) + return '' + endfunction + ]]) + execute("set completeopt=menuone,noselect") + end) + + it("works", function() + feed('i<C-r>=TestComplete()<CR>') + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('7') + screen:expect([[ + 7^ | + {1:7 }{6: } | + {1:70 }{6: } | + {1:71 }{6: } | + {1:72 }{2: } | + {1:73 }{2: } | + {1:74 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<c-n>') + screen:expect([[ + 7^ | + {2:7 }{6: } | + {1:70 }{6: } | + {1:71 }{6: } | + {1:72 }{2: } | + {1:73 }{2: } | + {1:74 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<c-n>') + screen:expect([[ + 70^ | + {1:7 }{6: } | + {2:70 }{6: } | + {1:71 }{6: } | + {1:72 }{2: } | + {1:73 }{2: } | + {1:74 }{2: } | + {3:-- INSERT --} | + ]]) + end) + + it('can be navigated with <PageDown>, <PageUp>', function() + feed('i<C-r>=TestComplete()<CR>') + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageDown>') + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {2:3 } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageDown>') + screen:expect([[ + ^ | + {1:5 }{6: } | + {1:6 }{2: } | + {2:7 } | + {1:8 }{2: } | + {1:9 }{2: } | + {1:10 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<Down>') + screen:expect([[ + ^ | + {1:5 }{6: } | + {1:6 }{2: } | + {1:7 }{2: } | + {2:8 } | + {1:9 }{2: } | + {1:10 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') + screen:expect([[ + ^ | + {1:2 }{6: } | + {1:3 }{2: } | + {2:4 } | + {1:5 }{2: } | + {1:6 }{2: } | + {1:7 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- stop on first item + screen:expect([[ + ^ | + {2:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- when on first item, unselect + screen:expect([[ + ^ | + {1:0 }{6: } | + {1:1 }{2: } | + {1:2 }{2: } | + {1:3 }{2: } | + {1:4 }{2: } | + {1:5 }{2: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- when unselected, select last item + screen:expect([[ + ^ | + {1:95 }{2: } | + {1:96 }{2: } | + {1:97 }{2: } | + {1:98 }{2: } | + {1:99 }{2: } | + {2:100 }{6: } | + {3:-- INSERT --} | + ]]) + feed('<PageUp>') + screen:expect([[ + ^ | + {1:94 }{2: } | + {1:95 }{2: } | + {2:96 } | + {1:97 }{2: } | + {1:98 }{2: } | + {1:99 }{6: } | + {3:-- INSERT --} | + ]]) + feed('<cr>') + screen:expect([[ + 96^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- INSERT --} | + ]]) + end) + end) + + + it('disables folding during completion', function () + execute("set foldmethod=indent") + feed('i<Tab>foo<CR><Tab>bar<Esc>gg') + screen:expect([[ + ^foo | + bar | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]]) + feed('A<C-x><C-l>') + screen:expect([[ + foo^ | + bar | + ~ | + ~ | + ~ | + ~ | + ~ | + {3:-- Whole line completion (^L^N^P) }{7:Pattern not found} | + ]]) + eq(-1, eval('foldclosed(1)')) + end) + + it('popupmenu is not interrupted by events', function () + execute("set complete=.") + + feed('ifoobar fooegg<cr>f<c-p>') + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar } | + {2:fooegg } | + ~ | + ~ | + ~ | + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + + eval('1 + 1') + -- popupmenu still visible + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar } | + {2:fooegg } | + ~ | + ~ | + ~ | + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + + feed('<c-p>') + -- Didn't restart completion: old matches still used + screen:expect([[ + foobar fooegg | + foobar^ | + {2:foobar } | + {1:fooegg } | + ~ | + ~ | + ~ | + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + end) + + describe('from the commandline window', function() + + it('is cleared after CTRL-C', function () + feed('q:') + feed('ifoo faa fee f') + screen:expect([[ + | + {8:[No Name] }| + :foo faa fee f^ | + :~ | + :~ | + :~ | + {9:[Command Line] }| + {3:-- INSERT --} | + ]], {[3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [8] = {reverse = true}, + [9] = {bold = true, reverse = true}}) + feed('<c-x><c-n>') + screen:expect([[ + | + {8:[No Name] }| + :foo faa fee foo^ | + :~ {2: foo } | + :~ {1: faa } | + :~ {1: fee } | + {9:[Command Line] }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 3} | + ]],{[1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.Grey}, + [3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [8] = {reverse = true}, + [9] = {bold = true, reverse = true}}) + feed('<c-c>') + screen:expect([[ + | + {8:[No Name] }| + :foo faa fee foo | + :~ | + :~ | + :~ | + {9:[Command Line] }| + :foo faa fee foo^ | + ]], {[8] = {reverse = true}, [9] = {bold = true, reverse = true}}) + end) end) end) diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua new file mode 100644 index 0000000000..30cb86f8d1 --- /dev/null +++ b/test/functional/viml/errorlist_spec.lua @@ -0,0 +1,73 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local exc_exec = helpers.exc_exec +local get_cur_win_var = helpers.curwinmeths.get_var + +describe('setqflist()', function() + local setqflist = helpers.funcs.setqflist + + before_each(clear) + + it('requires a list for {list}', function() + eq('Vim(call):E714: List required', exc_exec('call setqflist("foo")')) + eq('Vim(call):E714: List required', exc_exec('call setqflist(5)')) + eq('Vim(call):E714: List required', exc_exec('call setqflist({})')) + end) + + it('requires a string for {action}', function() + eq('Vim(call):E114: String required', exc_exec('call setqflist([], 5)')) + eq('Vim(call):E114: String required', exc_exec('call setqflist([], [])')) + eq('Vim(call):E114: String required', exc_exec('call setqflist([], {})')) + end) + + it('sets w:quickfix_title', function() + setqflist({''}, 'r', 'foo') + command('copen') + eq(':foo', get_cur_win_var('quickfix_title')) + end) + + it('requires string or number for {title}', function() + command('copen') + setqflist({}, 'r', '5') + eq(':5', get_cur_win_var('quickfix_title')) + setqflist({}, 'r', 6) + eq(':6', get_cur_win_var('quickfix_title')) + local exc = exc_exec('call setqflist([], "r", function("function"))') + eq('Vim(call):E729: using Funcref as a String', exc) + exc = exc_exec('call setqflist([], "r", [])') + eq('Vim(call):E730: using List as a String', exc) + exc = exc_exec('call setqflist([], "r", {})') + eq('Vim(call):E731: using Dictionary as a String', exc) + end) +end) + +describe('setloclist()', function() + local setloclist = helpers.funcs.setloclist + + before_each(clear) + + it('requires a list for {list}', function() + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, "foo")')) + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, 5)')) + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, {})')) + end) + + it('requires a string for {action}', function() + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], 5)')) + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], [])')) + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], {})')) + end) + + it('sets w:quickfix_title for the correct window', function() + command('rightbelow vsplit') + setloclist(1, {}, 'r', 'foo') + setloclist(2, {}, 'r', 'bar') + command('lopen') + eq(':bar', get_cur_win_var('quickfix_title')) + command('lclose | wincmd w | lopen') + eq(':foo', get_cur_win_var('quickfix_title')) + end) +end) diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua new file mode 100644 index 0000000000..665f5d4467 --- /dev/null +++ b/test/functional/viml/function_spec.lua @@ -0,0 +1,29 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local eq = helpers.eq +local exc_exec = helpers.exc_exec + +describe('Up to MAX_FUNC_ARGS arguments are handled by', function() + local max_func_args = 20 -- from eval.h + local range = helpers.funcs.range + + before_each(clear) + + it('printf()', function() + local printf = helpers.funcs.printf + local rep = helpers.funcs['repeat'] + local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' + eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) + local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function printf', ret) + end) + + it('rpcnotify()', function() + local rpcnotify = helpers.funcs.rpcnotify + local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) + eq(1, ret) + ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) + end) +end) diff --git a/test/includes/pre/sys/fcntl.h b/test/includes/pre/fcntl.h index 767c97a631..9c50ed5b1d 100644 --- a/test/includes/pre/sys/fcntl.h +++ b/test/includes/pre/fcntl.h @@ -1,4 +1,4 @@ -#include <sys/fcntl.h> +#include <fcntl.h> static const mode_t kO_RDONLY = O_RDONLY; static const mode_t kO_WRONLY = O_WRONLY; diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index a2e7bd91af..b7f82064d7 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -41,13 +41,13 @@ describe('buffer functions', function() describe('buf_valid', function() it('should view NULL as an invalid buffer', function() - eq(0, buffer.buf_valid(NULL)) + eq(false, buffer.buf_valid(NULL)) end) it('should view an open buffer as valid', function() local buf = buflist_new(path1, buffer.BLN_LISTED) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and hidden buffer as valid', function() @@ -55,7 +55,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, 0, 0) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and unloaded buffer as valid', function() @@ -63,7 +63,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and wiped buffer as invalid', function() @@ -71,7 +71,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0) - eq(0, buffer.buf_valid(buf)) + eq(false, buffer.buf_valid(buf)) end) end) diff --git a/test/unit/eval/decode_spec.lua b/test/unit/eval/decode_spec.lua new file mode 100644 index 0000000000..d94d809c14 --- /dev/null +++ b/test/unit/eval/decode_spec.lua @@ -0,0 +1,142 @@ +local helpers = require('test.unit.helpers') + +local cimport = helpers.cimport +local to_cstr = helpers.to_cstr +local eq = helpers.eq +local neq = helpers.neq +local ffi = helpers.ffi + +local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval_defs.h', + './src/nvim/globals.h', './src/nvim/memory.h', + './src/nvim/message.h') + +describe('json_decode_string()', function() + local saved_p_enc = nil + + before_each(function() + saved_p_enc = decode.p_enc + end) + + after_each(function() + decode.emsg_silent = 0 + decode.p_enc = saved_p_enc + while decode.delete_first_msg() == 1 do + -- Delete all messages + end + end) + + local char = function(c) + return ffi.gc(decode.xmemdup(c, 1), decode.xfree) + end + + it('does not overflow when running with `n…`, `t…`, `f…`', function() + local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) + decode.emsg_silent = 1 + -- This will not crash, but if `len` argument will be ignored it will parse + -- `null` as `null` and if not it will parse `null` as `n`. + eq(0, decode.json_decode_string('null', 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('true', 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('false', 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('null', 2, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('true', 2, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('false', 2, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('null', 3, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('true', 3, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('false', 3, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('false', 4, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + end) + + it('does not overflow and crash when running with `n`, `t`, `f`', function() + local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) + decode.emsg_silent = 1 + eq(0, decode.json_decode_string(char('n'), 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string(char('t'), 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string(char('f'), 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + end) + + it('does not overflow when running with `"…`', function() + local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) + decode.emsg_silent = 1 + eq(0, decode.json_decode_string('"t"', 2, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + eq(0, decode.json_decode_string('""', 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + end) + + local check_failure = function(s, len, msg) + local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) + eq(0, decode.json_decode_string(s, len, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + neq(nil, decode.last_msg_hist) + eq(msg, ffi.string(decode.last_msg_hist.msg)) + end + + it('does not overflow in error messages', function() + check_failure(']test', 1, 'E474: No container to close: ]') + check_failure('[}test', 2, 'E474: Closing list with curly bracket: }') + check_failure('{]test', 2, + 'E474: Closing dictionary with square bracket: ]') + check_failure('[1,]test', 4, 'E474: Trailing comma: ]') + check_failure('{"1":}test', 6, 'E474: Expected value after colon: }') + check_failure('{"1"}test', 5, 'E474: Expected value: }') + check_failure(',test', 1, 'E474: Comma not inside container: ,') + check_failure('[1,,1]test', 6, 'E474: Duplicate comma: ,1]') + check_failure('{"1":,}test', 7, 'E474: Comma after colon: ,}') + check_failure('{"1",}test', 6, 'E474: Using comma in place of colon: ,}') + check_failure('{,}test', 3, 'E474: Leading comma: ,}') + check_failure('[,]test', 3, 'E474: Leading comma: ,]') + check_failure(':test', 1, 'E474: Colon not inside container: :') + check_failure('[:]test', 3, 'E474: Using colon not in dictionary: :]') + check_failure('{:}test', 3, 'E474: Unexpected colon: :}') + check_failure('{"1"::1}test', 8, 'E474: Duplicate colon: :1}') + check_failure('ntest', 1, 'E474: Expected null: n') + check_failure('ttest', 1, 'E474: Expected true: t') + check_failure('ftest', 1, 'E474: Expected false: f') + check_failure('"\\test', 2, 'E474: Unfinished escape sequence: "\\') + check_failure('"\\u"test', 4, + 'E474: Unfinished unicode escape sequence: "\\u"') + check_failure('"\\uXXXX"est', 8, + 'E474: Expected four hex digits after \\u: \\uXXXX"') + check_failure('"\\?"test', 4, 'E474: Unknown escape sequence: \\?"') + check_failure( + '"\t"test', 3, + 'E474: ASCII control characters cannot be present inside string: \t"') + check_failure('"\194"test', 3, 'E474: Only UTF-8 strings allowed: \194"') + check_failure('"\252\144\128\128\128\128"test', 8, 'E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"') + check_failure('"test', 1, 'E474: Expected string end: "') + decode.p_enc = to_cstr('latin1') + check_failure('"\\uABCD"test', 8, + 'E474: Failed to convert string "ê¯" from UTF-8') + decode.p_enc = saved_p_enc + check_failure('-test', 1, 'E474: Missing number after minus sign: -') + check_failure('-1.test', 3, 'E474: Missing number after decimal dot: -1.') + check_failure('-1.0etest', 5, 'E474: Missing exponent: -1.0e') + check_failure('?test', 1, 'E474: Unidentified byte: ?') + check_failure('1?test', 2, 'E474: Trailing characters: ?') + check_failure('[1test', 2, 'E474: Unexpected end of input: [1') + end) + + it('does not overflow with `-`', function() + check_failure('-0', 1, 'E474: Missing number after minus sign: -') + end) + + it('does not overflow and crash when running with `"`', function() + local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) + decode.emsg_silent = 1 + eq(0, decode.json_decode_string(char('"'), 1, rettv)) + eq(decode.VAR_UNKNOWN, rettv.v_type) + end) +end) diff --git a/test/unit/eval/encode_spec.lua b/test/unit/eval/encode_spec.lua new file mode 100644 index 0000000000..f151a191fb --- /dev/null +++ b/test/unit/eval/encode_spec.lua @@ -0,0 +1,100 @@ +local helpers = require('test.unit.helpers') +local eval_helpers = require('test.unit.eval.helpers') + +local cimport = helpers.cimport +local to_cstr = helpers.to_cstr +local eq = helpers.eq + +local list = eval_helpers.list +local lst2tbl = eval_helpers.lst2tbl +local type_key = eval_helpers.type_key +local list_type = eval_helpers.list_type +local null_string = eval_helpers.null_string + +local encode = cimport('./src/nvim/eval/encode.h') + +describe('encode_list_write()', function() + local encode_list_write = function(l, s) + return encode.encode_list_write(l, to_cstr(s), #s) + end + + it('writes empty string', function() + local l = list() + eq(0, encode_list_write(l, '')) + eq({[type_key]=list_type}, lst2tbl(l)) + end) + + it('writes ASCII string literal with printable characters', function() + local l = list() + eq(0, encode_list_write(l, 'abc')) + eq({[type_key]=list_type, 'abc'}, lst2tbl(l)) + end) + + it('writes string starting with NL', function() + local l = list() + eq(0, encode_list_write(l, '\nabc')) + eq({[type_key]=list_type, null_string, 'abc'}, lst2tbl(l)) + end) + + it('writes string starting with NL twice', function() + local l = list() + eq(0, encode_list_write(l, '\nabc')) + eq({[type_key]=list_type, null_string, 'abc'}, lst2tbl(l)) + eq(0, encode_list_write(l, '\nabc')) + eq({[type_key]=list_type, null_string, 'abc', 'abc'}, lst2tbl(l)) + end) + + it('writes string ending with NL', function() + local l = list() + eq(0, encode_list_write(l, 'abc\n')) + eq({[type_key]=list_type, 'abc', null_string}, lst2tbl(l)) + end) + + it('writes string ending with NL twice', function() + local l = list() + eq(0, encode_list_write(l, 'abc\n')) + eq({[type_key]=list_type, 'abc', null_string}, lst2tbl(l)) + eq(0, encode_list_write(l, 'abc\n')) + eq({[type_key]=list_type, 'abc', 'abc', null_string}, lst2tbl(l)) + end) + + it('writes string starting, ending and containing NL twice', function() + local l = list() + eq(0, encode_list_write(l, '\na\nb\n')) + eq({[type_key]=list_type, null_string, 'a', 'b', null_string}, lst2tbl(l)) + eq(0, encode_list_write(l, '\na\nb\n')) + eq({[type_key]=list_type, null_string, 'a', 'b', null_string, 'a', 'b', null_string}, lst2tbl(l)) + end) + + it('writes string starting, ending and containing NUL with NL between twice', function() + local l = list() + eq(0, encode_list_write(l, '\0\n\0\n\0')) + eq({[type_key]=list_type, '\n', '\n', '\n'}, lst2tbl(l)) + eq(0, encode_list_write(l, '\0\n\0\n\0')) + eq({[type_key]=list_type, '\n', '\n', '\n\n', '\n', '\n'}, lst2tbl(l)) + end) + + it('writes string starting, ending and containing NL with NUL between twice', function() + local l = list() + eq(0, encode_list_write(l, '\n\0\n\0\n')) + eq({[type_key]=list_type, null_string, '\n', '\n', null_string}, lst2tbl(l)) + eq(0, encode_list_write(l, '\n\0\n\0\n')) + eq({[type_key]=list_type, null_string, '\n', '\n', null_string, '\n', '\n', null_string}, lst2tbl(l)) + end) + + it('writes string containing a single NL twice', function() + local l = list() + eq(0, encode_list_write(l, '\n')) + eq({[type_key]=list_type, null_string, null_string}, lst2tbl(l)) + eq(0, encode_list_write(l, '\n')) + eq({[type_key]=list_type, null_string, null_string, null_string}, lst2tbl(l)) + end) + + it('writes string containing a few NLs twice', function() + local l = list() + eq(0, encode_list_write(l, '\n\n\n')) + eq({[type_key]=list_type, null_string, null_string, null_string, null_string}, lst2tbl(l)) + eq(0, encode_list_write(l, '\n\n\n')) + eq({[type_key]=list_type, null_string, null_string, null_string, null_string, null_string, null_string, null_string}, lst2tbl(l)) + end) +end) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua new file mode 100644 index 0000000000..2367f03e0d --- /dev/null +++ b/test/unit/eval/helpers.lua @@ -0,0 +1,72 @@ +local helpers = require('test.unit.helpers') + +local cimport = helpers.cimport +local to_cstr = helpers.to_cstr +local ffi = helpers.ffi +local eq = helpers.eq + +local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h') + +local null_string = {[true]='NULL string'} +local null_list = {[true]='NULL list'} +local type_key = {[true]='type key'} +local list_type = {[true]='list type'} + +local function list(...) + local ret = ffi.gc(eval.list_alloc(), eval.list_unref) + eq(0, ret.lv_refcount) + ret.lv_refcount = 1 + for i = 1, select('#', ...) do + local val = select(i, ...) + local typ = type(val) + if typ == 'string' then + eval.list_append_string(ret, to_cstr(val)) + elseif typ == 'table' and val == null_string then + eval.list_append_string(ret, nil) + elseif typ == 'table' and val == null_list then + eval.list_append_list(ret, nil) + elseif typ == 'table' and val[type_key] == list_type then + local itemlist = ffi.gc(list(table.unpack(val)), nil) + eq(1, itemlist.lv_refcount) + itemlist.lv_refcount = 0 + eval.list_append_list(ret, itemlist) + else + assert(false, 'Not implemented yet') + end + end + return ret +end + +local lst2tbl = function(l) + local ret = {[type_key]=list_type} + if l == nil then + return ret + end + local li = l.lv_first + -- (listitem_T *) NULL is equal to nil, but yet it is not false. + while li ~= nil do + local typ = li.li_tv.v_type + if typ == eval.VAR_STRING then + local str = li.li_tv.vval.v_string + if str == nil then + ret[#ret + 1] = null_string + else + ret[#ret + 1] = ffi.string(str) + end + else + assert(false, 'Not implemented yet') + end + li = li.li_next + end + return ret +end + +return { + null_string=null_string, + null_list=null_list, + list_type=list_type, + type_key=type_key, + + list=list, + lst2tbl=lst2tbl, +} diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua new file mode 100644 index 0000000000..4c5184995c --- /dev/null +++ b/test/unit/eval/tricks_spec.lua @@ -0,0 +1,43 @@ +local helpers = require('test.unit.helpers') + +local cimport = helpers.cimport +local to_cstr = helpers.to_cstr +local ffi = helpers.ffi +local eq = helpers.eq + +local eval = cimport('./src/nvim/eval.h', './src/nvim/memory.h') + +local eval_expr = function(expr) + return ffi.gc(eval.eval_expr(to_cstr(expr), nil), function(tv) + eval.clear_tv(tv) + eval.xfree(tv) + end) +end + +describe('NULL typval_T', function() + it('is produced by $XXX_UNEXISTENT_VAR_XXX', function() + -- Required for various tests which need to check whether typval_T with NULL + -- string works correctly. This test checks that unexistent environment + -- variable produces NULL string, not that some specific environment + -- variable does not exist. Last bit is left for the test writers. + local unexistent_env = 'XXX_UNEXISTENT_VAR_XXX' + while os.getenv(unexistent_env) ~= nil do + unexistent_env = unexistent_env .. '_XXX' + end + local rettv = eval_expr('$' .. unexistent_env) + eq(eval.VAR_STRING, rettv.v_type) + eq(nil, rettv.vval.v_string) + end) + + it('is produced by v:_null_list', function() + local rettv = eval_expr('v:_null_list') + eq(eval.VAR_LIST, rettv.v_type) + eq(nil, rettv.vval.v_list) + end) + + it('is produced by v:_null_dict', function() + local rettv = eval_expr('v:_null_dict') + eq(eval.VAR_DICT, rettv.v_type) + eq(nil, rettv.vval.v_dict) + end) +end) diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index 3f86c5f1b1..00637e0b8d 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -238,7 +238,7 @@ local function standalone(...) -- luacheck: ignore end -- uncomment this line (and comment the `return`) for standalone debugging -- example usage: --- ../../.deps/usr/bin/luajit formatc.lua ../../include/tempfile.h.generated.h +-- ../../.deps/usr/bin/luajit formatc.lua ../../include/fileio.h.generated.h -- ../../.deps/usr/bin/luajit formatc.lua /usr/include/malloc.h -- standalone(...) return formatc diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 7b43b2218c..426ae2d9e0 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -28,8 +28,10 @@ local function filter_complex_blocks(body) local result = {} for line in body:gmatch("[^\r\n]+") do - if not (string.find(line, "(^)", 1, true) ~= nil or - string.find(line, "_ISwupper", 1, true)) then + if not (string.find(line, "(^)", 1, true) ~= nil + or string.find(line, "_ISwupper", 1, true) + or string.find(line, "msgpack_zone_push_finalizer") + or string.find(line, "msgpack_unpacker_reserve_buffer")) then result[#result + 1] = line end end @@ -103,6 +105,11 @@ local function cimport(...) -- request a sorted version of the new lines (same relative order as the -- original preprocessed file) and feed that to the LuaJIT ffi local new_lines = new_cdefs:to_table() + if os.getenv('NVIM_TEST_PRINT_CDEF') == '1' then + for lnum, line in ipairs(new_lines) do + print(lnum, line) + end + end ffi.cdef(table.concat(new_lines, "\n")) return libnvim @@ -133,6 +140,7 @@ do local time = cimport('./src/nvim/os/time.h') time.time_init() main.early_init() + main.event_init() end -- C constants. diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua new file mode 100644 index 0000000000..9b2415a93f --- /dev/null +++ b/test/unit/mbyte_spec.lua @@ -0,0 +1,276 @@ +local helpers = require("test.unit.helpers") + +local ffi = helpers.ffi +local eq = helpers.eq + +local mbyte = helpers.cimport("./src/nvim/mbyte.h") + +describe('mbyte', function() + + -- Array for composing characters + local intp = ffi.typeof('int[?]') + local function to_intp() + -- how to get MAX_MCO from globals.h? + return intp(7, 1) + end + + -- Convert from bytes to string + local function to_string(bytes) + local s = {} + for i = 1, #bytes do + s[i] = string.char(bytes[i]) + end + return table.concat(s) + end + + before_each(function() + end) + + it('utf_ptr2char', function() + -- For strings with length 1 the first byte is returned. + for c = 0, 255 do + eq(c, mbyte.utf_ptr2char(to_string({c, 0}))) + end + + -- Some ill formed byte sequences that should not be recognized as UTF-8 + -- First byte: 0xc0 or 0xc1 + -- Second byte: 0x80 .. 0xbf + --eq(0x00c0, mbyte.utf_ptr2char(to_string({0xc0, 0x80}))) + --eq(0x00c1, mbyte.utf_ptr2char(to_string({0xc1, 0xbf}))) + -- + -- Sequences with more than four bytes + end) + + + describe('utfc_ptr2char_len', function() + + it('1-byte sequences', function() + local pcc = to_intp() + for c = 0, 255 do + eq(c, mbyte.utfc_ptr2char_len(to_string({c}), pcc, 1)) + eq(0, pcc[0]) + end + end) + + it('2-byte sequences', function() + local pcc = to_intp() + -- No combining characters + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x7f}), pcc, 2)) + eq(0, pcc[0]) + -- No combining characters + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x80}), pcc, 2)) + eq(0, pcc[0]) + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f}), pcc, 2)) + eq(0, pcc[0]) + -- One UTF-8 character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80}), pcc, 2)) + eq(0, pcc[0]) + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0xc0}), pcc, 2)) + eq(0, pcc[0]) + end) + + it('3-byte sequences', function() + local pcc = to_intp() + + -- No second UTF-8 character + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x80, 0x80}), pcc, 3)) + eq(0, pcc[0]) + -- No combining character + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xc2, 0x80}), pcc, 3)) + eq(0, pcc[0]) + + -- Combining character is U+0300 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80}), pcc, 3)) + eq(0x0300, pcc[0]) + eq(0x0000, pcc[1]) + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f, 0xcc}), pcc, 3)) + eq(0, pcc[0]) + -- Incomplete combining character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc}), pcc, 3)) + eq(0, pcc[0]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x20d0, mbyte.utfc_ptr2char_len(to_string({0xe2, 0x83, 0x90}), pcc, 3)) + eq(0, pcc[0]) + end) + + it('4-byte sequences', function() + local pcc = to_intp() + + -- No following combining character + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x7f, 0xcc, 0x80}), pcc, 4)) + eq(0, pcc[0]) + -- No second UTF-8 character + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xc2, 0xcc, 0x80}), pcc, 4)) + eq(0, pcc[0]) + + -- Combining character U+0300 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc}), pcc, 4)) + eq(0x0300, pcc[0]) + eq(0x0000, pcc[1]) + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f, 0xcc, 0x80}), pcc, 4)) + eq(0, pcc[0]) + -- No following UTF-8 character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0xcc}), pcc, 4)) + eq(0, pcc[0]) + -- Combining character U+0301 + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0x81}), pcc, 4)) + eq(0x0301, pcc[0]) + eq(0x0000, pcc[1]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80}), pcc, 4)) + eq(0, pcc[0]) + end) + + it('5+-byte sequences', function() + local pcc = to_intp() + + -- No following combining character + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x7f, 0xcc, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- No second UTF-8 character + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xc2, 0xcc, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + + -- Combining character U+0300 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc}), pcc, 5)) + eq(0x0300, pcc[0]) + eq(0x0000, pcc[1]) + + -- Combining characters U+0300 and U+0301 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc, 0x81}), pcc, 5)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0000, pcc[2]) + -- Combining characters U+0300, U+0301, U+0302 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82}), pcc, 7)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0000, pcc[3]) + -- Combining characters U+0300, U+0301, U+0302, U+0303 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83}), pcc, 9)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0000, pcc[4]) + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84}), pcc, 11)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0304, pcc[4]) + eq(0x0000, pcc[5]) + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, + -- U+0305 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85}), pcc, 13)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0304, pcc[4]) + eq(0x0305, pcc[5]) + eq(1, pcc[6]) + + -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, + -- U+0305, U+0306, but only save six (= MAX_MCO). + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85, 0xcc, 0x86}), pcc, 15)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0303, pcc[3]) + eq(0x0304, pcc[4]) + eq(0x0305, pcc[5]) + eq(0x0001, pcc[6]) + + -- Only three following combining characters U+0300, U+0301, U+0302 + pcc = to_intp() + eq(0x007f, mbyte.utfc_ptr2char_len(to_string( + {0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xc2, 0x80, 0xcc, 0x84, 0xcc, 0x85}), pcc, 13)) + eq(0x0300, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0302, pcc[2]) + eq(0x0000, pcc[3]) + + + -- No UTF-8 sequence + pcc = to_intp() + eq(0x00c2, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x7f, 0xcc, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- No following UTF-8 character + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0xcc, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- Combining character U+0301 + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0x81, 0x7f}), pcc, 5)) + eq(0x0301, pcc[0]) + eq(0x0000, pcc[1]) + -- Combining character U+0301 + pcc = to_intp() + eq(0x0080, mbyte.utfc_ptr2char_len(to_string({0xc2, 0x80, 0xcc, 0x81, 0xcc}), pcc, 5)) + eq(0x0301, pcc[0]) + eq(0x0000, pcc[1]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80, 0x7f}), pcc, 5)) + eq(0, pcc[0]) + + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80, 0x80}), pcc, 5)) + eq(0, pcc[0]) + -- One UTF-8 character + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string({0xf4, 0x80, 0x80, 0x80, 0xcc}), pcc, 5)) + eq(0, pcc[0]) + + -- Combining characters U+1AB0 and U+0301 + pcc = to_intp() + eq(0x100000, mbyte.utfc_ptr2char_len(to_string( + {0xf4, 0x80, 0x80, 0x80, 0xe1, 0xaa, 0xb0, 0xcc, 0x81}), pcc, 9)) + eq(0x1ab0, pcc[0]) + eq(0x0301, pcc[1]) + eq(0x0000, pcc[2]) + end) + + end) + +end) diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua index e0e12a24f2..9e00a3e8f8 100644 --- a/test/unit/os/env_spec.lua +++ b/test/unit/os/env_spec.lua @@ -148,8 +148,8 @@ describe('env function', function() local name = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN' local value = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCV' os_setenv(name, value, 1) - -- TODO(bobtwinkles) This only tests UNIX expansions. There should be a - -- test for windows as well + -- TODO(bobtwinkles) This only tests Unix expansions. There should be a + -- test for Windows as well local input1 = to_cstr('$NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN/test') local input2 = to_cstr('${NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN}/test') local output_buff1 = cstr(255, '') diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index 95c98f18a1..71b5e7f576 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -22,7 +22,7 @@ cimport('./src/nvim/main.h') cimport('./src/nvim/fileio.h') local fs = cimport('./src/nvim/os/os.h') cppimport('sys/stat.h') -cppimport('sys/fcntl.h') +cppimport('fcntl.h') cppimport('uv-errno.h') local buffer = "" @@ -148,7 +148,7 @@ describe('fs function', function() local function os_can_exe(name) local buf = ffi.new('char *[1]') buf[0] = NULL - local ok = fs.os_can_exe(to_cstr(name), buf) + local ok = fs.os_can_exe(to_cstr(name), buf, true) -- When os_can_exe returns true, it must set the path. -- When it returns false, the path must be NULL. diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index 01deefab0c..93103e4e8c 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -11,11 +11,12 @@ if allowed_os[jit.os] ~= true then end local helpers = require('test.unit.helpers') -local shell = helpers.cimport( +local cimported = helpers.cimport( './src/nvim/os/shell.h', './src/nvim/option_defs.h', './src/nvim/main.h', - './src/nvim/misc1.h' + './src/nvim/misc1.h', + './src/nvim/memory.h' ) local ffi, eq = helpers.ffi, helpers.eq local intern = helpers.internalize @@ -24,16 +25,32 @@ local NULL = ffi.cast('void *', 0) describe('shell functions', function() setup(function() - shell.event_init() -- os_system() can't work when the p_sh and p_shcf variables are unset - shell.p_sh = to_cstr('/bin/bash') - shell.p_shcf = to_cstr('-c') + cimported.p_sh = to_cstr('/bin/bash') + cimported.p_shcf = to_cstr('-c') end) teardown(function() - shell.event_teardown() + cimported.event_teardown() end) + local function shell_build_argv(cmd, extra_args) + local res = cimported.shell_build_argv( + cmd and to_cstr(cmd), + extra_args and to_cstr(extra_args)) + local argc = 0 + local ret = {} + -- Explicitly free everything, so if it is not in allocated memory it will + -- crash. + while res[argc] ~= nil do + ret[#ret + 1] = ffi.string(res[argc]) + cimported.xfree(res[argc]) + argc = argc + 1 + end + cimported.xfree(res) + return ret + end + local function os_system(cmd, input) local input_or = input and to_cstr(input) or NULL local input_len = (input ~= nil) and string.len(input) or 0 @@ -41,8 +58,8 @@ describe('shell functions', function() local nread = ffi.new('size_t[1]') local argv = ffi.cast('char**', - shell.shell_build_argv(to_cstr(cmd), nil)) - local status = shell.os_system(argv, input_or, input_len, output, nread) + cimported.shell_build_argv(to_cstr(cmd), nil)) + local status = cimported.os_system(argv, input_or, input_len, output, nread) return status, intern(output[0], nread[0]) end @@ -74,4 +91,44 @@ describe('shell functions', function() eq(2, status) end) end) + + describe('shell_build_argv', function() + local saved_opts = {} + + setup(function() + saved_opts.p_sh = cimported.p_sh + saved_opts.p_shcf = cimported.p_shcf + end) + + teardown(function() + cimported.p_sh = saved_opts.p_sh + cimported.p_shcf = saved_opts.p_shcf + end) + + it('works with NULL arguments', function() + eq({'/bin/bash'}, shell_build_argv(nil, nil)) + end) + + it('works with cmd', function() + eq({'/bin/bash', '-c', 'abc def'}, shell_build_argv('abc def', nil)) + end) + + it('works with extra_args', function() + eq({'/bin/bash', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl')) + end) + + it('works with cmd and extra_args', function() + eq({'/bin/bash', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl')) + end) + + it('splits and unquotes &shell and &shellcmdflag', function() + cimported.p_sh = to_cstr('/Program" "Files/zsh -f') + cimported.p_shcf = to_cstr('-x -o "sh word split" "-"c') + eq({'/Program Files/zsh', '-f', + 'ghi jkl', + '-x', '-o', 'sh word split', + '-c', 'abc def'}, + shell_build_argv('abc def', 'ghi jkl')) + end) + end) end) diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua new file mode 100644 index 0000000000..e935d2af6a --- /dev/null +++ b/test/unit/strings_spec.lua @@ -0,0 +1,68 @@ +local helpers = require("test.unit.helpers") + +local cimport = helpers.cimport +local eq = helpers.eq +local ffi = helpers.ffi +local to_cstr = helpers.to_cstr + +local strings = cimport('stdlib.h', './src/nvim/strings.h', + './src/nvim/memory.h') + +describe('vim_strnsave_unquoted()', function() + local vim_strnsave_unquoted = function(s, len) + local res = strings.vim_strnsave_unquoted(to_cstr(s), len or #s) + local ret = ffi.string(res) + -- Explicitly free memory so we are sure it is allocated: if it was not it + -- will crash. + strings.xfree(res) + return ret + end + + it('copies unquoted strings as-is', function() + eq('-c', vim_strnsave_unquoted('-c')) + eq('', vim_strnsave_unquoted('')) + end) + + it('respects length argument', function() + eq('', vim_strnsave_unquoted('-c', 0)) + eq('-', vim_strnsave_unquoted('-c', 1)) + eq('-', vim_strnsave_unquoted('"-c', 2)) + end) + + it('unquotes fully quoted word', function() + eq('/bin/sh', vim_strnsave_unquoted('"/bin/sh"')) + end) + + it('unquotes partially quoted word', function() + eq('/Program Files/sh', vim_strnsave_unquoted('/Program" "Files/sh')) + end) + + it('removes ""', function() + eq('/Program Files/sh', vim_strnsave_unquoted('/""Program" "Files/sh')) + end) + + it('performs unescaping of "', function() + eq('/"Program Files"/sh', vim_strnsave_unquoted('/"\\""Program Files"\\""/sh')) + end) + + it('performs unescaping of \\', function() + eq('/\\Program Files\\foo/sh', vim_strnsave_unquoted('/"\\\\"Program Files"\\\\foo"/sh')) + end) + + it('strips quote when there is no pair to it', function() + eq('/Program Files/sh', vim_strnsave_unquoted('/Program" Files/sh')) + eq('', vim_strnsave_unquoted('"')) + end) + + it('allows string to end with one backslash unescaped', function() + eq('/Program Files/sh\\', vim_strnsave_unquoted('/Program" Files/sh\\')) + end) + + it('does not perform unescaping out of quotes', function() + eq('/Program\\ Files/sh\\', vim_strnsave_unquoted('/Program\\ Files/sh\\')) + end) + + it('does not unescape \\n', function() + eq('/Program\\nFiles/sh', vim_strnsave_unquoted('/Program"\\n"Files/sh')) + end) +end) diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua index e558ff04c8..7975d11aed 100644 --- a/test/unit/tempfile_spec.lua +++ b/test/unit/tempfile_spec.lua @@ -2,9 +2,12 @@ local lfs = require 'lfs' local helpers = require 'test.unit.helpers' local os = helpers.cimport './src/nvim/os/os.h' -local tempfile = helpers.cimport './src/nvim/tempfile.h' +local tempfile = helpers.cimport './src/nvim/fileio.h' describe('tempfile related functions', function() + before_each(function() + tempfile.vim_deltempdir() + end) after_each(function() tempfile.vim_deltempdir() end) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index c7053e6a93..f584815499 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -21,6 +21,10 @@ option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED}) option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED}) option(USE_BUNDLED_LUAJIT "Use the bundled version of luajit." ${USE_BUNDLED}) option(USE_BUNDLED_LUAROCKS "Use the bundled version of luarocks." ${USE_BUNDLED}) +option(USE_BUNDLED_LUV "Use the bundled version of luv." ${USE_BUNDLED}) +#XXX(tarruda): Lua is only used for debugging the functional test client, no +# build it unless explicitly requested +option(USE_BUNDLED_LUA "Use the bundled version of lua." OFF) option(USE_EXISTING_SRC_DIR "Skip download of deps sources in case of existing source directory." OFF) @@ -55,7 +59,7 @@ endif() # Cross compiling: use these for dependencies built for the # HOST system, when not crosscompiling these should be the -# same as DEPS_*. Except when targeting UNIX in which case +# same as DEPS_*. Except when targeting Unix in which case # want all the dependencies to use the same compiler. if(CMAKE_CROSSCOMPILING AND NOT UNIX) set(HOSTDEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/host") @@ -71,15 +75,18 @@ endif() include(ExternalProject) -set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.7.3.tar.gz) -set(LIBUV_SHA256 db5d46318e18330c696d954747036e1be8e2346411d4f30236d7e2f499f0cfab) +set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.8.0.tar.gz) +set(LIBUV_SHA256 906e1a5c673c95cb261adeacdb7308a65b4a8f7c9c50d85f3021364951fa9cde) set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/cpp-1.0.0.tar.gz) set(MSGPACK_SHA256 afda64ca445203bb7092372b822bae8b2539fdcebbfc3f753f393628c2bcfe7d) -set(LUAJIT_URL http://luajit.org/download/LuaJIT-2.0.4.tar.gz) +set(LUAJIT_URL https://github.com/neovim/deps/raw/master/opt/LuaJIT-2.0.4.tar.gz) set(LUAJIT_SHA256 620fa4eb12375021bef6e4f237cbd2dd5d49e56beb414bee052c746beef1807d) +set(LUA_URL https://github.com/lua/lua/archive/5.1.5.tar.gz) +set(LUA_SHA256 1cd642c4c39778306a14e62ccddace5c7a4fb2257b0b06f43bc81cf305c7415f) + set(LUAROCKS_URL https://github.com/keplerproject/luarocks/archive/5d8a16526573b36d5b22aa74866120c998466697.tar.gz) set(LUAROCKS_SHA256 cae709111c5701235770047dfd7169f66b82ae1c7b9b79207f9df0afb722bfd9) @@ -89,11 +96,14 @@ set(UNIBILIUM_SHA256 623af1099515e673abfd3cae5f2fa808a09ca55dda1c65a7b5c9424eb30 set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.18.tar.gz) set(LIBTERMKEY_SHA256 239746de41c845af52bb3c14055558f743292dd6c24ac26c2d6567a5a6093926) -set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/1b745d29d45623aa8d22a7b9288c7b0e331c7088.tar.gz) -set(LIBVTERM_SHA256 3fc75908256c0d158d6c2a32d39f34e86bfd26364f5404b7d9c03bb70cdc3611) +set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/a9c7c6fd20fa35e0ad3e0e98901ca12dfca9c25c.tar.gz) +set(LIBVTERM_SHA256 1a4272be91d9614dc183a503786df83b6584e4afaab7feaaa5409f841afbd796) set(JEMALLOC_URL https://github.com/jemalloc/jemalloc/releases/download/4.0.2/jemalloc-4.0.2.tar.bz2) set(JEMALLOC_SHA256 0d8a9c8a98adb6983e0ccb521d45d9db1656ef3e71d0b14fb333f2c8138f4611) + +set(LUV_URL https://github.com/luvit/luv/archive/146f1ce4c08c3b67f604c9ee1e124b1cf5c15cf3.tar.gz) +set(LUV_SHA256 3d537f8eb9fa5adb146a083eae22af886aee324ec268e2aa0fa75f2f1c52ca7a) if(USE_BUNDLED_UNIBILIUM) include(BuildUnibilium) @@ -119,6 +129,10 @@ if(USE_BUNDLED_LUAJIT) include(BuildLuajit) endif() +if(USE_BUNDLED_LUA AND NOT CMAKE_CROSSCOMPILING) + include(BuildLua) +endif() + if(USE_BUNDLED_LUAROCKS) include(BuildLuarocks) endif() @@ -127,6 +141,10 @@ if(USE_BUNDLED_JEMALLOC) include(BuildJeMalloc) endif() +if(USE_BUNDLED_LUV) + include(BuildLuv) +endif() + add_custom_target(clean-shared-libraries COMMAND ${CMAKE_COMMAND} -DREMOVE_FILE_GLOB=${DEPS_INSTALL_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}*${CMAKE_SHARED_LIBRARY_SUFFIX}* diff --git a/third-party/cmake/BuildJeMalloc.cmake b/third-party/cmake/BuildJeMalloc.cmake index 8b0fac36a4..637aadaad9 100644 --- a/third-party/cmake/BuildJeMalloc.cmake +++ b/third-party/cmake/BuildJeMalloc.cmake @@ -19,6 +19,6 @@ ExternalProject_Add(jemalloc CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/src/jemalloc/configure CC=${DEPS_C_COMPILER} --prefix=${DEPS_INSTALL_DIR} BUILD_COMMAND "" - INSTALL_COMMAND ${MAKE_PRG} install_include install_lib) + INSTALL_COMMAND ${MAKE_PRG} install_include install_lib_static) list(APPEND THIRD_PARTY_DEPS jemalloc) diff --git a/third-party/cmake/BuildLibuv.cmake b/third-party/cmake/BuildLibuv.cmake index 5debe5c071..1d8d69e64e 100644 --- a/third-party/cmake/BuildLibuv.cmake +++ b/third-party/cmake/BuildLibuv.cmake @@ -41,18 +41,18 @@ set(UNIX_CFGCMD sh ${DEPS_BUILD_DIR}/src/libuv/autogen.sh && CC=${DEPS_C_COMPILER}) if(UNIX) - BuildLibUv( + BuildLibuv( CONFIGURE_COMMAND ${UNIX_CFGCMD} INSTALL_COMMAND ${MAKE_PRG} V=1 install) elseif(MINGW AND CMAKE_CROSSCOMPILING) # Build libuv for the host - BuildLibUv(TARGET libuv_host + BuildLibuv(TARGET libuv_host CONFIGURE_COMMAND sh ${DEPS_BUILD_DIR}/src/libuv_host/autogen.sh && ${DEPS_BUILD_DIR}/src/libuv_host/configure --with-pic --disable-shared --prefix=${HOSTDEPS_INSTALL_DIR} CC=${HOST_C_COMPILER} INSTALL_COMMAND ${MAKE_PRG} V=1 install) # Build libuv for the target - BuildLibUv( + BuildLibuv( CONFIGURE_COMMAND ${UNIX_CFGCMD} --host=${CROSS_TARGET} INSTALL_COMMAND ${MAKE_PRG} V=1 install) @@ -70,7 +70,7 @@ elseif(WIN32 AND MSVC) else() set(VS_ARCH x64) endif() - BuildLibUv( + BuildLibuv( # By default this creates Debug builds BUILD_COMMAND set PYTHON=${PYTHON_EXECUTABLE} COMMAND ${DEPS_BUILD_DIR}/src/libuv/vcbuild.bat static debug ${VS_ARCH} INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/lib diff --git a/third-party/cmake/BuildLua.cmake b/third-party/cmake/BuildLua.cmake new file mode 100644 index 0000000000..1c5e2a186c --- /dev/null +++ b/third-party/cmake/BuildLua.cmake @@ -0,0 +1,85 @@ +include(CMakeParseArguments) + +# BuildLua(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...) +# Reusable function to build lua, wraps ExternalProject_Add. +# Failing to pass a command argument will result in no command being run +function(BuildLua) + cmake_parse_arguments(_lua + "" + "" + "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND" + ${ARGN}) + + if(NOT _lua_CONFIGURE_COMMAND AND NOT _lua_BUILD_COMMAND + AND NOT _lua_INSTALL_COMMAND) + message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND") + endif() + + ExternalProject_Add(lua + PREFIX ${DEPS_BUILD_DIR} + URL ${LUA_URL} + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua + DOWNLOAD_COMMAND ${CMAKE_COMMAND} + -DPREFIX=${DEPS_BUILD_DIR} + -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/lua + -DURL=${LUA_URL} + -DEXPECTED_SHA256=${LUA_SHA256} + -DTARGET=lua + -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake + CONFIGURE_COMMAND "${_lua_CONFIGURE_COMMAND}" + BUILD_IN_SOURCE 1 + BUILD_COMMAND "${_lua_BUILD_COMMAND}" + INSTALL_COMMAND "${_lua_INSTALL_COMMAND}") +endfunction() + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LUA_TARGET linux) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(LUA_TARGET macosx) +elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(LUA_TARGET freebsd) +elseif(CMAKE_SYSTEM_NAME MATCHES "BSD") + set(CMAKE_LUA_TARGET bsd) +elseif(SYSTEM_NAME MATCHES "^MINGW") + set(CMAKE_LUA_TARGET mingw) +else() + if(UNIX) + set(LUA_TARGET posix) + else() + set(LUA_TARGET generic) + endif() +endif() + +set(LUA_CONFIGURE_COMMAND + sed -e "/^CC/s@gcc@${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}@" + -e "/^CFLAGS/s@-O2@-g3@" + -e "s@-lreadline@@g" + -e "s@-lhistory@@g" + -e "s@-lncurses@@g" + -i ${DEPS_BUILD_DIR}/src/lua/src/Makefile && + sed -e "/#define LUA_USE_READLINE/d" + -i ${DEPS_BUILD_DIR}/src/lua/src/luaconf.h) +set(LUA_BUILD_COMMAND + ${MAKE_PRG} ${LUA_TARGET}) +set(LUA_INSTALL_COMMAND + ${MAKE_PRG} INSTALL_TOP=${DEPS_INSTALL_DIR} install) + +message(STATUS "Lua target is ${LUA_TARGET}") + +BuildLua(CONFIGURE_COMMAND ${LUA_CONFIGURE_COMMAND} + BUILD_COMMAND ${LUA_BUILD_COMMAND} + INSTALL_COMMAND ${LUA_INSTALL_COMMAND}) +list(APPEND THIRD_PARTY_DEPS lua) +add_dependencies(lua busted) + +set(BUSTED ${DEPS_INSTALL_DIR}/bin/busted) +set(BUSTED_LUA ${BUSTED}-lua) + +add_custom_command(OUTPUT ${BUSTED_LUA} + COMMAND sed -e 's/^exec/exec $$LUA_DEBUGGER/' -e 's/jit//g' < ${BUSTED} > ${BUSTED_LUA} && chmod +x ${BUSTED_LUA} + DEPENDS lua) +add_custom_target(busted-lua + DEPENDS ${DEPS_INSTALL_DIR}/bin/busted-lua) + +list(APPEND THIRD_PARTY_DEPS busted-lua) diff --git a/third-party/cmake/BuildLuarocks.cmake b/third-party/cmake/BuildLuarocks.cmake index a6126af789..1662f89b24 100644 --- a/third-party/cmake/BuildLuarocks.cmake +++ b/third-party/cmake/BuildLuarocks.cmake @@ -89,25 +89,26 @@ list(APPEND THIRD_PARTY_DEPS luarocks) if(USE_BUNDLED_LUAJIT) add_dependencies(luarocks luajit) + if(MINGW AND CMAKE_CROSSCOMPILING) + add_dependencies(luarocks luajit_host) + endif() endif() # Each target depends on the previous module, this serializes all calls to # luarocks since it is unhappy to be called in parallel. -add_custom_command(OUTPUT ${HOSTDEPS_LIB_DIR}/luarocks/rocks/lua-messagepack +add_custom_command(OUTPUT ${HOSTDEPS_LIB_DIR}/luarocks/rocks/mpack COMMAND ${LUAROCKS_BINARY} - ARGS build lua-messagepack ${LUAROCKS_BUILDARGS} + ARGS build mpack ${LUAROCKS_BUILDARGS} DEPENDS luarocks) -add_custom_target(lua-messagepack - DEPENDS ${HOSTDEPS_LIB_DIR}/luarocks/rocks/lua-messagepack) -list(APPEND THIRD_PARTY_DEPS lua-messagepack) +add_custom_target(mpack + DEPENDS ${HOSTDEPS_LIB_DIR}/luarocks/rocks/mpack) +list(APPEND THIRD_PARTY_DEPS mpack) -# Like before, depend on lua-messagepack to ensure serialization of install -# commands add_custom_command(OUTPUT ${HOSTDEPS_LIB_DIR}/luarocks/rocks/lpeg COMMAND ${LUAROCKS_BINARY} ARGS build lpeg ${LUAROCKS_BUILDARGS} - DEPENDS lua-messagepack) + DEPENDS mpack) add_custom_target(lpeg DEPENDS ${HOSTDEPS_LIB_DIR}/luarocks/rocks/lpeg) @@ -117,7 +118,7 @@ if(USE_BUNDLED_BUSTED) add_custom_command(OUTPUT ${HOSTDEPS_BIN_DIR}/busted COMMAND ${LUAROCKS_BINARY} ARGS build https://raw.githubusercontent.com/Olivine-Labs/busted/v2.0.rc11-0/busted-2.0.rc11-0.rockspec ${LUAROCKS_BUILDARGS} - DEPENDS luarocks) + DEPENDS lpeg) add_custom_target(busted DEPENDS ${HOSTDEPS_BIN_DIR}/busted) @@ -128,10 +129,22 @@ if(USE_BUNDLED_BUSTED) add_custom_target(luacheck DEPENDS ${HOSTDEPS_BIN_DIR}/luacheck) + set(LUV_DEPS luacheck luv-static) + if(MINGW AND CMAKE_CROSSCOMPILING) + set(LUV_DEPS ${LUV_DEPS} libuv_host) + endif() + add_custom_command(OUTPUT ${HOSTDEPS_LIB_DIR}/luarocks/rocks/luv + COMMAND ${LUAROCKS_BINARY} + ARGS make ${LUAROCKS_BUILDARGS} LIBUV_DIR=${HOSTDEPS_INSTALL_DIR} CFLAGS='-O0 -g3 -fPIC' + WORKING_DIRECTORY ${DEPS_BUILD_DIR}/src/luv + DEPENDS ${LUV_DEPS}) + add_custom_target(luv + DEPENDS ${HOSTDEPS_LIB_DIR}/luarocks/rocks/luv) + add_custom_command(OUTPUT ${HOSTDEPS_LIB_DIR}/luarocks/rocks/nvim-client COMMAND ${LUAROCKS_BINARY} - ARGS build https://raw.githubusercontent.com/neovim/lua-client/0.0.1-14/nvim-client-0.0.1-14.rockspec ${LUAROCKS_BUILDARGS} LIBUV_DIR=${HOSTDEPS_INSTALL_DIR} - DEPENDS luacheck libuv) + ARGS build https://raw.githubusercontent.com/neovim/lua-client/0.0.1-24/nvim-client-0.0.1-24.rockspec ${LUAROCKS_BUILDARGS} + DEPENDS luv) add_custom_target(nvim-client DEPENDS ${HOSTDEPS_LIB_DIR}/luarocks/rocks/nvim-client) diff --git a/third-party/cmake/BuildLuv.cmake b/third-party/cmake/BuildLuv.cmake new file mode 100644 index 0000000000..3060590bce --- /dev/null +++ b/third-party/cmake/BuildLuv.cmake @@ -0,0 +1,90 @@ +include(CMakeParseArguments) + +# BuildLuv(PATCH_COMMAND ... CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...) +# Reusable function to build luv, wraps ExternalProject_Add. +# Failing to pass a command argument will result in no command being run +function(BuildLuv) + cmake_parse_arguments(_luv + "" + "" + "PATCH_COMMAND;CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND" + ${ARGN}) + + if(NOT _luv_CONFIGURE_COMMAND AND NOT _luv_BUILD_COMMAND + AND NOT _luv_INSTALL_COMMAND) + message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND") + endif() + + ExternalProject_Add(luv-static + PREFIX ${DEPS_BUILD_DIR} + URL ${LUV_URL} + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luv + DOWNLOAD_COMMAND ${CMAKE_COMMAND} + -DPREFIX=${DEPS_BUILD_DIR} + -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luv + -DURL=${LUV_URL} + -DEXPECTED_SHA256=${LUV_SHA256} + -DTARGET=luv + -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake + PATCH_COMMAND "${_luv_PATCH_COMMAND}" + CONFIGURE_COMMAND "${_luv_CONFIGURE_COMMAND}" + BUILD_COMMAND "${_luv_BUILD_COMMAND}" + INSTALL_COMMAND "${_luv_INSTALL_COMMAND}") +endfunction() + +set(LUV_SRC_DIR ${DEPS_BUILD_DIR}/src/luv) +set(LUV_INCLUDE_FLAGS + "-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.0") + +set(LUV_PATCH_COMMAND + ${CMAKE_COMMAND} -DLUV_SRC_DIR=${LUV_SRC_DIR} + -P ${PROJECT_SOURCE_DIR}/cmake/PatchLuv.cmake) + +set(LUV_CONFIGURE_COMMAND_COMMON + ${CMAKE_COMMAND} ${LUV_SRC_DIR} + -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} + -DLUA_BUILD_TYPE=System + -DWITH_SHARED_LIBUV=ON + -DBUILD_SHARED_LIBS=OFF + -DBUILD_MODULE=OFF) + +if(MINGW AND CMAKE_CROSSCOMPILING) + get_filename_component(TOOLCHAIN ${CMAKE_TOOLCHAIN_FILE} REALPATH) + set(LUV_CONFIGURE_COMMAND + ${LUV_CONFIGURE_COMMAND_COMMON} + # Pass toolchain + -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} + "-DCMAKE_C_FLAGS:STRING=${LUV_INCLUDE_FLAGS} -D_WIN32_WINNT=0x0600" + # Hack to avoid -rdynamic in Mingw + -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="") +elseif(MSVC) + set(LUV_CONFIGURE_COMMAND + ${LUV_CONFIGURE_COMMAND_COMMON} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + # Same as Unix without fPIC + "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} ${LUV_INCLUDE_FLAGS}" + # Make sure we use the same generator, otherwise we may + # accidentaly end up using different MSVC runtimes + -DCMAKE_GENERATOR=${CMAKE_GENERATOR} + # Use static runtime + -DCMAKE_C_FLAGS_DEBUG="-MTd" + -DCMAKE_C_FLAGS_RELEASE="-MT") +else() + set(LUV_CONFIGURE_COMMAND + ${LUV_CONFIGURE_COMMAND_COMMON} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} ${LUV_INCLUDE_FLAGS} -fPIC") +endif() + +set(LUV_BUILD_COMMAND ${CMAKE_COMMAND} --build .) +set(LUV_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install) + +BuildLuv(PATCH_COMMAND ${LUV_PATCH_COMMAND} + CONFIGURE_COMMAND ${LUV_CONFIGURE_COMMAND} + BUILD_COMMAND ${LUV_BUILD_COMMAND} + INSTALL_COMMAND ${LUV_INSTALL_COMMAND}) + +list(APPEND THIRD_PARTY_DEPS luv-static) +add_dependencies(luv-static luajit) +add_dependencies(luv-static libuv) diff --git a/third-party/cmake/BuildMsgpack.cmake b/third-party/cmake/BuildMsgpack.cmake index f0d5fab676..4b6b361e85 100644 --- a/third-party/cmake/BuildMsgpack.cmake +++ b/third-party/cmake/BuildMsgpack.cmake @@ -53,7 +53,7 @@ if(MINGW AND CMAKE_CROSSCOMPILING) # Hack to avoid -rdynamic in Mingw -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="") elseif(MSVC) - # Same as UNIX without fPIC + # Same as Unix without fPIC set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack -DMSGPACK_ENABLE_CXX=OFF -DMSGPACK_BUILD_TESTS=OFF diff --git a/third-party/cmake/PatchLuv.cmake b/third-party/cmake/PatchLuv.cmake new file mode 100644 index 0000000000..96595a2f30 --- /dev/null +++ b/third-party/cmake/PatchLuv.cmake @@ -0,0 +1,29 @@ +# replace luv default rockspec with the alternate one under the "rockspecs" +# directory +file(GLOB LUV_ROCKSPEC RELATIVE ${LUV_SRC_DIR} ${LUV_SRC_DIR}/*.rockspec) +file(RENAME ${LUV_SRC_DIR}/rockspecs/${LUV_ROCKSPEC} ${LUV_SRC_DIR}/${LUV_ROCKSPEC}) + +# Some versions of mingw are missing defines required by luv dns module, add +# them now +set(LUV_SRC_DNS_C_DEFS +"#ifndef AI_NUMERICSERV +# define AI_NUMERICSERV 0x0008 +#endif +#ifndef AI_ALL +# define AI_ALL 0x00000100 +#endif +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0x00000400 +#endif +#ifndef AI_V4MAPPED +# define AI_V4MAPPED 0x00000800 +#endif") + +file(READ ${LUV_SRC_DIR}/src/dns.c LUV_SRC_DNS_C) +string(REPLACE + "\n#include <netdb.h>" + "\n#include <netdb.h>\n#else\n${LUV_SRC_DNS_C_DEFS}" + LUV_SRC_DNS_C_PATCHED + "${LUV_SRC_DNS_C}") +file(WRITE ${LUV_SRC_DIR}/src/dns.c "${LUV_SRC_DNS_C_PATCHED}") + |