diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2022-08-03 00:08:17 -0600 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2022-08-03 00:08:17 -0600 |
commit | 9449e1b8d273ff78eb894c588110ffa0c17d6ee3 (patch) | |
tree | 9e4470c33bd4187d9f42f0b2c4aaa995310c5be8 | |
parent | 308e1940dcd64aa6c344c403d4f9e0dda58d9c5c (diff) | |
parent | b8dcbcc732baf84fc48d6b272c3ade0bcb129b3b (diff) | |
download | rneovim-9449e1b8d273ff78eb894c588110ffa0c17d6ee3.tar.gz rneovim-9449e1b8d273ff78eb894c588110ffa0c17d6ee3.tar.bz2 rneovim-9449e1b8d273ff78eb894c588110ffa0c17d6ee3.zip |
Merge remote-tracking branch 'upstream/master' into rahm
282 files changed, 28361 insertions, 16889 deletions
diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml index 60548f4f58..b5799e424b 100644 --- a/.builds/openbsd.yml +++ b/.builds/openbsd.yml @@ -6,11 +6,11 @@ packages: - autoconf-2.71 - automake-1.16.3 - cmake -- gettext-runtime-0.21p1 -- gettext-tools-0.21p1 +- gettext-runtime +- gettext-tools - gmake - libtool -- ninja-1.10.2p0 +- ninja - unzip-6.0p14 - gdb diff --git a/.github/labeler.yml b/.github/labeler.yml index 04e7b281d1..e86c7aabe8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -27,8 +27,6 @@ - src/nvim/terminal.* "column": - - src/nvim/mark.h - - src/nvim/mark.c - src/nvim/sign* "folds": diff --git a/.github/workflows/release-winget.yml b/.github/workflows/release-winget.yml new file mode 100644 index 0000000000..c3ca5fe752 --- /dev/null +++ b/.github/workflows/release-winget.yml @@ -0,0 +1,12 @@ +name: Publish to WinGet +on: + release: + types: [released] +jobs: + publish: + runs-on: windows-latest # action can only be run on windows + steps: + - uses: vedantmgoyal2009/winget-releaser@latest + with: + identifier: Neovim.Neovim + token: ${{ secrets.WINGET_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d57c09ab8..cab57add52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,14 +112,12 @@ jobs: CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH= $OSX_FLAGS" \ DEPS_CMAKE_FLAGS="$OSX_FLAGS" make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-macos" install - - name: Create package - run: | - cd "$GITHUB_WORKSPACE/build/release" - tar cfz nvim-macos.tar.gz nvim-macos + cd "$GITHUB_WORKSPACE/build/" + cpack -C "$NVIM_BUILD_TYPE" - uses: actions/upload-artifact@v3 with: name: nvim-macos - path: build/release/nvim-macos.tar.gz + path: build/nvim-macos.tar.gz retention-days: 1 windows: diff --git a/CMakeLists.txt b/CMakeLists.txt index b996a8fb0c..caf9658699 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,16 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") # them be included as one of the first places to look for dependencies. list(APPEND CMAKE_PREFIX_PATH /sw /opt/local) + # If the macOS deployment target is not set manually (via $MACOSX_DEPLOYMENT_TARGET), + # fall back to local system version. Needs to be done both here and in cmake.deps. + if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + execute_process(COMMAND sw_vers -productVersion + OUTPUT_VARIABLE MACOS_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "${MACOS_VERSION}") + endif() + message("Using deployment target ${CMAKE_OSX_DEPLOYMENT_TARGET}") + # Work around some old, broken detection by CMake for knowing when to use the # isystem flag. Apple's compilers have supported this for quite some time # now. @@ -665,6 +675,19 @@ add_dependencies(lintcommit nvim) add_custom_target(lint) add_dependencies(lint check-single-includes lintc lintlua lintpy lintsh lintcommit lintuncrustify) +# +# Format +# +add_custom_target(formatlua + COMMAND ${CMAKE_COMMAND} + -D FORMAT_PRG=${STYLUA_PRG} + -D LANG=lua + -P ${PROJECT_SOURCE_DIR}/cmake/Format.cmake + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + +add_custom_target(format) +add_dependencies(format formatc formatlua) + install_helper( FILES ${CMAKE_SOURCE_DIR}/src/man/nvim.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -787,4 +810,3 @@ add_custom_target(uninstall if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(cmake.packaging) endif() - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e26d0d63c5..17622fa33a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -218,6 +218,11 @@ You can lint a single file (but this will _not_ exclude legacy errors): ### Style +- You can format files by using: +``` + make format +``` +This will format changed Lua and C files with all appropriate flags set. - Style rules are (mostly) defined by `src/uncrustify.cfg` which tries to match the [style-guide]. To use the Nvim `gq` command with `uncrustify`: ``` @@ -137,7 +137,7 @@ helphtml: | nvim build/runtime/doc/tags functionaltest functionaltest-lua unittest benchmark: | nvim $(BUILD_TOOL) -C build $@ -lintlua lintsh lintpy lintuncrustify lintc lintcfull check-single-includes generated-sources lintcommit lint: | build/.ran-cmake +lintlua lintsh lintpy lintuncrustify lintc lintcfull check-single-includes generated-sources lintcommit lint formatc formatlua format: | build/.ran-cmake $(CMAKE_PRG) --build build --target $@ test: functionaltest unittest @@ -174,4 +174,4 @@ $(DEPS_BUILD_DIR)/%: phony_force $(BUILD_TOOL) -C $(DEPS_BUILD_DIR) $(patsubst $(DEPS_BUILD_DIR)/%,%,$@) endif -.PHONY: test lintlua lintpy lintsh functionaltest unittest lint lintc clean distclean nvim libnvim cmake deps install appimage checkprefix lintcommit +.PHONY: test lintlua lintpy lintsh functionaltest unittest lint lintc clean distclean nvim libnvim cmake deps install appimage checkprefix lintcommit formatc formatlua format diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt index 5427382783..8724ab4916 100644 --- a/cmake.deps/CMakeLists.txt +++ b/cmake.deps/CMakeLists.txt @@ -1,5 +1,5 @@ # This is not meant to be included by the top-level. -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.10) project(NVIM_DEPS C) # Needed for: check_c_compiler_flag() @@ -132,6 +132,18 @@ if(CMAKE_OSX_ARCHITECTURES) endforeach() endif() +# If the macOS deployment target is not set manually (via $MACOSX_DEPLOYMENT_TARGET), +# fall back to local system version. Needs to be done here and in top-level CMakeLists.txt. +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + execute_process(COMMAND sw_vers -productVersion + OUTPUT_VARIABLE MACOS_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "${MACOS_VERSION}") + endif() + message("-- Using deployment target ${CMAKE_OSX_DEPLOYMENT_TARGET}") +endif() + set(HOSTDEPS_INSTALL_DIR "${DEPS_INSTALL_DIR}") set(HOSTDEPS_BIN_DIR "${DEPS_BIN_DIR}") set(HOSTDEPS_LIB_DIR "${DEPS_LIB_DIR}") @@ -147,8 +159,8 @@ set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-4.0.0/m set(MSGPACK_SHA256 420fe35e7572f2a168d17e660ef981a589c9cbe77faa25eb34a520e1fcc032c8) # https://github.com/LuaJIT/LuaJIT/tree/v2.1 -set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/50936d784474747b4569d988767f1b5bab8bb6d0.tar.gz) -set(LUAJIT_SHA256 4d44e4709130b031c1c2c81cf5c102dfce877bf454409dabba03249e18870e66) +set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/a7d0265480c662964988f83d4e245bf139eb7cc0.tar.gz) +set(LUAJIT_SHA256 7d7f58ca5c02b453ed4ddd2298e741053cbd6cd3d96e79460d06ec6684244c59) set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz) set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333) diff --git a/cmake.deps/cmake/BuildLuajit.cmake b/cmake.deps/cmake/BuildLuajit.cmake index c8d5b39398..9b393310d6 100644 --- a/cmake.deps/cmake/BuildLuajit.cmake +++ b/cmake.deps/cmake/BuildLuajit.cmake @@ -60,17 +60,10 @@ set(BUILDCMD_UNIX ${MAKE_PRG} CFLAGS=-fPIC CCDEBUG+=-g Q=) +# Setting MACOSX_DEPLOYMENT_TARGET is mandatory for LuaJIT; use version set by +# cmake.deps/CMakeLists.txt (either environment variable or current system version). if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - if(CMAKE_OSX_DEPLOYMENT_TARGET) - set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") - else() - execute_process(COMMAND sw_vers -productVersion - OUTPUT_VARIABLE MACOS_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE) - set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${MACOS_VERSION}") - endif() -else() - set(DEPLOYMENT_TARGET "") + set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() if((UNIX AND NOT APPLE) OR (APPLE AND NOT CMAKE_OSX_ARCHITECTURES)) diff --git a/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake b/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake index 7a82f4248b..32d973680f 100644 --- a/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake +++ b/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.10) set(HEX_ALPHABET "0123456789abcdef") diff --git a/cmake/CheckUncrustifyVersion.cmake b/cmake/CheckUncrustifyVersion.cmake new file mode 100644 index 0000000000..4812c24ace --- /dev/null +++ b/cmake/CheckUncrustifyVersion.cmake @@ -0,0 +1,13 @@ +if(UNCRUSTIFY_PRG) + execute_process(COMMAND uncrustify --version + OUTPUT_VARIABLE user_version + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX REPLACE "[A-Za-z_#-]" "" user_version ${user_version}) + + file(STRINGS ${CONFIG_FILE} required_version LIMIT_COUNT 1) + string(REGEX REPLACE "[A-Za-z_# -]" "" required_version ${required_version}) + + if(NOT user_version STREQUAL required_version) + message(FATAL_ERROR "Wrong uncrustify version! Required version is ${required_version} but found ${user_version}") + endif() +endif() diff --git a/cmake/Format.cmake b/cmake/Format.cmake new file mode 100644 index 0000000000..4115e66705 --- /dev/null +++ b/cmake/Format.cmake @@ -0,0 +1,67 @@ +# Returns a list of all files that has been changed in current branch compared +# to master branch. This includes unstaged, staged and committed files. +function(get_changed_files outvar) + set(default_branch master) + + execute_process( + COMMAND git branch --show-current + OUTPUT_VARIABLE current_branch + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process( + COMMAND git merge-base ${default_branch} ${current_branch} + OUTPUT_VARIABLE ancestor_commit + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Changed files that have been committed + execute_process( + COMMAND git diff --name-only ${ancestor_commit}...${current_branch} + OUTPUT_VARIABLE committed_files + OUTPUT_STRIP_TRAILING_WHITESPACE) + separate_arguments(committed_files NATIVE_COMMAND ${committed_files}) + + # Unstaged files + execute_process( + COMMAND git diff --name-only + OUTPUT_VARIABLE unstaged_files + OUTPUT_STRIP_TRAILING_WHITESPACE) + separate_arguments(unstaged_files NATIVE_COMMAND ${unstaged_files}) + + # Staged files + execute_process( + COMMAND git diff --cached --name-only + OUTPUT_VARIABLE staged_files + OUTPUT_STRIP_TRAILING_WHITESPACE) + separate_arguments(staged_files NATIVE_COMMAND ${staged_files}) + + set(files ${committed_files} ${unstaged_files} ${staged_files}) + list(REMOVE_DUPLICATES files) + + set(${outvar} "${files}" PARENT_SCOPE) +endfunction() + +get_changed_files(changed_files) + +if(LANG STREQUAL c) + list(FILTER changed_files INCLUDE REGEX "\\.[ch]$") + list(FILTER changed_files INCLUDE REGEX "^src/nvim/") + + if(changed_files) + if(FORMAT_PRG) + execute_process(COMMAND ${FORMAT_PRG} -c "src/uncrustify.cfg" --replace --no-backup ${changed_files}) + else() + message(STATUS "Uncrustify not found. Skip formatting C files.") + endif() + endif() +elseif(LANG STREQUAL lua) + list(FILTER changed_files INCLUDE REGEX "\\.lua$") + list(FILTER changed_files INCLUDE REGEX "^runtime/") + + if(changed_files) + if(FORMAT_PRG) + execute_process(COMMAND ${FORMAT_PRG} ${changed_files}) + else() + message(STATUS "Stylua not found. Skip formatting lua files.") + endif() + endif() +endif() diff --git a/cmake/GenerateVersion.cmake b/cmake/GenerateVersion.cmake index b9313f2498..6118d8cba7 100644 --- a/cmake/GenerateVersion.cmake +++ b/cmake/GenerateVersion.cmake @@ -1,48 +1,37 @@ -# Handle generating version from Git. -set(use_git_version 0) if(NVIM_VERSION_MEDIUM) message(STATUS "USING NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}") return() endif() -find_program(GIT_EXECUTABLE git) -if(NOT GIT_EXECUTABLE) - message(AUTHOR_WARNING "Skipping version-string generation (cannot find git)") - file(WRITE "${OUTPUT}" "") - return() -endif() +set(NVIM_VERSION_MEDIUM + "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}${NVIM_VERSION_PRERELEASE}") execute_process( - COMMAND git describe --first-parent --tags --always --dirty + COMMAND git describe --first-parent --dirty OUTPUT_VARIABLE GIT_TAG ERROR_VARIABLE ERR RESULT_VARIABLE RES ) -if("${RES}" EQUAL 1) - if(EXISTS ${OUTPUT}) - message(STATUS "Unable to extract version-string from git: keeping the last known version") - else() - # this will only be executed once since the file will get generated afterwards - message(AUTHOR_WARNING "Git tag extraction failed with: " "${ERR}") - file(WRITE "${OUTPUT}" "") - endif() +if(NOT RES EQUAL 0) + message(STATUS "Git tag extraction failed:\n" " ${GIT_TAG}${ERR}" ) + # This will only be executed once since the file will get generated afterwards. + message(STATUS "Using NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}") + file(WRITE "${OUTPUT}" "${NVIM_VERSION_STRING}") return() endif() string(STRIP "${GIT_TAG}" GIT_TAG) string(REGEX REPLACE "^v[0-9]+.[0-9]+.[0-9]+-" "" NVIM_VERSION_GIT "${GIT_TAG}") -set(NVIM_VERSION_MEDIUM - "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}-dev-${NVIM_VERSION_GIT}" -) +set(NVIM_VERSION_MEDIUM "${NVIM_VERSION_MEDIUM}-${NVIM_VERSION_GIT}") set(NVIM_VERSION_STRING "#define NVIM_VERSION_MEDIUM \"${NVIM_VERSION_MEDIUM}\"\n") -string(SHA1 CURRENT_VERSION_HASH "${NVIM_VERSION_STRING}") +string(SHA1 CURRENT_VERSION_HASH "${NVIM_VERSION_STRING}") if(EXISTS ${OUTPUT}) file(SHA1 "${OUTPUT}" NVIM_VERSION_HASH) endif() if(NOT "${NVIM_VERSION_HASH}" STREQUAL "${CURRENT_VERSION_HASH}") - message(STATUS "Updating NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}") + message(STATUS "Using NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}") file(WRITE "${OUTPUT}" "${NVIM_VERSION_STRING}") endif() diff --git a/runtime/autoload/bitbake.vim b/runtime/autoload/bitbake.vim new file mode 100644 index 0000000000..bb3fc5c0e2 --- /dev/null +++ b/runtime/autoload/bitbake.vim @@ -0,0 +1,95 @@ +" Support for bitbake indenting, see runtime/indent/bitbake.vim + +function s:is_bb_python_func_def(lnum) + let stack = synstack(a:lnum, 1) + if len(stack) == 0 + return 0 + endif + + return synIDattr(stack[0], "name") == "bbPyFuncDef" +endfunction + +function bitbake#Indent(lnum) + if !has('syntax_items') + return -1 + endif + + let stack = synstack(a:lnum, 1) + if len(stack) == 0 + return -1 + endif + + let name = synIDattr(stack[0], "name") + + " TODO: support different styles of indentation for assignments. For now, + " we only support like this: + " VAR = " \ + " value1 \ + " value2 \ + " " + " + " i.e. each value indented by shiftwidth(), with the final quote " completely unindented. + if name == "bbVarValue" + " Quote handling is tricky. kernel.bbclass has this line for instance: + " EXTRA_OEMAKE = " HOSTCC="${BUILD_CC} ${BUILD_CFLAGS} ${BUILD_LDFLAGS}" " HOSTCPP="${BUILD_CPP}"" + " Instead of trying to handle crazy cases like that, just assume that a + " double-quote on a line by itself (following an assignment) means the + " user is closing the assignment, and de-dent. + if getline(a:lnum) =~ '^\s*"$' + return 0 + endif + + let prevstack = synstack(a:lnum - 1, 1) + if len(prevstack) == 0 + return -1 + endif + + let prevname = synIDattr(prevstack[0], "name") + + " Only indent if there was actually a continuation character on + " the previous line, to avoid misleading indentation. + let prevlinelastchar = synIDattr(synID(a:lnum - 1, col([a:lnum - 1, "$"]) - 1, 1), "name") + let prev_continued = prevlinelastchar == "bbContinue" + + " Did the previous line introduce an assignment? + if index(["bbVarDef", "bbVarFlagDef"], prevname) != -1 + if prev_continued + return shiftwidth() + endif + endif + + if !prev_continued + return 0 + endif + + " Autoindent can take it from here + return -1 + endif + + if index(["bbPyDefRegion", "bbPyFuncRegion"], name) != -1 + let ret = python#GetIndent(a:lnum, function('s:is_bb_python_func_def')) + " Should normally always be indented by at least one shiftwidth; but allow + " return of -1 (defer to autoindent) or -2 (force indent to 0) + if ret == 0 + return shiftwidth() + elseif ret == -2 + return 0 + endif + return ret + endif + + " TODO: GetShIndent doesn't detect tasks prepended with 'fakeroot' + " Need to submit a patch upstream to Vim to provide an extension point. + " Unlike the Python indenter, the Sh indenter is way too large to copy and + " modify here. + if name == "bbShFuncRegion" + return GetShIndent() + endif + + " TODO: + " + heuristics for de-denting out of a bbPyDefRegion? e.g. when the user + " types an obvious BB keyword like addhandler or addtask, or starts + " writing a shell task. Maybe too hard to implement... + + return -1 +endfunction diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 9e30ae1f51..a2f485dd67 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -519,7 +519,7 @@ func dist#ft#FTinc() " headers so assume POV-Ray elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords setf pascal - elseif lines =~# '\<\%(require\|inherit\)\>' || lines =~# '\w\+ = ' + elseif lines =~# '\<\%(require\|inherit\)\>' || lines =~# '[A-Z][A-Za-z0-9_:${}]*\s\+\%(??\|[?:+]\)\?= ' setf bitbake else call dist#ft#FTasmsyntax() diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 5cda7cfd03..d104bcfd67 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -615,10 +615,10 @@ function! s:check_node() abort return endif - if !executable('node') || (!executable('npm') && !executable('yarn')) + if !executable('node') || (!executable('npm') && !executable('yarn') && !executable('pnpm')) call health#report_warn( - \ '`node` and `npm` (or `yarn`) must be in $PATH.', - \ ['Install Node.js and verify that `node` and `npm` (or `yarn`) commands work.']) + \ '`node` and `npm` (or `yarn`, `pnpm`) must be in $PATH.', + \ ['Install Node.js and verify that `node` and `npm` (or `yarn`, `pnpm`) commands work.']) return endif let node_v = get(split(s:system(['node', '-v']), "\n"), 0, '') @@ -634,15 +634,22 @@ function! s:check_node() abort let [host, err] = provider#node#Detect() if empty(host) - call health#report_warn('Missing "neovim" npm (or yarn) package.', + call health#report_warn('Missing "neovim" npm (or yarn, pnpm) package.', \ ['Run in shell: npm install -g neovim', \ 'Run in shell (if you use yarn): yarn global add neovim', + \ 'Run in shell (if you use pnpm): pnpm install -g neovim', \ 'You may disable this provider (and warning) by adding `let g:loaded_node_provider = 0` to your init.vim']) return endif call health#report_info('Nvim node.js host: '. host) - let manager = executable('npm') ? 'npm' : 'yarn' + let manager = 'npm' + if executable('yarn') + let manager = 'yarn' + elseif executable('pnpm') + let manager = 'pnpm' + endif + let latest_npm_cmd = has('win32') ? \ 'cmd /c '. manager .' info neovim --json' : \ manager .' info neovim --json' @@ -673,9 +680,10 @@ function! s:check_node() abort \ printf('Package "neovim" is out-of-date. Installed: %s, latest: %s', \ current_npm, latest_npm), \ ['Run in shell: npm install -g neovim', - \ 'Run in shell (if you use yarn): yarn global add neovim']) + \ 'Run in shell (if you use yarn): yarn global add neovim', + \ 'Run in shell (if you use pnpm): pnpm install -g neovim']) else - call health#report_ok('Latest "neovim" npm/yarn package is installed: '. current_npm) + call health#report_ok('Latest "neovim" npm/yarn/pnpm package is installed: '. current_npm) endif endfunction diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim index 5079c07d8c..45b1dd4fd7 100644 --- a/runtime/autoload/provider/node.vim +++ b/runtime/autoload/provider/node.vim @@ -82,6 +82,13 @@ function! provider#node#Detect() abort let yarn_opts.job_id = jobstart('yarn global dir', yarn_opts) endif + let pnpm_opts = {} + if executable('pnpm') + let pnpm_opts = deepcopy(s:NodeHandler) + let pnpm_opts.entry_point = '/neovim/bin/cli.js' + let pnpm_opts.job_id = jobstart('pnpm --loglevel silent root -g', pnpm_opts) + endif + " npm returns the directory faster, so let's check that first if !empty(npm_opts) let result = jobwait([npm_opts.job_id]) @@ -97,6 +104,13 @@ function! provider#node#Detect() abort endif endif + if !empty(pnpm_opts) + let result = jobwait([pnpm_opts.job_id]) + if result[0] == 0 && pnpm_opts.result != '' + return [pnpm_opts.result, ''] + endif + endif + return ['', 'failed to detect node'] endfunction diff --git a/runtime/autoload/python.vim b/runtime/autoload/python.vim new file mode 100644 index 0000000000..7e7bca6fb6 --- /dev/null +++ b/runtime/autoload/python.vim @@ -0,0 +1,228 @@ +" Support for Python indenting, see runtime/indent/python.vim + +let s:keepcpo= &cpo +set cpo&vim + +" See if the specified line is already user-dedented from the expected value. +function s:Dedented(lnum, expected) + return indent(a:lnum) <= a:expected - shiftwidth() +endfunction + +let s:maxoff = 50 " maximum number of lines to look backwards for () + +" Some other filetypes which embed Python have slightly different indent +" rules (e.g. bitbake). Those filetypes can pass an extra funcref to this +" function which is evaluated below. +function python#GetIndent(lnum, ...) + let ExtraFunc = a:0 > 0 ? a:1 : 0 + + " If this line is explicitly joined: If the previous line was also joined, + " line it up with that one, otherwise add two 'shiftwidth' + if getline(a:lnum - 1) =~ '\\$' + if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$' + return indent(a:lnum - 1) + endif + return indent(a:lnum - 1) + (exists("g:pyindent_continue") ? eval(g:pyindent_continue) : (shiftwidth() * 2)) + endif + + " If the start of the line is in a string don't change the indent. + if has('syntax_items') + \ && synIDattr(synID(a:lnum, 1, 1), "name") =~ "String$" + return -1 + endif + + " Search backwards for the previous non-empty line. + let plnum = prevnonblank(v:lnum - 1) + + if plnum == 0 + " This is the first non-empty line, use zero indent. + return 0 + endif + + call cursor(plnum, 1) + + " Identing inside parentheses can be very slow, regardless of the searchpair() + " timeout, so let the user disable this feature if he doesn't need it + let disable_parentheses_indenting = get(g:, "pyindent_disable_parentheses_indenting", 0) + + if disable_parentheses_indenting == 1 + let plindent = indent(plnum) + let plnumstart = plnum + else + " searchpair() can be slow sometimes, limit the time to 150 msec or what is + " put in g:pyindent_searchpair_timeout + let searchpair_stopline = 0 + let searchpair_timeout = get(g:, 'pyindent_searchpair_timeout', 150) + + " If the previous line is inside parenthesis, use the indent of the starting + " line. + " Trick: use the non-existing "dummy" variable to break out of the loop when + " going too far back. + let parlnum = searchpair('(\|{\|\[', '', ')\|}\|\]', 'nbW', + \ "line('.') < " . (plnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'", + \ searchpair_stopline, searchpair_timeout) + if parlnum > 0 + if a:0 > 0 && ExtraFunc(parlnum) + " We may have found the opening brace of a bitbake Python task, e.g. 'python do_task {' + " If so, ignore it here - it will be handled later. + let parlnum = 0 + let plindent = indent(plnum) + let plnumstart = plnum + else + let plindent = indent(parlnum) + let plnumstart = parlnum + endif + else + let plindent = indent(plnum) + let plnumstart = plnum + endif + + " When inside parenthesis: If at the first line below the parenthesis add + " two 'shiftwidth', otherwise same as previous line. + " i = (a + " + b + " + c) + call cursor(a:lnum, 1) + let p = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'", + \ searchpair_stopline, searchpair_timeout) + if p > 0 + if a:0 > 0 && ExtraFunc(p) + " Currently only used by bitbake + " Handle first non-empty line inside a bitbake Python task + if p == plnum + return shiftwidth() + endif + + " Handle the user actually trying to close a bitbake Python task + let line = getline(a:lnum) + if line =~ '^\s*}' + return -2 + endif + + " Otherwise ignore the brace + let p = 0 + else + if p == plnum + " When the start is inside parenthesis, only indent one 'shiftwidth'. + let pp = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'", + \ searchpair_stopline, searchpair_timeout) + if pp > 0 + return indent(plnum) + (exists("g:pyindent_nested_paren") ? eval(g:pyindent_nested_paren) : shiftwidth()) + endif + return indent(plnum) + (exists("g:pyindent_open_paren") ? eval(g:pyindent_open_paren) : (shiftwidth() * 2)) + endif + if plnumstart == p + return indent(plnum) + endif + return plindent + endif + endif + endif + + + " Get the line and remove a trailing comment. + " Use syntax highlighting attributes when possible. + let pline = getline(plnum) + let pline_len = strlen(pline) + if has('syntax_items') + " If the last character in the line is a comment, do a binary search for + " the start of the comment. synID() is slow, a linear search would take + " too long on a long line. + if synIDattr(synID(plnum, pline_len, 1), "name") =~ "\\(Comment\\|Todo\\)$" + let min = 1 + let max = pline_len + while min < max + let col = (min + max) / 2 + if synIDattr(synID(plnum, col, 1), "name") =~ "\\(Comment\\|Todo\\)$" + let max = col + else + let min = col + 1 + endif + endwhile + let pline = strpart(pline, 0, min - 1) + endif + else + let col = 0 + while col < pline_len + if pline[col] == '#' + let pline = strpart(pline, 0, col) + break + endif + let col = col + 1 + endwhile + endif + + " If the previous line ended with a colon, indent this line + if pline =~ ':\s*$' + return plindent + shiftwidth() + endif + + " If the previous line was a stop-execution statement... + if getline(plnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>' + " See if the user has already dedented + if s:Dedented(a:lnum, indent(plnum)) + " If so, trust the user + return -1 + endif + " If not, recommend one dedent + return indent(plnum) - shiftwidth() + endif + + " If the current line begins with a keyword that lines up with "try" + if getline(a:lnum) =~ '^\s*\(except\|finally\)\>' + let lnum = a:lnum - 1 + while lnum >= 1 + if getline(lnum) =~ '^\s*\(try\|except\)\>' + let ind = indent(lnum) + if ind >= indent(a:lnum) + return -1 " indent is already less than this + endif + return ind " line up with previous try or except + endif + let lnum = lnum - 1 + endwhile + return -1 " no matching "try"! + endif + + " If the current line begins with a header keyword, dedent + if getline(a:lnum) =~ '^\s*\(elif\|else\)\>' + + " Unless the previous line was a one-liner + if getline(plnumstart) =~ '^\s*\(for\|if\|elif\|try\)\>' + return plindent + endif + + " Or the user has already dedented + if s:Dedented(a:lnum, plindent) + return -1 + endif + + return plindent - shiftwidth() + endif + + " When after a () construct we probably want to go back to the start line. + " a = (b + " + c) + " here + if parlnum > 0 + " ...unless the user has already dedented + if s:Dedented(a:lnum, plindent) + return -1 + else + return plindent + endif + endif + + return -1 +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/colors/blue.vim b/runtime/colors/blue.vim index d072ce6058..20f87bede8 100644 --- a/runtime/colors/blue.vim +++ b/runtime/colors/blue.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sat 11 Jun 2022 11:16:14 MSK +" Last Updated: 2022-07-26 15:49:58 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'blue' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#870000', '#006400', '#878700', '#000087', '#870087', '#008787', '#bcbcbc', '#878787', '#d70000', '#00ff00', '#ffdf00', '#5fafff', '#d787d7', '#5fffff', '#ffffff'] diff --git a/runtime/colors/darkblue.vim b/runtime/colors/darkblue.vim index 970a8cb060..3d24c9235a 100644 --- a/runtime/colors/darkblue.vim +++ b/runtime/colors/darkblue.vim @@ -4,7 +4,7 @@ " Maintainer: Original author Bohdan Vlasyuk <bohdan@vstu.edu.ua> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sat 11 Jun 2022 14:37:41 MSK +" Last Updated: 2022-07-26 15:49:59 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'darkblue' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#8b0000', '#90f020', '#ffa500', '#00008b', '#8b008b', '#008b8b', '#c0c0c0', '#808080', '#ffa0a0', '#90f020', '#ffff60', '#0030ff', '#ff00ff', '#90fff0', '#ffffff'] diff --git a/runtime/colors/delek.vim b/runtime/colors/delek.vim index 6bc1d1f699..c15d96ef33 100644 --- a/runtime/colors/delek.vim +++ b/runtime/colors/delek.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer David Schweikert <david@schweikert.ch> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:50:30 2022 +" Last Updated: 2022-07-26 15:50:00 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=light hi clear let g:colors_name = 'delek' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#ffffff', '#0000ff', '#00cd00', '#cd00cd', '#008b8b', '#0000ff', '#ff1493', '#bcbcbc', '#ee0000', '#0000ff', '#00cd00', '#cd00cd', '#008b8b', '#0000ff', '#ff1493', '#000000'] diff --git a/runtime/colors/desert.vim b/runtime/colors/desert.vim index 6cc7c21ceb..93bc73edec 100644 --- a/runtime/colors/desert.vim +++ b/runtime/colors/desert.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Hans Fugal <hans@fugal.net> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:51:36 2022 +" Last Updated: 2022-07-26 15:50:01 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'desert' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#7f7f8c', '#cd5c5c', '#9acd32', '#bdb76b', '#75a0ff', '#eeee00', '#cd853f', '#666666', '#8a7f7f', '#ff0000', '#89fb98', '#f0e68c', '#6dceeb', '#ffde9b', '#ffa0a0', '#c2bfa5'] @@ -40,7 +40,7 @@ hi TabLineFill guifg=NONE guibg=#c2bfa5 gui=NONE cterm=NONE hi TabLineSel guifg=#333333 guibg=#f0e68c gui=NONE cterm=NONE hi ToolbarLine guifg=NONE guibg=#666666 gui=NONE cterm=NONE hi ToolbarButton guifg=#333333 guibg=#ffde9b gui=bold cterm=bold -hi NonText guifg=#6dceeb guibg=NONE gui=NONE cterm=NONE +hi NonText guifg=#6dceeb guibg=#4d4d4d gui=NONE cterm=NONE hi SpecialKey guifg=#9acd32 guibg=NONE gui=NONE cterm=NONE hi Folded guifg=#eeee00 guibg=#4d4d4d gui=NONE cterm=NONE hi Visual guifg=#f0e68c guibg=#6b8e24 gui=NONE cterm=NONE @@ -112,7 +112,7 @@ if s:t_Co >= 256 hi TabLineSel ctermfg=236 ctermbg=186 cterm=NONE hi ToolbarLine ctermfg=NONE ctermbg=241 cterm=NONE hi ToolbarButton ctermfg=236 ctermbg=222 cterm=bold - hi NonText ctermfg=81 ctermbg=NONE cterm=NONE + hi NonText ctermfg=81 ctermbg=239 cterm=NONE hi SpecialKey ctermfg=112 ctermbg=NONE cterm=NONE hi Folded ctermfg=226 ctermbg=239 cterm=NONE hi Visual ctermfg=186 ctermbg=64 cterm=NONE diff --git a/runtime/colors/elflord.vim b/runtime/colors/elflord.vim index 54a6afbd79..f6e66ab06e 100644 --- a/runtime/colors/elflord.vim +++ b/runtime/colors/elflord.vim @@ -3,7 +3,7 @@ " Maintainer: original maintainer Ron Aaron <ron@ronware.org> " Website: https://www.github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:48:00 2022 +" Last Updated: 2022-07-26 15:50:02 " Generated by Colortemplate v2.2.0 @@ -12,7 +12,7 @@ set background=dark hi clear let g:colors_name = 'elflord' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 hi! link Terminal Normal hi! link Boolean Constant diff --git a/runtime/colors/evening.vim b/runtime/colors/evening.vim index 777c37e3ae..bc39e87b9a 100644 --- a/runtime/colors/evening.vim +++ b/runtime/colors/evening.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:52:56 2022 +" Last Updated: 2022-07-26 15:50:03 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'evening' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#ffa500', '#2e8b57', '#ffff00', '#006faf', '#8b008b', '#008b8b', '#bebebe', '#4d4d4d', '#ff5f5f', '#00ff00', '#ffff60', '#0087ff', '#ff80ff', '#00ffff', '#ffffff'] diff --git a/runtime/colors/habamax.vim b/runtime/colors/habamax.vim new file mode 100644 index 0000000000..469d1846d6 --- /dev/null +++ b/runtime/colors/habamax.vim @@ -0,0 +1,519 @@ +" Name: habamax +" Description: Hubba hubba hubba. +" Author: Maxim Kim <habamax@gmail.com> +" Maintainer: Maxim Kim <habamax@gmail.com> +" Website: https://github.com/vim/colorschemes +" License: Same as Vim +" Last Updated: 2022-07-26 15:50:04 + +" Generated by Colortemplate v2.2.0 + +set background=dark + +hi clear +let g:colors_name = 'habamax' + +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 + +if (has('termguicolors') && &termguicolors) || has('gui_running') + let g:terminal_ansi_colors = ['#1c1c1c', '#d75f5f', '#87af87', '#afaf87', '#5f87af', '#af87af', '#5f8787', '#9e9e9e', '#767676', '#df875f', '#afd7af', '#dfdf87', '#87afd7', '#dfafdf', '#87afaf', '#bcbcbc'] +endif +hi! link Terminal Normal +hi! link StatuslineTerm Statusline +hi! link StatuslineTermNC StatuslineNC +hi! link javaScriptFunction Statement +hi! link javaScriptIdentifier Statement +hi! link sqlKeyword Statement +hi! link yamlBlockMappingKey Statement +hi! link rubyMacro Statement +hi! link rubyDefine Statement +hi! link vimVar Normal +hi! link vimOper Normal +hi! link vimSep Normal +hi! link vimParenSep Normal +hi! link vimCommentString Comment +hi! link gitCommitSummary Title +hi! link markdownUrl String +hi! link elixirOperator Statement +hi! link elixirKeyword Statement +hi! link elixirBlockDefinition Statement +hi! link elixirDefine Statement +hi! link elixirPrivateDefine Statement +hi! link elixirGuard Statement +hi! link elixirPrivateGuard Statement +hi! link elixirModuleDefine Statement +hi! link elixirProtocolDefine Statement +hi! link elixirImplDefine Statement +hi! link elixirRecordDefine Statement +hi! link elixirPrivateRecordDefine Statement +hi! link elixirMacroDefine Statement +hi! link elixirPrivateMacroDefine Statement +hi! link elixirDelegateDefine Statement +hi! link elixirOverridableDefine Statement +hi! link elixirExceptionDefine Statement +hi! link elixirCallbackDefine Statement +hi! link elixirStructDefine Statement +hi! link elixirExUnitMacro Statement +hi! link elixirInclude Statement +hi! link elixirAtom PreProc +hi! link elixirDocTest String +hi ALEErrorSign guifg=#d75f5f guibg=NONE gui=NONE cterm=NONE +hi ALEInfoSign guifg=#dfdf87 guibg=NONE gui=NONE cterm=NONE +hi ALEWarningSign guifg=#af87af guibg=NONE gui=NONE cterm=NONE +hi ALEError guifg=#1c1c1c guibg=#d75f5f gui=NONE cterm=NONE +hi ALEVirtualTextError guifg=#1c1c1c guibg=#d75f5f gui=NONE cterm=NONE +hi ALEWarning guifg=#1c1c1c guibg=#af87af gui=NONE cterm=NONE +hi ALEVirtualTextWarning guifg=#1c1c1c guibg=#af87af gui=NONE cterm=NONE +hi ALEInfo guifg=#dfdf87 guibg=NONE gui=NONE cterm=NONE +hi ALEVirtualTextInfo guifg=#dfdf87 guibg=NONE gui=NONE cterm=NONE +hi Normal guifg=#bcbcbc guibg=#1c1c1c gui=NONE cterm=NONE +hi Statusline guifg=#1c1c1c guibg=#9e9e9e gui=NONE cterm=NONE +hi StatuslineNC guifg=#1c1c1c guibg=#767676 gui=NONE cterm=NONE +hi VertSplit guifg=#767676 guibg=#767676 gui=NONE cterm=NONE +hi TabLine guifg=#1c1c1c guibg=#767676 gui=NONE cterm=NONE +hi TabLineFill guifg=#1c1c1c guibg=#767676 gui=NONE cterm=NONE +hi TabLineSel guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold +hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi ToolbarButton guifg=#9e9e9e guibg=#1c1c1c gui=bold,reverse cterm=bold,reverse +hi QuickFixLine guifg=#1c1c1c guibg=#5f87af gui=NONE cterm=NONE +hi CursorLineNr guifg=#ffaf5f guibg=NONE gui=bold cterm=bold +hi LineNr guifg=#585858 guibg=NONE gui=NONE cterm=NONE +hi LineNrAbove guifg=#585858 guibg=NONE gui=NONE cterm=NONE +hi LineNrBelow guifg=#585858 guibg=NONE gui=NONE cterm=NONE +hi NonText guifg=#585858 guibg=NONE gui=NONE cterm=NONE +hi EndOfBuffer guifg=#585858 guibg=NONE gui=NONE cterm=NONE +hi SpecialKey guifg=#585858 guibg=NONE gui=NONE cterm=NONE +hi FoldColumn guifg=#585858 guibg=NONE gui=NONE cterm=NONE +hi Visual guifg=#1c1c1c guibg=#87afaf gui=NONE cterm=NONE +hi VisualNOS guifg=#1c1c1c guibg=#5f8787 gui=NONE cterm=NONE +hi Pmenu guifg=NONE guibg=#262626 gui=NONE cterm=NONE +hi PmenuThumb guifg=NONE guibg=#767676 gui=NONE cterm=NONE +hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi PmenuSel guifg=#1c1c1c guibg=#afaf87 gui=NONE cterm=NONE +hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi Error guifg=#d75f5f guibg=#1c1c1c gui=reverse cterm=reverse +hi ErrorMsg guifg=#d75f5f guibg=#1c1c1c gui=reverse cterm=reverse +hi ModeMsg guifg=#1c1c1c guibg=#dfdf87 gui=NONE cterm=NONE +hi MoreMsg guifg=#87af87 guibg=NONE gui=NONE cterm=NONE +hi Question guifg=#afaf87 guibg=NONE gui=NONE cterm=NONE +hi WarningMsg guifg=#df875f guibg=NONE gui=NONE cterm=NONE +hi Todo guifg=#dfdf87 guibg=#1c1c1c gui=reverse cterm=reverse +hi MatchParen guifg=#5f8787 guibg=#1c1c1c gui=reverse cterm=reverse +hi Search guifg=#1c1c1c guibg=#87af87 gui=NONE cterm=NONE +hi IncSearch guifg=#1c1c1c guibg=#ffaf5f gui=NONE cterm=NONE +hi CurSearch guifg=#1c1c1c guibg=#afaf87 gui=NONE cterm=NONE +hi WildMenu guifg=#1c1c1c guibg=#dfdf87 gui=NONE cterm=NONE +hi debugPC guifg=#1c1c1c guibg=#5f87af gui=NONE cterm=NONE +hi debugBreakpoint guifg=#1c1c1c guibg=#df875f gui=NONE cterm=NONE +hi Cursor guifg=#1c1c1c guibg=#ffaf5f gui=NONE cterm=NONE +hi lCursor guifg=#1c1c1c guibg=#5fff00 gui=NONE cterm=NONE +hi CursorLine guifg=NONE guibg=#303030 gui=NONE cterm=NONE +hi CursorColumn guifg=NONE guibg=#303030 gui=NONE cterm=NONE +hi Folded guifg=#9e9e9e guibg=#262626 gui=NONE cterm=NONE +hi ColorColumn guifg=NONE guibg=#262626 gui=NONE cterm=NONE +hi SpellBad guifg=NONE guibg=NONE guisp=#d75f5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi SpellCap guifg=NONE guibg=NONE guisp=#5f87af gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi SpellLocal guifg=NONE guibg=NONE guisp=#87af87 gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi SpellRare guifg=NONE guibg=NONE guisp=#dfafdf gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi Comment guifg=#767676 guibg=NONE gui=NONE cterm=NONE +hi Constant guifg=#df875f guibg=NONE gui=NONE cterm=NONE +hi String guifg=#87af87 guibg=NONE gui=NONE cterm=NONE +hi Character guifg=#afd7af guibg=NONE gui=NONE cterm=NONE +hi Identifier guifg=#87afaf guibg=NONE gui=NONE cterm=NONE +hi Statement guifg=#af87af guibg=NONE gui=NONE cterm=NONE +hi PreProc guifg=#afaf87 guibg=NONE gui=NONE cterm=NONE +hi Type guifg=#87afd7 guibg=NONE gui=NONE cterm=NONE +hi Special guifg=#5f8787 guibg=NONE gui=NONE cterm=NONE +hi Underlined guifg=NONE guibg=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline +hi Title guifg=#dfdf87 guibg=NONE gui=bold cterm=bold +hi Directory guifg=#87afaf guibg=NONE gui=bold cterm=bold +hi Conceal guifg=#767676 guibg=NONE gui=NONE cterm=NONE +hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi Debug guifg=#5f8787 guibg=NONE gui=NONE cterm=NONE +hi DiffAdd guifg=#000000 guibg=#87af87 gui=NONE cterm=NONE +hi DiffDelete guifg=#af875f guibg=NONE gui=NONE cterm=NONE +hi diffAdded guifg=#87af87 guibg=NONE gui=NONE cterm=NONE +hi diffRemoved guifg=#d75f5f guibg=NONE gui=NONE cterm=NONE +hi diffSubname guifg=#af87af guibg=NONE gui=NONE cterm=NONE +hi DiffText guifg=#000000 guibg=#dfdfdf gui=NONE cterm=NONE +hi DiffChange guifg=#000000 guibg=#afafaf gui=NONE cterm=NONE + +if s:t_Co >= 256 + hi! link Terminal Normal + hi! link StatuslineTerm Statusline + hi! link StatuslineTermNC StatuslineNC + hi! link javaScriptFunction Statement + hi! link javaScriptIdentifier Statement + hi! link sqlKeyword Statement + hi! link yamlBlockMappingKey Statement + hi! link rubyMacro Statement + hi! link rubyDefine Statement + hi! link vimVar Normal + hi! link vimOper Normal + hi! link vimSep Normal + hi! link vimParenSep Normal + hi! link vimCommentString Comment + hi! link gitCommitSummary Title + hi! link markdownUrl String + hi! link elixirOperator Statement + hi! link elixirKeyword Statement + hi! link elixirBlockDefinition Statement + hi! link elixirDefine Statement + hi! link elixirPrivateDefine Statement + hi! link elixirGuard Statement + hi! link elixirPrivateGuard Statement + hi! link elixirModuleDefine Statement + hi! link elixirProtocolDefine Statement + hi! link elixirImplDefine Statement + hi! link elixirRecordDefine Statement + hi! link elixirPrivateRecordDefine Statement + hi! link elixirMacroDefine Statement + hi! link elixirPrivateMacroDefine Statement + hi! link elixirDelegateDefine Statement + hi! link elixirOverridableDefine Statement + hi! link elixirExceptionDefine Statement + hi! link elixirCallbackDefine Statement + hi! link elixirStructDefine Statement + hi! link elixirExUnitMacro Statement + hi! link elixirInclude Statement + hi! link elixirAtom PreProc + hi! link elixirDocTest String + hi ALEErrorSign ctermfg=167 ctermbg=NONE cterm=NONE + hi ALEInfoSign ctermfg=186 ctermbg=NONE cterm=NONE + hi ALEWarningSign ctermfg=139 ctermbg=NONE cterm=NONE + hi ALEError ctermfg=234 ctermbg=167 cterm=NONE + hi ALEVirtualTextError ctermfg=234 ctermbg=167 cterm=NONE + hi ALEWarning ctermfg=234 ctermbg=139 cterm=NONE + hi ALEVirtualTextWarning ctermfg=234 ctermbg=139 cterm=NONE + hi ALEInfo ctermfg=186 ctermbg=NONE cterm=NONE + hi ALEVirtualTextInfo ctermfg=186 ctermbg=NONE cterm=NONE + hi Normal ctermfg=250 ctermbg=234 cterm=NONE + hi Statusline ctermfg=234 ctermbg=247 cterm=NONE + hi StatuslineNC ctermfg=234 ctermbg=243 cterm=NONE + hi VertSplit ctermfg=243 ctermbg=243 cterm=NONE + hi TabLine ctermfg=234 ctermbg=243 cterm=NONE + hi TabLineFill ctermfg=234 ctermbg=243 cterm=NONE + hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarButton ctermfg=247 ctermbg=234 cterm=bold,reverse + hi QuickFixLine ctermfg=234 ctermbg=67 cterm=NONE + hi CursorLineNr ctermfg=215 ctermbg=NONE cterm=bold + hi LineNr ctermfg=240 ctermbg=NONE cterm=NONE + hi LineNrAbove ctermfg=240 ctermbg=NONE cterm=NONE + hi LineNrBelow ctermfg=240 ctermbg=NONE cterm=NONE + hi NonText ctermfg=240 ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=240 ctermbg=NONE cterm=NONE + hi SpecialKey ctermfg=240 ctermbg=NONE cterm=NONE + hi FoldColumn ctermfg=240 ctermbg=NONE cterm=NONE + hi Visual ctermfg=234 ctermbg=109 cterm=NONE + hi VisualNOS ctermfg=234 ctermbg=66 cterm=NONE + hi Pmenu ctermfg=NONE ctermbg=235 cterm=NONE + hi PmenuThumb ctermfg=NONE ctermbg=243 cterm=NONE + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE + hi PmenuSel ctermfg=234 ctermbg=144 cterm=NONE + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=167 ctermbg=234 cterm=reverse + hi ErrorMsg ctermfg=167 ctermbg=234 cterm=reverse + hi ModeMsg ctermfg=234 ctermbg=186 cterm=NONE + hi MoreMsg ctermfg=108 ctermbg=NONE cterm=NONE + hi Question ctermfg=144 ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=173 ctermbg=NONE cterm=NONE + hi Todo ctermfg=186 ctermbg=234 cterm=reverse + hi MatchParen ctermfg=66 ctermbg=234 cterm=reverse + hi Search ctermfg=234 ctermbg=108 cterm=NONE + hi IncSearch ctermfg=234 ctermbg=215 cterm=NONE + hi CurSearch ctermfg=234 ctermbg=144 cterm=NONE + hi WildMenu ctermfg=234 ctermbg=186 cterm=NONE + hi debugPC ctermfg=234 ctermbg=67 cterm=NONE + hi debugBreakpoint ctermfg=234 ctermbg=173 cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=236 cterm=NONE + hi CursorColumn ctermfg=NONE ctermbg=236 cterm=NONE + hi Folded ctermfg=247 ctermbg=235 cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=235 cterm=NONE + hi SpellBad ctermfg=167 ctermbg=NONE cterm=underline + hi SpellCap ctermfg=67 ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=108 ctermbg=NONE cterm=underline + hi SpellRare ctermfg=182 ctermbg=NONE cterm=underline + hi Comment ctermfg=243 ctermbg=NONE cterm=NONE + hi Constant ctermfg=173 ctermbg=NONE cterm=NONE + hi String ctermfg=108 ctermbg=NONE cterm=NONE + hi Character ctermfg=151 ctermbg=NONE cterm=NONE + hi Identifier ctermfg=109 ctermbg=NONE cterm=NONE + hi Statement ctermfg=139 ctermbg=NONE cterm=NONE + hi PreProc ctermfg=144 ctermbg=NONE cterm=NONE + hi Type ctermfg=110 ctermbg=NONE cterm=NONE + hi Special ctermfg=66 ctermbg=NONE cterm=NONE + hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline + hi Title ctermfg=186 ctermbg=NONE cterm=bold + hi Directory ctermfg=109 ctermbg=NONE cterm=bold + hi Conceal ctermfg=243 ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi Debug ctermfg=66 ctermbg=NONE cterm=NONE + hi DiffAdd ctermfg=16 ctermbg=108 cterm=NONE + hi DiffDelete ctermfg=137 ctermbg=NONE cterm=NONE + hi diffAdded ctermfg=108 ctermbg=NONE cterm=NONE + hi diffRemoved ctermfg=167 ctermbg=NONE cterm=NONE + hi diffSubname ctermfg=139 ctermbg=NONE cterm=NONE + hi DiffText ctermfg=16 ctermbg=254 cterm=NONE + hi DiffChange ctermfg=16 ctermbg=145 cterm=NONE + unlet s:t_Co + finish +endif + +if s:t_Co >= 16 + hi ALEErrorSign ctermfg=darkred ctermbg=NONE cterm=NONE + hi ALEInfoSign ctermfg=yellow ctermbg=NONE cterm=NONE + hi ALEWarningSign ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi ALEError ctermfg=black ctermbg=darkred cterm=NONE + hi ALEVirtualTextError ctermfg=black ctermbg=darkred cterm=NONE + hi ALEWarning ctermfg=black ctermbg=darkmagenta cterm=NONE + hi ALEVirtualTextWarning ctermfg=black ctermbg=darkmagenta cterm=NONE + hi ALEInfo ctermfg=yellow ctermbg=NONE cterm=NONE + hi ALEVirtualTextInfo ctermfg=yellow ctermbg=NONE cterm=NONE + hi Normal ctermfg=white ctermbg=black cterm=NONE + hi Statusline ctermfg=black ctermbg=gray cterm=NONE + hi StatuslineNC ctermfg=black ctermbg=darkgray cterm=NONE + hi VertSplit ctermfg=darkgray ctermbg=darkgray cterm=NONE + hi TabLine ctermfg=black ctermbg=darkgray cterm=NONE + hi TabLineFill ctermfg=black ctermbg=darkgray cterm=NONE + hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarButton ctermfg=gray ctermbg=black cterm=bold,reverse + hi QuickFixLine ctermfg=black ctermbg=blue cterm=NONE + hi CursorLineNr ctermfg=red ctermbg=NONE cterm=bold + hi LineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi LineNrAbove ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi LineNrBelow ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi NonText ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi SpecialKey ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi FoldColumn ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi Visual ctermfg=black ctermbg=cyan cterm=NONE + hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE + hi Pmenu ctermfg=NONE ctermbg=darkgrey cterm=NONE + hi PmenuThumb ctermfg=NONE ctermbg=darkgray cterm=NONE + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE + hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=darkred ctermbg=black cterm=reverse + hi ErrorMsg ctermfg=darkred ctermbg=black cterm=reverse + hi ModeMsg ctermfg=black ctermbg=yellow cterm=NONE + hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Question ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE + hi Todo ctermfg=yellow ctermbg=black cterm=reverse + hi MatchParen ctermfg=darkcyan ctermbg=black cterm=reverse + hi Search ctermfg=black ctermbg=darkgreen cterm=NONE + hi IncSearch ctermfg=black ctermbg=red cterm=NONE + hi CurSearch ctermfg=black ctermbg=darkyellow cterm=NONE + hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE + hi debugPC ctermfg=black ctermbg=blue cterm=NONE + hi debugBreakpoint ctermfg=black ctermbg=red cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi Folded ctermfg=black ctermbg=darkyellow cterm=NONE + hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi SpellBad ctermfg=darkred ctermbg=NONE cterm=underline + hi SpellCap ctermfg=blue ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=darkgreen ctermbg=NONE cterm=underline + hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline + hi Comment ctermfg=darkgray ctermbg=NONE cterm=NONE + hi Constant ctermfg=red ctermbg=NONE cterm=NONE + hi String ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Character ctermfg=green ctermbg=NONE cterm=NONE + hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE + hi Statement ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi Type ctermfg=blue ctermbg=NONE cterm=NONE + hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline + hi Title ctermfg=yellow ctermbg=NONE cterm=bold + hi Directory ctermfg=cyan ctermbg=NONE cterm=bold + hi Conceal ctermfg=darkgray ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi Debug ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi DiffAdd ctermfg=black ctermbg=darkgreen cterm=NONE + hi DiffDelete ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi diffAdded ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi diffRemoved ctermfg=darkred ctermbg=NONE cterm=NONE + hi diffSubname ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi DiffText ctermfg=black ctermbg=lightgrey cterm=NONE + hi DiffChange ctermfg=black ctermbg=darkgray cterm=NONE + unlet s:t_Co + finish +endif + +if s:t_Co >= 8 + hi Normal ctermfg=gray ctermbg=black cterm=NONE + hi Statusline ctermfg=gray ctermbg=black cterm=bold,reverse + hi StatuslineNC ctermfg=gray ctermbg=black cterm=reverse + hi VertSplit ctermfg=gray ctermbg=black cterm=reverse + hi TabLine ctermfg=black ctermbg=gray cterm=NONE + hi TabLineFill ctermfg=black ctermbg=gray cterm=NONE + hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarButton ctermfg=gray ctermbg=black cterm=bold,reverse + hi QuickFixLine ctermfg=black ctermbg=blue cterm=NONE + hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold + hi LineNr ctermfg=gray ctermbg=NONE cterm=bold + hi LineNrAbove ctermfg=gray ctermbg=NONE cterm=bold + hi LineNrBelow ctermfg=gray ctermbg=NONE cterm=bold + hi NonText ctermfg=gray ctermbg=NONE cterm=bold + hi EndOfBuffer ctermfg=gray ctermbg=NONE cterm=bold + hi SpecialKey ctermfg=gray ctermbg=NONE cterm=bold + hi FoldColumn ctermfg=gray ctermbg=NONE cterm=bold + hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=reverse + hi Pmenu ctermfg=black ctermbg=gray cterm=NONE + hi PmenuThumb ctermfg=gray ctermbg=black cterm=NONE + hi PmenuSbar ctermfg=NONE ctermbg=gray cterm=NONE + hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=darkred ctermbg=gray cterm=bold,reverse + hi ErrorMsg ctermfg=darkred ctermbg=gray cterm=bold,reverse + hi ModeMsg ctermfg=black ctermbg=darkyellow cterm=NONE + hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Question ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE + hi Todo ctermfg=darkyellow ctermbg=black cterm=reverse + hi MatchParen ctermfg=darkcyan ctermbg=black cterm=reverse + hi Search ctermfg=black ctermbg=darkgreen cterm=NONE + hi IncSearch ctermfg=black ctermbg=darkyellow cterm=NONE + hi CurSearch ctermfg=black ctermbg=darkyellow cterm=NONE + hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE + hi debugPC ctermfg=black ctermbg=blue cterm=NONE + hi debugBreakpoint ctermfg=black ctermbg=darkcyan cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi Folded ctermfg=black ctermbg=darkyellow cterm=NONE + hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi SpellBad ctermfg=darkred ctermbg=gray cterm=reverse + hi SpellCap ctermfg=blue ctermbg=gray cterm=reverse + hi SpellLocal ctermfg=darkgreen ctermbg=black cterm=reverse + hi SpellRare ctermfg=darkmagenta ctermbg=gray cterm=reverse + hi Comment ctermfg=gray ctermbg=NONE cterm=bold + hi Constant ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi String ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Character ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Identifier ctermfg=gray ctermbg=NONE cterm=NONE + hi Statement ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi Type ctermfg=blue ctermbg=NONE cterm=NONE + hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline + hi Title ctermfg=darkyellow ctermbg=NONE cterm=bold + hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold + hi Conceal ctermfg=gray ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi Debug ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi DiffAdd ctermfg=black ctermbg=darkgreen cterm=NONE + hi DiffDelete ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi diffAdded ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi diffRemoved ctermfg=darkred ctermbg=NONE cterm=NONE + hi diffSubname ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi DiffText ctermfg=white ctermbg=black cterm=bold,reverse + hi DiffChange ctermfg=black ctermbg=white cterm=NONE + unlet s:t_Co + finish +endif + +if s:t_Co >= 0 + hi Normal term=NONE + hi ColorColumn term=reverse + hi Conceal term=NONE + hi Cursor term=reverse + hi CursorColumn term=NONE + hi CursorLine term=underline + hi CursorLineNr term=bold + hi DiffAdd term=reverse + hi DiffChange term=NONE + hi DiffDelete term=reverse + hi DiffText term=reverse + hi Directory term=NONE + hi EndOfBuffer term=NONE + hi ErrorMsg term=bold,reverse + hi FoldColumn term=NONE + hi Folded term=NONE + hi IncSearch term=bold,reverse,underline + hi LineNr term=NONE + hi MatchParen term=bold,underline + hi ModeMsg term=bold + hi MoreMsg term=NONE + hi NonText term=NONE + hi Pmenu term=reverse + hi PmenuSbar term=reverse + hi PmenuSel term=bold + hi PmenuThumb term=NONE + hi Question term=standout + hi Search term=reverse + hi SignColumn term=reverse + hi SpecialKey term=bold + hi SpellBad term=underline + hi SpellCap term=underline + hi SpellLocal term=underline + hi SpellRare term=underline + hi StatusLine term=bold,reverse + hi StatusLineNC term=bold,underline + hi TabLine term=bold,underline + hi TabLineFill term=NONE + hi Terminal term=NONE + hi TabLineSel term=bold,reverse + hi Title term=NONE + hi VertSplit term=NONE + hi Visual term=reverse + hi VisualNOS term=NONE + hi WarningMsg term=standout + hi WildMenu term=bold + hi CursorIM term=NONE + hi ToolbarLine term=reverse + hi ToolbarButton term=bold,reverse + hi CurSearch term=reverse + hi CursorLineFold term=underline + hi CursorLineSign term=underline + hi Comment term=bold + hi Constant term=NONE + hi Error term=bold,reverse + hi Identifier term=NONE + hi Ignore term=NONE + hi PreProc term=NONE + hi Special term=NONE + hi Statement term=NONE + hi Todo term=bold,reverse + hi Type term=NONE + hi Underlined term=underline + unlet s:t_Co + finish +endif + +" Background: dark +" Color: color00 #1C1C1C 234 black +" Color: color08 #767676 243 darkgray +" Color: color01 #D75F5F 167 darkred +" Color: color09 #DF875F 173 red +" Color: color02 #87AF87 108 darkgreen +" Color: color10 #AFD7AF 151 green +" Color: color03 #AFAF87 144 darkyellow +" Color: color11 #DFDF87 186 yellow +" Color: color04 #5F87AF 67 blue +" Color: color12 #87AFD7 110 blue +" Color: color05 #AF87AF 139 darkmagenta +" Color: color13 #DFAFDF 182 magenta +" Color: color06 #5F8787 66 darkcyan +" Color: color14 #87AFAF 109 cyan +" Color: color07 #9E9E9E 247 gray +" Color: color15 #BCBCBC 250 white +" Color: colorLine #303030 236 darkgrey +" Color: colorB #262626 235 darkgrey +" Color: colorNonT #585858 240 darkgrey +" Color: colorC #FFAF5F 215 red +" Color: colorlC #5FFF00 ~ +" Color: colorV #1F3F5F 109 cyan +" Color: diffAdd #87AF87 108 darkgreen +" Color: diffDelete #af875f 137 darkyellow +" Color: diffChange #AFAFAF 145 darkgray +" Color: diffText #DFDFDF 254 lightgrey +" Color: black #000000 16 black +" Color: white #FFFFFF 231 white +" Term colors: color00 color01 color02 color03 color04 color05 color06 color07 +" Term colors: color08 color09 color10 color11 color12 color13 color14 color15 +" vim: et ts=2 sw=2 diff --git a/runtime/colors/industry.vim b/runtime/colors/industry.vim index 41bfe129e7..d6678b2bb2 100644 --- a/runtime/colors/industry.vim +++ b/runtime/colors/industry.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Shian Lee. " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:53:58 2022 +" Last Updated: 2022-07-26 15:50:05 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'industry' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#303030', '#870000', '#5fd75f', '#afaf00', '#87afff', '#af00af', '#00afaf', '#6c6c6c', '#444444', '#ff0000', '#00ff00', '#ffff00', '#005fff', '#ff00ff', '#00ffff', '#ffffff'] diff --git a/runtime/colors/koehler.vim b/runtime/colors/koehler.vim index ecbc854030..87f1893ad7 100644 --- a/runtime/colors/koehler.vim +++ b/runtime/colors/koehler.vim @@ -3,7 +3,7 @@ " Maintainer: original maintainer Ron Aaron <ron@ronware.org> " Website: https://www.github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sat 11 Jun 2022 11:24:58 MSK +" Last Updated: 2022-07-26 15:50:06 " Generated by Colortemplate v2.2.0 @@ -12,7 +12,7 @@ set background=dark hi clear let g:colors_name = 'koehler' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 hi! link Terminal Normal hi! link Boolean Constant diff --git a/runtime/colors/morning.vim b/runtime/colors/morning.vim index 8a76fdf928..d32f1026f0 100644 --- a/runtime/colors/morning.vim +++ b/runtime/colors/morning.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Bram Moolenaar <Bram@vim.org> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:55:30 2022 +" Last Updated: 2022-07-26 15:50:07 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=light hi clear let g:colors_name = 'morning' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#e4e4e4', '#a52a2a', '#ff00ff', '#6a0dad', '#008787', '#2e8b57', '#6a5acd', '#bcbcbc', '#0000ff', '#a52a2a', '#ff00ff', '#6a0dad', '#008787', '#2e8b57', '#6a5acd', '#000000'] diff --git a/runtime/colors/murphy.vim b/runtime/colors/murphy.vim index 1ba096ecec..e9f31c2c8b 100644 --- a/runtime/colors/murphy.vim +++ b/runtime/colors/murphy.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Ron Aaron <ron@ronware.org>. " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:56:21 2022 +" Last Updated: 2022-07-26 15:50:08 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'murphy' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#303030', '#ffa700', '#005f00', '#ffd7af', '#87afff', '#ffafaf', '#00afaf', '#bcbcbc', '#444444', '#ff0000', '#00875f', '#ffff00', '#005fff', '#ff00ff', '#00ffff', '#ffffff'] diff --git a/runtime/colors/pablo.vim b/runtime/colors/pablo.vim index 1dc086d820..ee689af25e 100644 --- a/runtime/colors/pablo.vim +++ b/runtime/colors/pablo.vim @@ -3,7 +3,7 @@ " Maintainer: Original maintainerRon Aaron <ron@ronware.org> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:57:11 2022 +" Last Updated: 2022-07-26 15:50:09 " Generated by Colortemplate v2.2.0 @@ -12,7 +12,7 @@ set background=dark hi clear let g:colors_name = 'pablo' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff'] diff --git a/runtime/colors/peachpuff.vim b/runtime/colors/peachpuff.vim index a540be2734..2a925b6592 100644 --- a/runtime/colors/peachpuff.vim +++ b/runtime/colors/peachpuff.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer David Ne\v{c}as (Yeti) <yeti@physics.muni.cz> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 10:58:17 2022 +" Last Updated: 2022-07-26 15:50:10 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=light hi clear let g:colors_name = 'peachpuff' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#ffdab9', '#a52a2a', '#c00058', '#cd00cd', '#008b8b', '#2e8b57', '#6a5acd', '#737373', '#406090', '#a52a2a', '#c00058', '#cd00cd', '#008b8b', '#2e8b57', '#6a5acd', '#000000'] diff --git a/runtime/colors/quiet.vim b/runtime/colors/quiet.vim new file mode 100644 index 0000000000..2ebe5e628e --- /dev/null +++ b/runtime/colors/quiet.vim @@ -0,0 +1,707 @@ +" Name: quiet +" Description: `monochrome`, but less ugly, with diffs, searches, a few other niceties, and both light and dark versions. +" Author: neutaaaaan <neutaaaaan-gh@protonmail.com> +" Maintainer: neutaaaaan <neutaaaaan-gh@protonmail.com> +" Website: https://github.com/vim/colorschemes +" License: Vim License (see `:help license`)` +" Last Updated: 2022-08-01 15:13:21 + +" Generated by Colortemplate v2.2.0 + +hi clear +let g:colors_name = 'quiet' + +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 + +hi! link StatusLineTerm StatusLine +hi! link StatusLineTermNC StatusLineNC +hi! link Boolean Constant +hi! link Character Constant +hi! link Conditional Statement +hi! link Define PreProc +hi! link Debug Special +hi! link Delimiter Special +hi! link Exception Statement +hi! link Float Constant +hi! link Function Identifier +hi! link Include PreProc +hi! link Keyword Statement +hi! link Label Statement +hi! link Macro PreProc +hi! link Number Constant +hi! link Operator Statement +hi! link PreCondit PreProc +hi! link Repeat Statement +hi! link SpecialChar Special +hi! link SpecialComment Special +hi! link StorageClass Type +hi! link String Constant +hi! link Structure Type +hi! link Tag Special +hi! link Typedef Type +hi! link lCursor Cursor +hi! link debugBreakpoint ModeMsg +hi! link debugPC CursorLine + +if &background ==# 'dark' + if (has('termguicolors') && &termguicolors) || has('gui_running') + let g:terminal_ansi_colors = ['#080808', '#d7005f', '#00af5f', '#d78700', '#0087d7', '#d787d7', '#00afaf', '#dadada', '#707070', '#ff005f', '#00d75f', '#ffaf00', '#5fafff', '#ff87ff', '#00d7d7', '#ffffff'] + endif + hi Normal guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi Terminal guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi ColorColumn guifg=NONE guibg=#1c1c1c gui=NONE cterm=NONE + hi Conceal guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn guifg=NONE guibg=#303030 gui=NONE cterm=NONE + hi CursorLine guifg=NONE guibg=#303030 gui=NONE cterm=NONE + hi CursorLineNr guifg=#dadada guibg=#303030 gui=NONE cterm=NONE + hi DiffAdd guifg=#00af00 guibg=#080808 gui=reverse cterm=reverse + hi DiffChange guifg=#87afd7 guibg=#080808 gui=reverse cterm=reverse + hi DiffDelete guifg=#d75f5f guibg=#080808 gui=reverse cterm=reverse + hi DiffText guifg=#d787d7 guibg=#080808 gui=reverse cterm=reverse + hi Directory guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi EndOfBuffer guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi ErrorMsg guifg=#dadada guibg=#080808 gui=reverse cterm=reverse + hi FoldColumn guifg=#707070 guibg=#080808 gui=NONE cterm=NONE + hi Folded guifg=#707070 guibg=#080808 gui=NONE cterm=NONE + hi IncSearch guifg=#ffaf00 guibg=#080808 gui=reverse cterm=reverse + hi LineNr guifg=#444444 guibg=#080808 gui=NONE cterm=NONE + hi MatchParen guifg=#ff00af guibg=#080808 gui=bold cterm=bold + hi ModeMsg guifg=#dadada guibg=#080808 gui=bold cterm=bold + hi MoreMsg guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi NonText guifg=#707070 guibg=NONE gui=NONE cterm=NONE + hi Pmenu guifg=#080808 guibg=#87afd7 gui=NONE cterm=NONE + hi PmenuSbar guifg=#dadada guibg=#707070 gui=NONE cterm=NONE + hi PmenuSel guifg=#080808 guibg=#d787d7 gui=NONE cterm=NONE + hi PmenuThumb guifg=#dadada guibg=#d787d7 gui=NONE cterm=NONE + hi Question guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi QuickFixLine guifg=#d787d7 guibg=#080808 gui=reverse cterm=reverse + hi Search guifg=#00afff guibg=#080808 gui=reverse cterm=reverse + hi SignColumn guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi SpecialKey guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi SpellBad guifg=#d7005f guibg=#080808 guisp=#d7005f gui=undercurl cterm=underline + hi SpellCap guifg=#0087d7 guibg=#080808 guisp=#0087d7 gui=undercurl cterm=underline + hi SpellLocal guifg=#d787d7 guibg=#080808 guisp=#d787d7 gui=undercurl cterm=underline + hi SpellRare guifg=#00afaf guibg=#080808 guisp=#00afaf gui=undercurl cterm=underline + hi StatusLine guifg=#080808 guibg=#dadada gui=bold cterm=bold + hi StatusLineNC guifg=#707070 guibg=#080808 gui=underline cterm=underline + hi TabLine guifg=#707070 guibg=#080808 gui=underline cterm=underline + hi TabLineFill guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi TabLineSel guifg=#080808 guibg=#dadada gui=bold cterm=bold + hi Title guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi VertSplit guifg=#707070 guibg=#080808 gui=NONE cterm=NONE + hi Visual guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS guifg=NONE guibg=#303030 gui=NONE cterm=NONE + hi WarningMsg guifg=#dadada guibg=#080808 gui=NONE cterm=NONE + hi WildMenu guifg=#00afff guibg=#080808 gui=bold cterm=bold + hi Comment guifg=#707070 guibg=#080808 gui=bold cterm=bold + hi Constant guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi Error guifg=#ff005f guibg=#080808 gui=bold,reverse cterm=bold,reverse + hi Identifier guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi Ignore guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi PreProc guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi Special guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi Statement guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi Todo guifg=#00ffaf guibg=NONE gui=bold,reverse cterm=bold,reverse + hi Type guifg=#dadada guibg=NONE gui=NONE cterm=NONE + hi Underlined guifg=#dadada guibg=NONE gui=underline cterm=underline + hi CursorIM guifg=#080808 guibg=#afff00 gui=NONE cterm=NONE + hi ToolbarLine guifg=NONE guibg=#080808 gui=NONE cterm=NONE + hi ToolbarButton guifg=#dadada guibg=#080808 gui=bold cterm=bold +else + " Light background + if (has('termguicolors') && &termguicolors) || has('gui_running') + let g:terminal_ansi_colors = ['#080808', '#af0000', '#005f00', '#af5f00', '#005faf', '#870087', '#008787', '#d7d7d7', '#626262', '#d70000', '#008700', '#d78700', '#0087d7', '#af00af', '#00afaf', '#ffffff'] + endif + hi Normal guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi Terminal guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi ColorColumn guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE + hi Conceal guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE + hi CursorLine guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE + hi CursorLineNr guifg=#080808 guibg=#eeeeee gui=NONE cterm=NONE + hi DiffAdd guifg=#87d787 guibg=#080808 gui=reverse cterm=reverse + hi DiffChange guifg=#afafd7 guibg=#080808 gui=reverse cterm=reverse + hi DiffDelete guifg=#d78787 guibg=#080808 gui=reverse cterm=reverse + hi DiffText guifg=#d787d7 guibg=#080808 gui=reverse cterm=reverse + hi Directory guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi EndOfBuffer guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi ErrorMsg guifg=#080808 guibg=#d7d7d7 gui=reverse cterm=reverse + hi FoldColumn guifg=#626262 guibg=#d7d7d7 gui=NONE cterm=NONE + hi Folded guifg=#626262 guibg=#d7d7d7 gui=NONE cterm=NONE + hi IncSearch guifg=#ffaf00 guibg=#080808 gui=reverse cterm=reverse + hi LineNr guifg=#a8a8a8 guibg=#d7d7d7 gui=NONE cterm=NONE + hi MatchParen guifg=#ff00af guibg=#d7d7d7 gui=bold cterm=bold + hi ModeMsg guifg=#080808 guibg=#d7d7d7 gui=bold cterm=bold + hi MoreMsg guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi NonText guifg=#626262 guibg=NONE gui=NONE cterm=NONE + hi Pmenu guifg=#080808 guibg=#afafd7 gui=NONE cterm=NONE + hi PmenuSbar guifg=#080808 guibg=#626262 gui=NONE cterm=NONE + hi PmenuSel guifg=#080808 guibg=#d787d7 gui=NONE cterm=NONE + hi PmenuThumb guifg=#080808 guibg=#d787d7 gui=NONE cterm=NONE + hi Question guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi QuickFixLine guifg=#d787d7 guibg=#080808 gui=reverse cterm=reverse + hi Search guifg=#00afff guibg=#080808 gui=reverse cterm=reverse + hi SignColumn guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi SpecialKey guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi SpellBad guifg=#af0000 guibg=#d7d7d7 guisp=#af0000 gui=undercurl cterm=underline + hi SpellCap guifg=#005faf guibg=#d7d7d7 guisp=#005faf gui=undercurl cterm=underline + hi SpellLocal guifg=#870087 guibg=#d7d7d7 guisp=#870087 gui=undercurl cterm=underline + hi SpellRare guifg=#008787 guibg=#d7d7d7 guisp=#008787 gui=undercurl cterm=underline + hi StatusLine guifg=#eeeeee guibg=#080808 gui=bold cterm=bold + hi StatusLineNC guifg=#080808 guibg=#a8a8a8 gui=NONE cterm=NONE + hi TabLine guifg=#080808 guibg=#a8a8a8 gui=NONE cterm=NONE + hi TabLineFill guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi TabLineSel guifg=#eeeeee guibg=#080808 gui=bold cterm=bold + hi Title guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi VertSplit guifg=#626262 guibg=#d7d7d7 gui=NONE cterm=NONE + hi Visual guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE + hi WarningMsg guifg=#080808 guibg=#d7d7d7 gui=NONE cterm=NONE + hi WildMenu guifg=#080808 guibg=#eeeeee gui=bold cterm=bold + hi Comment guifg=#080808 guibg=#d7d7d7 gui=bold cterm=bold + hi Constant guifg=#080808 guibg=NONE gui=NONE cterm=NONE + hi Error guifg=#ff005f guibg=#080808 gui=bold,reverse cterm=bold,reverse + hi Identifier guifg=#080808 guibg=NONE gui=NONE cterm=NONE + hi Ignore guifg=#080808 guibg=NONE gui=NONE cterm=NONE + hi PreProc guifg=#080808 guibg=NONE gui=NONE cterm=NONE + hi Special guifg=#080808 guibg=NONE gui=NONE cterm=NONE + hi Statement guifg=#080808 guibg=NONE gui=NONE cterm=NONE + hi Todo guifg=#00ffaf guibg=#080808 gui=bold,reverse cterm=bold,reverse + hi Type guifg=#080808 guibg=NONE gui=NONE cterm=NONE + hi Underlined guifg=#080808 guibg=NONE gui=underline cterm=underline + hi CursorIM guifg=#080808 guibg=#afff00 gui=NONE cterm=NONE + hi ToolbarLine guifg=NONE guibg=#d7d7d7 gui=NONE cterm=NONE + hi ToolbarButton guifg=#080808 guibg=#d7d7d7 gui=bold cterm=bold +endif + +if s:t_Co >= 256 + if &background ==# 'dark' + hi Normal ctermfg=253 ctermbg=232 cterm=NONE + hi Terminal ctermfg=253 ctermbg=232 cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=234 cterm=NONE + hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn ctermfg=NONE ctermbg=236 cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=236 cterm=NONE + hi CursorLineNr ctermfg=253 ctermbg=236 cterm=NONE + hi DiffAdd ctermfg=34 ctermbg=232 cterm=reverse + hi DiffChange ctermfg=110 ctermbg=232 cterm=reverse + hi DiffDelete ctermfg=167 ctermbg=232 cterm=reverse + hi DiffText ctermfg=176 ctermbg=232 cterm=reverse + hi Directory ctermfg=253 ctermbg=232 cterm=NONE + hi EndOfBuffer ctermfg=253 ctermbg=232 cterm=NONE + hi ErrorMsg ctermfg=253 ctermbg=232 cterm=reverse + hi FoldColumn ctermfg=242 ctermbg=232 cterm=NONE + hi Folded ctermfg=242 ctermbg=232 cterm=NONE + hi IncSearch ctermfg=214 ctermbg=232 cterm=reverse + hi LineNr ctermfg=238 ctermbg=232 cterm=NONE + hi MatchParen ctermfg=199 ctermbg=232 cterm=bold + hi ModeMsg ctermfg=253 ctermbg=232 cterm=bold + hi MoreMsg ctermfg=253 ctermbg=232 cterm=NONE + hi NonText ctermfg=242 ctermbg=NONE cterm=NONE + hi Pmenu ctermfg=232 ctermbg=110 cterm=NONE + hi PmenuSbar ctermfg=253 ctermbg=242 cterm=NONE + hi PmenuSel ctermfg=232 ctermbg=176 cterm=NONE + hi PmenuThumb ctermfg=253 ctermbg=176 cterm=NONE + hi Question ctermfg=253 ctermbg=232 cterm=NONE + hi QuickFixLine ctermfg=176 ctermbg=232 cterm=reverse + hi Search ctermfg=39 ctermbg=232 cterm=reverse + hi SignColumn ctermfg=253 ctermbg=232 cterm=NONE + hi SpecialKey ctermfg=253 ctermbg=232 cterm=NONE + hi SpellBad ctermfg=161 ctermbg=232 cterm=underline + hi SpellCap ctermfg=32 ctermbg=232 cterm=underline + hi SpellLocal ctermfg=176 ctermbg=232 cterm=underline + hi SpellRare ctermfg=37 ctermbg=232 cterm=underline + hi StatusLine ctermfg=232 ctermbg=253 cterm=bold + hi StatusLineNC ctermfg=242 ctermbg=232 cterm=underline + hi TabLine ctermfg=242 ctermbg=232 cterm=underline + hi TabLineFill ctermfg=253 ctermbg=NONE cterm=NONE + hi TabLineSel ctermfg=232 ctermbg=253 cterm=bold + hi Title ctermfg=253 ctermbg=232 cterm=NONE + hi VertSplit ctermfg=242 ctermbg=232 cterm=NONE + hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS ctermfg=NONE ctermbg=236 cterm=NONE + hi WarningMsg ctermfg=253 ctermbg=232 cterm=NONE + hi WildMenu ctermfg=39 ctermbg=232 cterm=bold + hi Comment ctermfg=242 ctermbg=232 cterm=bold + hi Constant ctermfg=253 ctermbg=NONE cterm=NONE + hi Error ctermfg=197 ctermbg=232 cterm=bold,reverse + hi Identifier ctermfg=253 ctermbg=NONE cterm=NONE + hi Ignore ctermfg=253 ctermbg=NONE cterm=NONE + hi PreProc ctermfg=253 ctermbg=NONE cterm=NONE + hi Special ctermfg=253 ctermbg=NONE cterm=NONE + hi Statement ctermfg=253 ctermbg=NONE cterm=NONE + hi Todo ctermfg=49 ctermbg=NONE cterm=bold,reverse + hi Type ctermfg=253 ctermbg=NONE cterm=NONE + hi Underlined ctermfg=253 ctermbg=NONE cterm=underline + hi CursorIM ctermfg=232 ctermbg=154 cterm=NONE + hi ToolbarLine ctermfg=NONE ctermbg=232 cterm=NONE + hi ToolbarButton ctermfg=253 ctermbg=232 cterm=bold + else + " Light background + hi Normal ctermfg=232 ctermbg=188 cterm=NONE + hi Terminal ctermfg=232 ctermbg=188 cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=254 cterm=NONE + hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn ctermfg=NONE ctermbg=255 cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=255 cterm=NONE + hi CursorLineNr ctermfg=232 ctermbg=255 cterm=NONE + hi DiffAdd ctermfg=114 ctermbg=232 cterm=reverse + hi DiffChange ctermfg=146 ctermbg=232 cterm=reverse + hi DiffDelete ctermfg=174 ctermbg=232 cterm=reverse + hi DiffText ctermfg=176 ctermbg=232 cterm=reverse + hi Directory ctermfg=232 ctermbg=188 cterm=NONE + hi EndOfBuffer ctermfg=232 ctermbg=188 cterm=NONE + hi ErrorMsg ctermfg=232 ctermbg=188 cterm=reverse + hi FoldColumn ctermfg=241 ctermbg=188 cterm=NONE + hi Folded ctermfg=241 ctermbg=188 cterm=NONE + hi IncSearch ctermfg=214 ctermbg=232 cterm=reverse + hi LineNr ctermfg=248 ctermbg=188 cterm=NONE + hi MatchParen ctermfg=199 ctermbg=188 cterm=bold + hi ModeMsg ctermfg=232 ctermbg=188 cterm=bold + hi MoreMsg ctermfg=232 ctermbg=188 cterm=NONE + hi NonText ctermfg=241 ctermbg=NONE cterm=NONE + hi Pmenu ctermfg=232 ctermbg=146 cterm=NONE + hi PmenuSbar ctermfg=232 ctermbg=241 cterm=NONE + hi PmenuSel ctermfg=232 ctermbg=176 cterm=NONE + hi PmenuThumb ctermfg=232 ctermbg=176 cterm=NONE + hi Question ctermfg=232 ctermbg=188 cterm=NONE + hi QuickFixLine ctermfg=176 ctermbg=232 cterm=reverse + hi Search ctermfg=39 ctermbg=232 cterm=reverse + hi SignColumn ctermfg=232 ctermbg=188 cterm=NONE + hi SpecialKey ctermfg=232 ctermbg=188 cterm=NONE + hi SpellBad ctermfg=124 ctermbg=188 cterm=underline + hi SpellCap ctermfg=25 ctermbg=188 cterm=underline + hi SpellLocal ctermfg=90 ctermbg=188 cterm=underline + hi SpellRare ctermfg=30 ctermbg=188 cterm=underline + hi StatusLine ctermfg=255 ctermbg=232 cterm=bold + hi StatusLineNC ctermfg=232 ctermbg=248 cterm=NONE + hi TabLine ctermfg=232 ctermbg=248 cterm=NONE + hi TabLineFill ctermfg=232 ctermbg=188 cterm=NONE + hi TabLineSel ctermfg=255 ctermbg=232 cterm=bold + hi Title ctermfg=232 ctermbg=188 cterm=NONE + hi VertSplit ctermfg=241 ctermbg=188 cterm=NONE + hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS ctermfg=NONE ctermbg=255 cterm=NONE + hi WarningMsg ctermfg=232 ctermbg=188 cterm=NONE + hi WildMenu ctermfg=232 ctermbg=255 cterm=bold + hi Comment ctermfg=232 ctermbg=188 cterm=bold + hi Constant ctermfg=232 ctermbg=NONE cterm=NONE + hi Error ctermfg=197 ctermbg=232 cterm=bold,reverse + hi Identifier ctermfg=232 ctermbg=NONE cterm=NONE + hi Ignore ctermfg=232 ctermbg=NONE cterm=NONE + hi PreProc ctermfg=232 ctermbg=NONE cterm=NONE + hi Special ctermfg=232 ctermbg=NONE cterm=NONE + hi Statement ctermfg=232 ctermbg=NONE cterm=NONE + hi Todo ctermfg=49 ctermbg=232 cterm=bold,reverse + hi Type ctermfg=232 ctermbg=NONE cterm=NONE + hi Underlined ctermfg=232 ctermbg=NONE cterm=underline + hi CursorIM ctermfg=232 ctermbg=154 cterm=NONE + hi ToolbarLine ctermfg=NONE ctermbg=188 cterm=NONE + hi ToolbarButton ctermfg=232 ctermbg=188 cterm=bold + endif + unlet s:t_Co + finish +endif + +if s:t_Co >= 16 + if &background ==# 'dark' + hi Normal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Terminal ctermfg=NONE ctermbg=NONE cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold + hi DiffAdd ctermfg=2 ctermbg=0 cterm=reverse + hi DiffChange ctermfg=4 ctermbg=0 cterm=reverse + hi DiffDelete ctermfg=1 ctermbg=0 cterm=reverse + hi DiffText ctermfg=5 ctermbg=0 cterm=reverse + hi Directory ctermfg=NONE ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=NONE ctermbg=NONE cterm=NONE + hi ErrorMsg ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Folded ctermfg=NONE ctermbg=NONE cterm=NONE + hi IncSearch ctermfg=3 ctermbg=0 cterm=bold,reverse,underline + hi LineNr ctermfg=NONE ctermbg=NONE cterm=NONE + hi MatchParen ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold + hi MoreMsg ctermfg=NONE ctermbg=NONE cterm=NONE + hi NonText ctermfg=NONE ctermbg=NONE cterm=NONE + hi Pmenu ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSel ctermfg=NONE ctermbg=NONE cterm=bold + hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=NONE + hi Question ctermfg=NONE ctermbg=NONE cterm=standout + hi QuickFixLine ctermfg=5 ctermbg=0 cterm=reverse + hi Search ctermfg=6 ctermbg=0 cterm=reverse + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi SpecialKey ctermfg=NONE ctermbg=NONE cterm=bold + hi SpellBad ctermfg=1 ctermbg=NONE cterm=underline + hi SpellCap ctermfg=4 ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=5 ctermbg=NONE cterm=underline + hi SpellRare ctermfg=6 ctermbg=NONE cterm=underline + hi StatusLine ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi StatusLineNC ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLine ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=NONE + hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Title ctermfg=NONE ctermbg=NONE cterm=NONE + hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE + hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout + hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold + hi Comment ctermfg=NONE ctermbg=NONE cterm=bold + hi Constant ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi PreProc ctermfg=NONE ctermbg=NONE cterm=NONE + hi Special ctermfg=NONE ctermbg=NONE cterm=NONE + hi Statement ctermfg=NONE ctermbg=NONE cterm=NONE + hi Todo ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Type ctermfg=NONE ctermbg=NONE cterm=NONE + hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorIM ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=reverse + hi ToolbarButton ctermfg=NONE ctermbg=NONE cterm=bold,reverse + else + " Light background + hi Normal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Terminal ctermfg=NONE ctermbg=NONE cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold + hi DiffAdd ctermfg=2 ctermbg=0 cterm=reverse + hi DiffChange ctermfg=4 ctermbg=0 cterm=reverse + hi DiffDelete ctermfg=1 ctermbg=0 cterm=reverse + hi DiffText ctermfg=5 ctermbg=0 cterm=reverse + hi Directory ctermfg=NONE ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=NONE ctermbg=NONE cterm=NONE + hi ErrorMsg ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Folded ctermfg=NONE ctermbg=NONE cterm=NONE + hi IncSearch ctermfg=3 ctermbg=0 cterm=bold,reverse,underline + hi LineNr ctermfg=NONE ctermbg=NONE cterm=NONE + hi MatchParen ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold + hi MoreMsg ctermfg=NONE ctermbg=NONE cterm=NONE + hi NonText ctermfg=NONE ctermbg=NONE cterm=NONE + hi Pmenu ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSel ctermfg=NONE ctermbg=NONE cterm=bold + hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=NONE + hi Question ctermfg=NONE ctermbg=NONE cterm=standout + hi QuickFixLine ctermfg=5 ctermbg=0 cterm=reverse + hi Search ctermfg=6 ctermbg=0 cterm=reverse + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi SpecialKey ctermfg=NONE ctermbg=NONE cterm=bold + hi SpellBad ctermfg=1 ctermbg=NONE cterm=underline + hi SpellCap ctermfg=4 ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=5 ctermbg=NONE cterm=underline + hi SpellRare ctermfg=6 ctermbg=NONE cterm=underline + hi StatusLine ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi StatusLineNC ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLine ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=NONE + hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Title ctermfg=NONE ctermbg=NONE cterm=NONE + hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE + hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout + hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold + hi Comment ctermfg=NONE ctermbg=NONE cterm=bold + hi Constant ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi PreProc ctermfg=NONE ctermbg=NONE cterm=NONE + hi Special ctermfg=NONE ctermbg=NONE cterm=NONE + hi Statement ctermfg=NONE ctermbg=NONE cterm=NONE + hi Todo ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Type ctermfg=NONE ctermbg=NONE cterm=NONE + hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorIM ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=reverse + hi ToolbarButton ctermfg=NONE ctermbg=NONE cterm=bold,reverse + endif + unlet s:t_Co + finish +endif + +if s:t_Co >= 8 + if &background ==# 'dark' + hi Normal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Terminal ctermfg=NONE ctermbg=NONE cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold + hi DiffAdd ctermfg=2 ctermbg=0 cterm=reverse + hi DiffChange ctermfg=4 ctermbg=0 cterm=reverse + hi DiffDelete ctermfg=1 ctermbg=0 cterm=reverse + hi DiffText ctermfg=5 ctermbg=0 cterm=reverse + hi Directory ctermfg=NONE ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=NONE ctermbg=NONE cterm=NONE + hi ErrorMsg ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Folded ctermfg=NONE ctermbg=NONE cterm=NONE + hi IncSearch ctermfg=3 ctermbg=0 cterm=bold,reverse,underline + hi LineNr ctermfg=NONE ctermbg=NONE cterm=NONE + hi MatchParen ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold + hi MoreMsg ctermfg=NONE ctermbg=NONE cterm=NONE + hi NonText ctermfg=NONE ctermbg=NONE cterm=NONE + hi Pmenu ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSel ctermfg=NONE ctermbg=NONE cterm=bold + hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=NONE + hi Question ctermfg=NONE ctermbg=NONE cterm=standout + hi QuickFixLine ctermfg=5 ctermbg=0 cterm=reverse + hi Search ctermfg=6 ctermbg=0 cterm=reverse + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi SpecialKey ctermfg=NONE ctermbg=NONE cterm=bold + hi SpellBad ctermfg=1 ctermbg=NONE cterm=underline + hi SpellCap ctermfg=4 ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=5 ctermbg=NONE cterm=underline + hi SpellRare ctermfg=6 ctermbg=NONE cterm=underline + hi StatusLine ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi StatusLineNC ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLine ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=NONE + hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Title ctermfg=NONE ctermbg=NONE cterm=NONE + hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE + hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout + hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold + hi Comment ctermfg=NONE ctermbg=NONE cterm=bold + hi Constant ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi PreProc ctermfg=NONE ctermbg=NONE cterm=NONE + hi Special ctermfg=NONE ctermbg=NONE cterm=NONE + hi Statement ctermfg=NONE ctermbg=NONE cterm=NONE + hi Todo ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Type ctermfg=NONE ctermbg=NONE cterm=NONE + hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorIM ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=reverse + hi ToolbarButton ctermfg=NONE ctermbg=NONE cterm=bold,reverse + else + " Light background + hi Normal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Terminal ctermfg=NONE ctermbg=NONE cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE + hi Cursor ctermfg=NONE ctermbg=NONE cterm=reverse + hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold + hi DiffAdd ctermfg=2 ctermbg=0 cterm=reverse + hi DiffChange ctermfg=4 ctermbg=0 cterm=reverse + hi DiffDelete ctermfg=1 ctermbg=0 cterm=reverse + hi DiffText ctermfg=5 ctermbg=0 cterm=reverse + hi Directory ctermfg=NONE ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=NONE ctermbg=NONE cterm=NONE + hi ErrorMsg ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Folded ctermfg=NONE ctermbg=NONE cterm=NONE + hi IncSearch ctermfg=3 ctermbg=0 cterm=bold,reverse,underline + hi LineNr ctermfg=NONE ctermbg=NONE cterm=NONE + hi MatchParen ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold + hi MoreMsg ctermfg=NONE ctermbg=NONE cterm=NONE + hi NonText ctermfg=NONE ctermbg=NONE cterm=NONE + hi Pmenu ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSel ctermfg=NONE ctermbg=NONE cterm=bold + hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=NONE + hi Question ctermfg=NONE ctermbg=NONE cterm=standout + hi QuickFixLine ctermfg=5 ctermbg=0 cterm=reverse + hi Search ctermfg=6 ctermbg=0 cterm=reverse + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=reverse + hi SpecialKey ctermfg=NONE ctermbg=NONE cterm=bold + hi SpellBad ctermfg=1 ctermbg=NONE cterm=underline + hi SpellCap ctermfg=4 ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=5 ctermbg=NONE cterm=underline + hi SpellRare ctermfg=6 ctermbg=NONE cterm=underline + hi StatusLine ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi StatusLineNC ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLine ctermfg=NONE ctermbg=NONE cterm=bold,underline + hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=NONE + hi TabLineSel ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Title ctermfg=NONE ctermbg=NONE cterm=NONE + hi VertSplit ctermfg=NONE ctermbg=NONE cterm=NONE + hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse + hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=NONE ctermbg=NONE cterm=standout + hi WildMenu ctermfg=NONE ctermbg=NONE cterm=bold + hi Comment ctermfg=NONE ctermbg=NONE cterm=bold + hi Constant ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Identifier ctermfg=NONE ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi PreProc ctermfg=NONE ctermbg=NONE cterm=NONE + hi Special ctermfg=NONE ctermbg=NONE cterm=NONE + hi Statement ctermfg=NONE ctermbg=NONE cterm=NONE + hi Todo ctermfg=NONE ctermbg=NONE cterm=bold,reverse + hi Type ctermfg=NONE ctermbg=NONE cterm=NONE + hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorIM ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=reverse + hi ToolbarButton ctermfg=NONE ctermbg=NONE cterm=bold,reverse + endif + unlet s:t_Co + finish +endif + +if s:t_Co >= 0 + hi Normal term=NONE + hi ColorColumn term=reverse + hi Conceal term=NONE + hi Cursor term=reverse + hi CursorColumn term=NONE + hi CursorLine term=underline + hi CursorLineNr term=bold + hi DiffAdd term=reverse + hi DiffChange term=NONE + hi DiffDelete term=reverse + hi DiffText term=reverse + hi Directory term=NONE + hi EndOfBuffer term=NONE + hi ErrorMsg term=bold,reverse + hi FoldColumn term=NONE + hi Folded term=NONE + hi IncSearch term=bold,reverse,underline + hi LineNr term=NONE + hi MatchParen term=bold,underline + hi ModeMsg term=bold + hi MoreMsg term=NONE + hi NonText term=NONE + hi Pmenu term=reverse + hi PmenuSbar term=reverse + hi PmenuSel term=bold + hi PmenuThumb term=NONE + hi Question term=standout + hi Search term=reverse + hi SignColumn term=reverse + hi SpecialKey term=bold + hi SpellBad term=underline + hi SpellCap term=underline + hi SpellLocal term=underline + hi SpellRare term=underline + hi StatusLine term=bold,reverse + hi StatusLineNC term=bold,underline + hi TabLine term=bold,underline + hi TabLineFill term=NONE + hi Terminal term=NONE + hi TabLineSel term=bold,reverse + hi Title term=NONE + hi VertSplit term=NONE + hi Visual term=reverse + hi VisualNOS term=NONE + hi WarningMsg term=standout + hi WildMenu term=bold + hi CursorIM term=NONE + hi ToolbarLine term=reverse + hi ToolbarButton term=bold,reverse + hi CurSearch term=reverse + hi CursorLineFold term=underline + hi CursorLineSign term=underline + hi Comment term=bold + hi Constant term=NONE + hi Error term=bold,reverse + hi Identifier term=NONE + hi Ignore term=NONE + hi PreProc term=NONE + hi Special term=NONE + hi Statement term=NONE + hi Todo term=bold,reverse + hi Type term=NONE + hi Underlined term=underline + unlet s:t_Co + finish +endif + +" Background: dark +" Color: dark0 #080808 ~ 0 +" Color: dark1 #d7005f ~ 1 +" Color: dark2 #00af5f ~ 2 +" Color: dark3 #d78700 ~ 3 +" Color: dark4 #0087d7 ~ 4 +" Color: dark5 #d787d7 ~ 5 +" Color: dark6 #00afaf ~ 6 +" Color: dark7 #dadada ~ 7 +" Color: dark8 #707070 ~ 8 +" Color: dark9 #ff005f ~ 9 +" Color: dark10 #00d75f ~ 10 +" Color: dark11 #ffaf00 ~ 11 +" Color: dark12 #5fafff ~ 12 +" Color: dark13 #ff87ff ~ 13 +" Color: dark14 #00d7d7 ~ 14 +" Color: dark15 #ffffff ~ 15 +" Color: diffred #d75f5f ~ +" Color: diffgreen #00af00 ~ +" Color: diffblue #87afd7 ~ +" Color: diffpink #d787d7 ~ +" Color: uipink #ff00af ~ +" Color: uilime #afff00 ~ +" Color: uiteal #00ffaf ~ +" Color: uiblue #00afff ~ +" Color: uipurple #af00ff ~ +" Color: uiamber #ffaf00 ~ +" Color: uiblack #303030 ~ +" Color: yasogrey #1c1c1c ~ +" Color: linenrblack #444444 ~ +" Color: errorred #ff005f ~ +" Term colors: dark0 dark1 dark2 dark3 dark4 dark5 dark6 dark7 +" Term colors: dark8 dark9 dark10 dark11 dark12 dark13 dark14 dark15 +" Background: light +" Color: brightwhite #eeeeee ~ +" Color: light0 #080808 ~ 0 +" Color: light1 #af0000 ~ 1 +" Color: light2 #005f00 ~ 2 +" Color: light3 #af5f00 ~ 3 +" Color: light4 #005faf ~ 4 +" Color: light5 #870087 ~ 5 +" Color: light6 #008787 ~ 6 +" Color: light7 #d7d7d7 ~ 7 +" Color: light8 #626262 ~ 8 +" Color: light9 #d70000 ~ 9 +" Color: light10 #008700 ~ 10 +" Color: light11 #d78700 ~ 11 +" Color: light12 #0087d7 ~ 12 +" Color: light13 #af00af ~ 13 +" Color: light14 #00afaf ~ 14 +" Color: light15 #ffffff ~ 15 +" Color: diffred #d78787 ~ +" Color: diffgreen #87d787 ~ +" Color: diffblue #afafd7 ~ +" Color: diffpink #d787d7 ~ +" Color: uipink #ff00af ~ +" Color: uilime #afff00 ~ +" Color: uiteal #00ffaf ~ +" Color: uiblue #00afff ~ +" Color: uipurple #af00ff ~ +" Color: uiamber #ffaf00 ~ +" Color: invisigrey #a8a8a8 ~ +" Color: yasogrey #e4e4e4 ~ +" Color: errorred #ff005f ~ +" Term colors: light0 light1 light2 light3 light4 light5 light6 light7 +" Term colors: light8 light9 light10 light11 light12 light13 light14 light15 +" Background: any +" vim: et ts=2 sw=2 diff --git a/runtime/colors/ron.vim b/runtime/colors/ron.vim index 6aa810e54e..eb5c8f1770 100644 --- a/runtime/colors/ron.vim +++ b/runtime/colors/ron.vim @@ -3,7 +3,7 @@ " Maintainer: original maintainer Ron Aaron <ron@ronware.org> " Website: https://www.github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sat 11 Jun 2022 11:29:07 MSK +" Last Updated: 2022-07-26 15:50:11 " Generated by Colortemplate v2.2.0 @@ -12,7 +12,7 @@ set background=dark hi clear let g:colors_name = 'ron' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 hi! link Terminal Normal hi! link Boolean Constant diff --git a/runtime/colors/shine.vim b/runtime/colors/shine.vim index 5ae6d12111..de24a88136 100644 --- a/runtime/colors/shine.vim +++ b/runtime/colors/shine.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer is Yasuhiro Matsumoto <mattn@mail.goo.ne.jp> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 11:02:11 2022 +" Last Updated: 2022-07-26 15:50:12 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=light hi clear let g:colors_name = 'shine' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#8b0000', '#006400', '#ffff00', '#00008b', '#6a0dad', '#008b8b', '#dadada', '#767676', '#ffafaf', '#90ee90', '#ffff60', '#add8e6', '#ff00ff', '#00ffff', '#ffffff'] diff --git a/runtime/colors/slate.vim b/runtime/colors/slate.vim index 192d4162ee..63e7d0d857 100644 --- a/runtime/colors/slate.vim +++ b/runtime/colors/slate.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Ralph Amissah <ralph@amissah.com> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 11:03:10 2022 +" Last Updated: 2022-07-26 15:50:14 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'slate' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#ff0000', '#5f8700', '#ffff00', '#87d7ff', '#d7d787', '#ffd7af', '#666666', '#333333', '#ffafaf', '#00875f', '#ffd700', '#5f87d7', '#afaf87', '#ff8787', '#ffffff'] diff --git a/runtime/colors/torte.vim b/runtime/colors/torte.vim index bddd1c4e74..9a9124f3c1 100644 --- a/runtime/colors/torte.vim +++ b/runtime/colors/torte.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Thorsten Maerz <info@netztorte.de> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 11:04:48 2022 +" Last Updated: 2022-07-26 15:50:15 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=dark hi clear let g:colors_name = 'torte' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff'] diff --git a/runtime/colors/zellner.vim b/runtime/colors/zellner.vim index 6133538885..0d38cefc08 100644 --- a/runtime/colors/zellner.vim +++ b/runtime/colors/zellner.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Ron Aaron <ron@ronware.org> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Updated: Sun Jun 12 11:05:43 2022 +" Last Updated: 2022-07-26 15:50:16 " Generated by Colortemplate v2.2.0 @@ -13,7 +13,7 @@ set background=light hi clear let g:colors_name = 'zellner' -let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1 +let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co >= 0 ? &t_Co : -1 if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#ffffff', '#a52a2a', '#ff00ff', '#a020f0', '#0000ff', '#0000ff', '#ff00ff', '#a9a9a9', '#ff0000', '#a52a2a', '#ff00ff', '#a020f0', '#0000ff', '#0000ff', '#ff00ff', '#000000'] diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c67187d857..be42b7c14e 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -348,7 +348,7 @@ callbacks. These callbacks are called frequently in various contexts; |nvim_buf_attach()| will take keyword args for the callbacks. "on_lines" will receive parameters ("lines", {buf}, {changedtick}, {firstline}, {lastline}, -{new_lastline}, {old_byte_size}[, {old_utf32_size}, {old_utf16_size}]). +{new_lastline}, {old_byte_size} [, {old_utf32_size}, {old_utf16_size}]). Unlike remote channel events the text contents are not passed. The new text can be accessed inside the callback as @@ -536,12 +536,6 @@ created for extmark changes. ============================================================================== Global Functions *api-global* -nvim__get_hl_defs({ns_id}) *nvim__get_hl_defs()* - TODO: Documentation - -nvim__get_lib_dir() *nvim__get_lib_dir()* - TODO: Documentation - nvim__get_runtime({pat}, {all}, {*opts}) *nvim__get_runtime()* Find files in runtime directories @@ -608,15 +602,6 @@ nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* NB: if your UI doesn't use hlstate, this will not return hlstate first time. -nvim__runtime_inspect() *nvim__runtime_inspect()* - TODO: Documentation - -nvim__screenshot({path}) *nvim__screenshot()* - TODO: Documentation - - Attributes: ~ - |api-fast| - nvim__set_hl_ns({ns_id}) *nvim__set_hl_ns()* Set active namespace for highlights. @@ -638,12 +623,6 @@ nvim__stats() *nvim__stats()* Return: ~ Map of various internal stats. -nvim__unpack({str}) *nvim__unpack()* - TODO: Documentation - - Attributes: ~ - |api-fast| - nvim_call_atomic({calls}) *nvim_call_atomic()* Calls many API methods atomically. @@ -995,7 +974,7 @@ nvim_get_keymap({mode}) *nvim_get_keymap()* {mode} Mode short-name ("n", "i", "v", ...) Return: ~ - Array of maparg()-like dictionaries describing mappings. + Array of |maparg()|-like dictionaries describing mappings. The "buffer" key is always zero. nvim_get_mark({name}, {opts}) *nvim_get_mark()* @@ -1529,7 +1508,12 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()* used to give a description to the mapping. When called from Lua, also accepts a "callback" key that takes a Lua function to call when the mapping - is executed. + is executed. When "expr" is true, + "replace_keycodes" (boolean) can be used to + replace keycodes in the resulting string (see + |nvim_replace_termcodes()|), and a Lua callback + returning `nil` is equivalent to returning an + empty string. nvim_set_var({name}, {value}) *nvim_set_var()* Sets a global (g:) variable. @@ -1989,7 +1973,7 @@ nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()* Option value nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* - Sets a buffer option value. Passing 'nil' as value deletes the + Sets a buffer option value. Passing `nil` as value deletes the option (only works if there's a global fallback) Parameters: ~ @@ -2096,8 +2080,8 @@ nvim_win_get_option({window}, {name}) *nvim_win_get_option()* Option value nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()* - Sets a window option value. Passing 'nil' as value deletes the - option(only works if there's a global fallback) + Sets a window option value. Passing `nil` as value deletes the + option (only works if there's a global fallback) Parameters: ~ {window} Window handle, or 0 for current window @@ -2124,13 +2108,6 @@ affected. You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check whether a buffer is loaded. - *nvim__buf_redraw_range()* -nvim__buf_redraw_range({buffer}, {first}, {last}) - TODO: Documentation - -nvim__buf_stats({buffer}) *nvim__buf_stats()* - TODO: Documentation - nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* Activates buffer-update events on a channel, or as Lua callbacks. @@ -2325,7 +2302,7 @@ nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()* {buffer} Buffer handle, or 0 for current buffer Return: ~ - Array of maparg()-like dictionaries describing mappings. + Array of |maparg()|-like dictionaries describing mappings. The "buffer" key holds the associated buffer handle. *nvim_buf_get_lines()* diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index df5a636070..f0f47cddf4 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -295,6 +295,8 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) rhs of mapping {name} in mode {mode} mapcheck({name} [, {mode} [, {abbr}]]) String check for mappings matching {name} +mapset({mode}, {abbr}, {dict}) + none restore mapping from |maparg()| result match({expr}, {pat} [, {start} [, {count}]]) Number position where {pat} matches in {expr} matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]]) @@ -1885,7 +1887,9 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is To check for a supported command always check the return value to be 2. :2match The |:2match| command. - :3match The |:3match| command. + :3match The |:3match| command (but you + probably should not use it, it is + reserved for internal usage) #event autocommand defined for this event #event#pattern autocommand defined for this event and pattern (the pattern is taken @@ -4714,6 +4718,7 @@ map({expr1}, {expr2}) *map()* Can also be used as a |method|: > mylist->map(expr2) + maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* When {dict} is omitted or zero: Return the rhs of mapping {name} in mode {mode}. The returned String has special @@ -4747,7 +4752,10 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* When {dict} is there and it is |TRUE| return a dictionary containing all the information of the mapping with the following items: - "lhs" The {lhs} of the mapping. + "lhs" The {lhs} of the mapping as it would be typed + "lhsraw" The {lhs} of the mapping as raw bytes + "lhsrawalt" The {lhs} of the mapping as raw bytes, alternate + form, only present when it differs from "lhsraw" "rhs" The {rhs} of the mapping as typed. "silent" 1 for a |:map-silent| mapping, else 0. "noremap" 1 if the {rhs} of the mapping is not remappable. @@ -4766,6 +4774,9 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* "nowait" Do not wait for other, longer mappings. (|:map-<nowait>|). + The dictionary can be used to restore a mapping with + |mapset()|. + The mappings local to the current buffer are checked first, then the global mappings. This function can be used to map a key even when it's already @@ -4811,6 +4822,22 @@ mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()* Can also be used as a |method|: > GetKey()->mapcheck('n') +mapset({mode}, {abbr}, {dict}) *mapset()* + Restore a mapping from a dictionary returned by |maparg()|. + {mode} and {abbr} should be the same as for the call to + |maparg()|. *E460* + {mode} is used to define the mode in which the mapping is set, + not the "mode" entry in {dict}. + Example for saving and restoring a mapping: > + let save_map = maparg('K', 'n', 0, 1) + nnoremap K somethingelse + ... + call mapset('n', 0, save_map) +< Note that if you are going to replace a map in several modes, + e.g. with `:map!`, you need to save the mapping for all of + them, since they can differe. + + match({expr}, {pat} [, {start} [, {count}]]) *match()* When {expr} is a |List| then this returns the index of the first item where {pat} matches. Each item is used as a @@ -4905,8 +4932,10 @@ matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]]) 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 or -1, - |matchadd()| automatically chooses a free ID. + respectively. 3 is reserved for use by the |matchparen| + plugin. + If the {id} argument is not specified or -1, |matchadd()| + automatically chooses a free ID. The optional {dict} argument allows for further custom values. Currently this is used to specify a match specific @@ -5282,6 +5311,7 @@ mode([expr]) Return a string that indicates the current mode. niV Normal using |i_CTRL-O| in |Virtual-Replace-mode| nt Normal in |terminal-emulator| (insert goes to Terminal mode) + ntT Normal using |t_CTRL-\_CTRL-O| in |Terminal-mode| v Visual by character vs Visual by character using |v_CTRL-O| in Select mode V Visual by line @@ -6397,7 +6427,7 @@ searchcount([{options}]) *searchcount()* " to 1) let result = searchcount() < - The function is useful to add the count to |statusline|: > + The function is useful to add the count to 'statusline': > function! LastSearchCount() abort let result = searchcount(#{recompute: 0}) if empty(result) @@ -6670,7 +6700,6 @@ setbufline({buf}, {lnum}, {text}) *setbufline()* |bufload()| if needed. To insert lines use |appendbufline()|. - Any text properties in {lnum} are cleared. {text} can be a string to set one line, or a list of strings to set multiple lines. If the list extends below the last @@ -8008,10 +8037,10 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()* The result is a String, which is the {what} attribute of syntax ID {synID}. This can be used to obtain information about a syntax item. - {mode} can be "gui", "cterm" or "term", to get the attributes + {mode} can be "gui" or "cterm", to get the attributes for that mode. When {mode} is omitted, or an invalid value is used, the attributes for the currently active highlighting are - used (GUI, cterm or term). + used (GUI or cterm). Use synIDtrans() to follow linked highlight groups. {what} result "name" the name of the syntax item @@ -8036,14 +8065,15 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()* "underdouble" "1" if double underlined "underdotted" "1" if dotted underlined "underdashed" "1" if dashed underlined - "strikethrough" "1" if struckthrough + "strikethrough" "1" if struckthrough + "nocombine" "1" if nocombine + + Returns an empty string on error. Example (echoes the color of the syntax item under the cursor): > :echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg") < - Returns an empty string on error. - Can also be used as a |method|: > :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg") diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index b905f53db7..a4ff4474e6 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -765,6 +765,8 @@ When the {string} starts with "\=" it is evaluated as an expression, see |sub-replace-expression|. You can use that for complex replacement or special characters. +The substitution is limited in recursion to 4 levels. *E1290* + Otherwise these characters in {string} have a special meaning: magic nomagic action ~ diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 3f3f1e6ba3..5d82f5985b 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -240,7 +240,7 @@ CTRL-[ *c_CTRL-[* *c_<Esc>* *c_Esc* Note: If your <Esc> key is hard to hit on your keyboard, train yourself to use CTRL-[. *c_META* *c_ALT* - ALT (|META|) acts like <Esc> if the chord is not mapped. + ALT (|META|) may act like <Esc> if the chord is not mapped. For example <A-x> acts like <Esc>x if <A-x> does not have a command-line mode mapping. *c_CTRL-C* diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 2446506dec..7fb10f2a66 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -74,7 +74,7 @@ Functions that take a severity as an optional parameter (e.g. 2. A table with a "min" or "max" key (or both): > - vim.diagnostic.get(0, { severity = {min=vim.diagnostic.severity.WARN} }) + vim.diagnostic.get(0, { severity = { min = vim.diagnostic.severity.WARN } }) The latter form allows users to specify a range of severities. @@ -298,7 +298,7 @@ EVENTS *diagnostic-events* DiagnosticChanged After diagnostics have changed. Example: > - autocmd DiagnosticChanged * lua vim.diagnostic.setqflist({open = false }) + autocmd DiagnosticChanged * lua vim.diagnostic.setqflist({ open = false }) < ============================================================================== Lua module: vim.diagnostic *diagnostic-api* @@ -315,12 +315,12 @@ config({opts}, {namespace}) *vim.diagnostic.config()* For example, if a user enables virtual text globally with > - vim.diagnostic.config({virtual_text = true}) + vim.diagnostic.config({ virtual_text = true }) < and a diagnostic producer sets diagnostics with > - vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false}) + vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false }) < then virtual text will not be enabled for those diagnostics. @@ -570,8 +570,8 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults}) local s = "WARNING filename:27:3: Variable 'foo' does not exist" local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$" - local groups = {"severity", "lnum", "col", "message"} - vim.diagnostic.match(s, pattern, groups, {WARNING = vim.diagnostic.WARN}) + local groups = { "severity", "lnum", "col", "message" } + vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN }) < Parameters: ~ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 17af40bdb9..376adfec7f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1736,6 +1736,8 @@ v:completed_item Dictionary containing the most recent |complete-items| after |CompleteDone|. Empty if the completion failed, or after leaving and re-entering insert mode. + Note: Plugins can modify the value to emulate the builtin + |CompleteDone| event behavior. *v:count* *count-variable* v:count The count given for the last Normal mode command. Can be used diff --git a/runtime/doc/ft_sql.txt b/runtime/doc/ft_sql.txt index 6972fe0768..335faf266e 100644 --- a/runtime/doc/ft_sql.txt +++ b/runtime/doc/ft_sql.txt @@ -502,7 +502,7 @@ documentation. Assuming you have followed the dbext-tutorial you can press <C-C>t to display a list of tables. There is a delay while dbext is creating the table list. After the list is displayed press <C-W>. This will remove both the -popup window and the table name already chosen when the list became active. > +popup window and the table name already chosen when the list became active. 4.3.1 Table Completion: *sql-completion-tables* @@ -510,7 +510,7 @@ Press <C-C>t to display a list of tables from within the database you have connected via the dbext plugin. NOTE: All of the SQL completion popups support typing a prefix before pressing the key map. This will limit the contents of the popup window to just items -beginning with those characters. > +beginning with those characters. 4.3.2 Column Completion: *sql-completion-columns* @@ -583,13 +583,13 @@ popup a list of columns for the customer table. It does this by looking back to the beginning of the select statement and finding a list of the tables specified in the FROM clause. In this case it notes that in the string "customer c", "c" is an alias for the customer table. The optional "AS" -keyword is also supported, "customer AS c". > +keyword is also supported, "customer AS c". 4.3.3 Procedure Completion: *sql-completion-procedures* Similar to the table list, <C-C>p, will display a list of stored -procedures stored within the database. > +procedures stored within the database. 4.3.4 View Completion: *sql-completion-views* diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 25b98ae4ab..7d8a89887a 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1101,8 +1101,11 @@ tag command action in Command-line editing mode ~ 5. Terminal mode *terminal-mode-index* In a |terminal| buffer all keys except CTRL-\ are forwarded to the terminal -job. If CTRL-\ is pressed, the next key is forwarded unless it is CTRL-N. +job. If CTRL-\ is pressed, the next key is forwarded unless it is CTRL-N +or CTRL-O. Use |CTRL-\_CTRL-N| to go to Normal mode. +Use |t_CTRL-\_CTRL-O| to execute one normal mode command and then return +to terminal mode. You found it, Arthur! *holy-grail* diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index a16d88b4e9..6b0899334b 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -39,7 +39,7 @@ char action ~ abbreviation. Note: If your <Esc> key is hard to hit, try CTRL-[ instead. *i_META* *i_ALT* - ALT (|META|) acts like <Esc> if the chord is not mapped. + ALT (|META|) may act like <Esc> if the chord is not mapped. For example <A-x> acts like <Esc>x if <A-x> does not have an insert-mode mapping. *i_CTRL-C* @@ -374,10 +374,10 @@ CTRL-G CTRL-J cursor one line down, insert start column *i_CTRL-G_CTRL-J* <S-ScrollWheelRight> move window one page right *i_<S-ScrollWheelRight>* CTRL-O execute one command, return to Insert mode *i_CTRL-O* CTRL-\ CTRL-O like CTRL-O but don't move the cursor *i_CTRL-\_CTRL-O* -CTRL-G u break undo sequence, start new change *i_CTRL-G_u* -CTRL-G U don't break undo with next left/right cursor *i_CTRL-G_U* - movement, if the cursor stays within the - same the line +CTRL-G u close undo sequence, start new change *i_CTRL-G_u* +CTRL-G U don't start a new undo block with the next *i_CTRL-G_U* + left/right cursor movement, if the cursor + stays within the same line ----------------------------------------------------------------------- The CTRL-O command sometimes has a side effect: If the cursor was beyond the @@ -411,8 +411,8 @@ that, with CTRL-O u. Another example: > :inoremap <CR> <C-]><C-G>u<CR> -This breaks undo at each line break. It also expands abbreviations before -this. +This starts a new undo block at each line break. It also expands +abbreviations before this. An example for using CTRL-G U: > @@ -426,9 +426,9 @@ An example for using CTRL-G U: > inoremap <expr> <End> repeat('<C-G>U<Right>', col('$') - col('.')) inoremap ( ()<C-G>U<Left> -This makes it possible to use the cursor keys in Insert mode, without breaking -the undo sequence and therefore using |.| (redo) will work as expected. -Also entering a text like (with the "(" mapping from above): +This makes it possible to use the cursor keys in Insert mode, without starting +a new undo block and therefore using |.| (redo) will work as expected. Also +entering a text like (with the "(" mapping from above): Lorem ipsum (dolor diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 51e823b75f..ae80935032 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -383,8 +383,8 @@ Note: <k1>, ..., <k9> and <kPoint> will not work. - Nvim supports mapping multibyte chars with modifiers such as `<M-ä>`. Which combinations actually work depends on the the UI or host terminal. -- When a key is pressed using a meta or alt modifier and no mapping exists - for that keypress, Nvim behaves as though <Esc> was pressed before the key. +- When a key is pressed using a meta or alt modifier and no mapping exists for + that keypress, Nvim may behave as though <Esc> was pressed before the key. - It is possible to notate combined modifiers (e.g. <C-A-T> for CTRL-ALT-T), but your terminal must encode the input for that to work. |tui-input| @@ -458,7 +458,7 @@ Ex mode Like Command-line mode, but after entering a command Terminal mode In Terminal mode all input (except CTRL-\) is sent to the process running in the current |terminal| buffer. If CTRL-\ is pressed, the next key is sent unless it - is CTRL-N (|CTRL-\_CTRL-N|). + is CTRL-N (|CTRL-\_CTRL-N|) or CTRL-O (|t_CTRL-\_CTRL-O|). If the 'showmode' option is on "-- TERMINAL --" is shown at the bottom of the window. @@ -550,7 +550,8 @@ Ex :vi -- -- -- -- -- *6 Go from Select mode to Insert mode by typing a printable character. The selection is deleted and the character is inserted. - *CTRL-\_CTRL-N* *i_CTRL-\_CTRL-N* *c_CTRL-\_CTRL-N* *v_CTRL-\_CTRL-N* + *CTRL-\_CTRL-N* *i_CTRL-\_CTRL-N* *c_CTRL-\_CTRL-N* + *v_CTRL-\_CTRL-N* *t_CTRL-\_CTRL-N* Additionally the command CTRL-\ CTRL-N or <C-\><C-N> can be used to go to Normal mode from any other mode. This can be used to make sure Vim is in Normal mode, without causing a beep like <Esc> would. However, this does not diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 78100d5277..11f96db8c9 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -412,6 +412,31 @@ For the format of the response message, see: For the format of the notification message, see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#notificationMessage + *on-list-handler* + +`on_list` receives a table with: + + - `items` table[], structured like |setqflist-what| + - `title` string, title for the list. + - `context` table|nil. `ctx` from |lsp-handler| + +This table can be used with vim.fn.setqflist or vim.fn.setloclist. E.g.: + + local function on_list(options) + vim.fn.setqflist({}, ' ', options) + vim.api.nvim_command('cfirst') + end + + vim.lsp.buf.definition{on_list=on_list} + vim.lsp.buf.references(nil, {on_list=on_list}) + +If you prefer loclist do something like this: + + local function on_list(options) + vim.fn.setloclist(0, {}, ' ', options) + vim.api.nvim_command('lopen') + end + ================================================================================ LSP HIGHLIGHT *lsp-highlight* @@ -1071,7 +1096,7 @@ code_action({options}) *vim.lsp.buf.code_action()* Parameters: ~ {options} (table|nil) Optional table which holds the following optional fields: - • context (table|nil): Corresponds to `CodeActionContext` of the LSP specification: + • context: (table|nil) Corresponds to `CodeActionContext` of the LSP specification: • diagnostics (table|nil): LSP`Diagnostic[]` . Inferred from the current position if not provided. • only (table|nil): List of LSP @@ -1079,13 +1104,18 @@ code_action({options}) *vim.lsp.buf.code_action()* actions. Most language servers support values like `refactor` or `quickfix`. - • filter (function|nil): Predicate function - taking an `CodeAction` and returning a - boolean. - • apply (boolean|nil): When set to `true`, and + • filter: (function|nil) Predicate taking an + `CodeAction` and returning a boolean. + • apply: (boolean|nil) When set to `true`, and there is just one remaining action (after filtering), the action is applied without user query. + • range: (table|nil) Range for which code + actions should be requested. If in visual + mode this defaults to the active selection. + Table must contain `start` and `end` keys + with {row, col} tuples using mark-like + indexing. See |api-indexing| See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction @@ -1114,6 +1144,8 @@ declaration({options}) *vim.lsp.buf.declaration()* {options} (table|nil) additional options • reuse_win: (boolean) Jump to existing window if buffer is already open. + • on_list: (function) handler for list results. + See |on-list-handler| definition({options}) *vim.lsp.buf.definition()* Jumps to the definition of the symbol under the cursor. @@ -1122,6 +1154,8 @@ definition({options}) *vim.lsp.buf.definition()* {options} (table|nil) additional options • reuse_win: (boolean) Jump to existing window if buffer is already open. + • on_list: (function) handler for list results. + See |on-list-handler| document_highlight() *vim.lsp.buf.document_highlight()* Send request to the server to resolve document highlights for @@ -1139,10 +1173,15 @@ document_highlight() *vim.lsp.buf.document_highlight()* to see the actual highlights. |LspReferenceText| |LspReferenceRead| |LspReferenceWrite| -document_symbol() *vim.lsp.buf.document_symbol()* +document_symbol({options}) *vim.lsp.buf.document_symbol()* Lists all symbols in the current buffer in the quickfix window. + Parameters: ~ + {options} (table|nil) additional options + • on_list: (function) handler for list results. + See |on-list-handler| + execute_command({command_params}) *vim.lsp.buf.execute_command()* Executes an LSP server command. @@ -1163,32 +1202,33 @@ format({options}) *vim.lsp.buf.format()* • formatting_options (table|nil): Can be used to specify FormattingOptions. Some unspecified options will be automatically - derived from the current Neovim options. - - See also: ~ - https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting - • timeout_ms (integer|nil, default 1000): Time in - milliseconds to block for formatting requests. No effect - if async=true - • bufnr (number|nil): Restrict formatting to the clients - attached to the given buffer, defaults to the current - buffer (0). - • filter (function|nil): Predicate used to filter clients. - Receives a client as argument and must return a boolean. - Clients matching the predicate are included. Example: • > + derived from the current Neovim options. See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions + • timeout_ms (integer|nil, default 1000): Time + in milliseconds to block for formatting + requests. No effect if async=true + • bufnr (number|nil): Restrict formatting to + the clients attached to the given buffer, + defaults to the current buffer (0). + • filter (function|nil): Predicate used to + filter clients. Receives a client as argument + and must return a boolean. Clients matching + the predicate are included. Example: • > -- Never request typescript-language-server for formatting vim.lsp.buf.format { filter = function(client) return client.name ~= "tsserver" end } < - • async boolean|nil If true the method won't block. - Defaults to false. Editing the buffer while formatting - asynchronous can lead to unexpected changes. - • id (number|nil): Restrict formatting to the client with - ID (client.id) matching this field. - • name (string|nil): Restrict formatting to the client - with name (client.name) matching this field. + • async boolean|nil If true the method won't + block. Defaults to false. Editing the buffer + while formatting asynchronous can lead to + unexpected changes. + • id (number|nil): Restrict formatting to the + client with ID (client.id) matching this + field. + • name (string|nil): Restrict formatting to the + client with name (client.name) matching this + field. formatting({options}) *vim.lsp.buf.formatting()* Formats the current buffer. @@ -1251,10 +1291,15 @@ hover() *vim.lsp.buf.hover()* in a floating window. Calling the function twice will jump into the floating window. -implementation() *vim.lsp.buf.implementation()* +implementation({options}) *vim.lsp.buf.implementation()* Lists all the implementations for the symbol under the cursor in the quickfix window. + Parameters: ~ + {options} (table|nil) additional options + • on_list: (function) handler for list results. + See |on-list-handler| + incoming_calls() *vim.lsp.buf.incoming_calls()* Lists all the call sites of the symbol under the cursor in the |quickfix| window. If the symbol can resolve to multiple @@ -1300,12 +1345,15 @@ range_formatting({options}, {start_pos}, {end_pos}) position. Defaults to the end of the last visual selection. -references({context}) *vim.lsp.buf.references()* +references({context}, {options}) *vim.lsp.buf.references()* Lists all the references to the symbol under the cursor in the quickfix window. Parameters: ~ {context} (table) Context for the request + {options} (table|nil) additional options + • on_list: (function) handler for list results. + See |on-list-handler| See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references @@ -1351,8 +1399,10 @@ type_definition({options}) *vim.lsp.buf.type_definition()* {options} (table|nil) additional options • reuse_win: (boolean) Jump to existing window if buffer is already open. + • on_list: (function) handler for list results. + See |on-list-handler| -workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()* +workspace_symbol({query}, {options}) *vim.lsp.buf.workspace_symbol()* Lists all symbols in the current workspace in the quickfix window. @@ -1362,7 +1412,10 @@ workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()* done. Parameters: ~ - {query} (string, optional) + {query} (string, optional) + {options} (table|nil) additional options + • on_list: (function) handler for list results. + See |on-list-handler| ============================================================================== @@ -1559,7 +1612,7 @@ character_offset({buf}, {row}, {col}, {offset_encoding}) certain buffer. Parameters: ~ - {buf} buffer id (0 for current) + {buf} (number) buffer number (0 for current) {row} 0-indexed line {col} 0-indexed byte offset in line {offset_encoding} (string) utf-8|utf-16|utf-32|nil @@ -1721,17 +1774,17 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding}) that is similar to |vim.lsp.util.make_range_params()|. Parameters: ~ - {start_pos} ({number, number}, optional) - mark-indexed position. Defaults to the - start of the last visual selection. - {end_pos} ({number, number}, optional) - mark-indexed position. Defaults to the - end of the last visual selection. - {bufnr} (optional, number): buffer handle or 0 - for current, defaults to current - {offset_encoding} (string) utf-8|utf-16|utf-32|nil - defaults to `offset_encoding` of first - client of `bufnr` + {start_pos} number[]|nil {row, col} mark-indexed + position. Defaults to the start of the + last visual selection. + {end_pos} number[]|nil {row, col} mark-indexed + position. Defaults to the end of the + last visual selection. + {bufnr} (number|nil) buffer handle or 0 for + current, defaults to current + {offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults + to `offset_encoding` of first client of + `bufnr` Return: ~ { textDocument = { uri = `current_file_uri` }, range = { @@ -1743,8 +1796,8 @@ make_position_params({window}, {offset_encoding}) buffer and cursor position. Parameters: ~ - {window} (optional, number): window handle or 0 - for current, defaults to current + {window} number|nil: window handle or 0 for + current, defaults to current {offset_encoding} (string) utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` @@ -1764,11 +1817,11 @@ make_range_params({window}, {offset_encoding}) `textDocument/rangeFormatting`. Parameters: ~ - {window} (optional, number): window handle or 0 - for current, defaults to current - {offset_encoding} (string) utf-8|utf-16|utf-32|nil - defaults to `offset_encoding` of first - client of buffer of `window` + {window} number|nil: window handle or 0 for + current, defaults to current + {offset_encoding} "utf-8"|"utf-16"|"utf-32"|nil defaults + to `offset_encoding` of first client of + buffer of `window` Return: ~ { textDocument = { uri = `current_file_uri` }, range = { @@ -1780,8 +1833,7 @@ make_text_document_params({bufnr}) buffer. Parameters: ~ - {bufnr} (optional, number): Buffer handle, defaults to - current + {bufnr} number|nil: Buffer handle, defaults to current Return: ~ `TextDocumentIdentifier` diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 089cf0ce9d..4062a35735 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -704,7 +704,7 @@ regex:match_str({str}) *regex:match_str()* As any integer is truth-y, `regex:match()` can be directly used as a condition in an if-statement. -regex:match_line({bufnr}, {line_idx}[, {start}, {end}]) *regex:match_line()* +regex:match_line({bufnr}, {line_idx} [, {start}, {end}]) *regex:match_line()* Match line {line_idx} (zero-based) in buffer {bufnr}. If {start} and {end} are supplied, match only this byte index range. Otherwise see |regex:match_str()|. If {start} is used, then the returned byte @@ -855,13 +855,13 @@ vim.empty_dict() *vim.empty_dict()* Note: If numeric keys are present in the table, Nvim ignores the metatable marker and converts the dict to a list/array anyway. -vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* +vim.rpcnotify({channel}, {method} [, {args}...]) *vim.rpcnotify()* Sends {event} to {channel} via |RPC| and returns immediately. If {channel} is 0, the event is broadcast to all channels. This function also works in a fast callback |lua-loop-callbacks|. -vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()* +vim.rpcrequest({channel}, {method} [, {args}...]) *vim.rpcrequest()* Sends a request to {channel} to invoke {method} via |RPC| and blocks until a response is received. @@ -873,7 +873,7 @@ vim.stricmp({a}, {b}) *vim.stricmp()* are equal, {a} is greater than {b} or {a} is lesser than {b}, respectively. -vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()* +vim.str_utfindex({str} [, {index}]) *vim.str_utfindex()* Convert byte index to UTF-32 and UTF-16 indices. If {index} is not supplied, the length of the string is used. All indices are zero-based. Returns two values: the UTF-32 and UTF-16 indices respectively. @@ -883,7 +883,7 @@ vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()* point each. An {index} in the middle of a UTF-8 sequence is rounded upwards to the end of that sequence. -vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()* +vim.str_byteindex({str}, {index} [, {use_utf16}]) *vim.str_byteindex()* Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not supplied, it defaults to false (use UTF-32). Returns the byte index. @@ -1291,6 +1291,9 @@ Lua module: vim *lua-vim* cmd({command}) *vim.cmd()* Execute Vim script commands. + Note that `vim.cmd` can be indexed with a command name to + return a callable function to the command. + Example: > vim.cmd('echo 42') @@ -1300,7 +1303,23 @@ cmd({command}) *vim.cmd()* autocmd FileType c setlocal cindent augroup END ]]) - vim.cmd({ cmd = 'echo', args = { '"foo"' } }) + + -- Ex command :echo "foo" + -- Note string literals need to be double quoted. + vim.cmd('echo "foo"') + vim.cmd { cmd = 'echo', args = { '"foo"' } } + vim.cmd.echo({ args = { '"foo"' } }) + vim.cmd.echo('"foo"') + + -- Ex command :write! myfile.txt + vim.cmd('write! myfile.txt') + vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true } + vim.cmd.write { args = { "myfile.txt" }, bang = true } + vim.cmd.write { "myfile.txt", bang = true } + + -- Ex command :colorscheme blue + vim.cmd('colorscheme blue') + vim.cmd.colorscheme('blue') < Parameters: ~ @@ -2029,30 +2048,30 @@ add({filetypes}) *vim.filetype.add()* vim.filetype.add({ extension = { - foo = "fooscript", + foo = 'fooscript', bar = function(path, bufnr) if some_condition() then - return "barscript", function(bufnr) + return 'barscript', function(bufnr) -- Set a buffer variable vim.b[bufnr].barscript_version = 2 end end - return "bar" + return 'bar' end, }, filename = { - [".foorc"] = "toml", - ["/etc/foo/config"] = "toml", + ['.foorc'] = 'toml', + ['/etc/foo/config'] = 'toml', }, pattern = { - [".*‍/etc/foo/.*"] = "fooscript", + ['.*/etc/foo/.*'] = 'fooscript', -- Using an optional priority - [".*‍/etc/foo/.*%.conf"] = { "dosini", { priority = 10 } }, - ["README.(%a+)$"] = function(path, bufnr, ext) - if ext == "md" then - return "markdown" - elseif ext == "rst" then - return "rst" + ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } }, + ['README.(a+)$'] = function(path, bufnr, ext) + if ext == 'md' then + return 'markdown' + elseif ext == 'rst' then + return 'rst' end end, }, @@ -2068,9 +2087,9 @@ add({filetypes}) *vim.filetype.add()* priority = -math.huge, function(path, bufnr) local content = vim.filetype.getlines(bufnr, 1) - if vim.filetype.matchregex(content, { [[^#!.*\<mine\>]] }) then + if vim.filetype.matchregex(content, [[^#!.*\<mine\>]]) then return 'mine' - elseif vim.filetype.matchregex(content, { [[\<drawing\>]] }) then + elseif vim.filetype.matchregex(content, [[\<drawing\>]]) then return 'drawing' end end, @@ -2199,9 +2218,7 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* create mapping on multiple modes. {lhs} (string) Left-hand side |{lhs}| of the mapping. {rhs} string|function Right-hand side |{rhs}| of the - mapping. Can also be a Lua function. If a Lua - function and `opts.expr == true`, returning `nil` - is equivalent to an empty string. + mapping. Can also be a Lua function. {opts} (table) A table of |:map-arguments| such as "silent". In addition to the options listed in |nvim_set_keymap()|, this table also accepts the @@ -2209,13 +2226,11 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* • buffer: (number or boolean) Add a mapping to the given buffer. When "true" or 0, use the current buffer. - • replace_keycodes: (boolean, default true) When - both this and expr is "true", - |nvim_replace_termcodes()| is applied to the - result of Lua expr maps. • remap: (boolean) Make the mapping recursive. This is the inverse of the "noremap" option from |nvim_set_keymap()|. Default `false`. + • replace_keycodes: (boolean) defaults to true if + "expr" is true. See also: ~ |nvim_set_keymap()| diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 7e94167e07..1e1f4e46af 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -610,19 +610,20 @@ two bytes 0xc3 0xa1. You don't want the 0xc3 byte to be mapped then or otherwise it would be impossible to type the á character. *<Leader>* *mapleader* -To define a mapping which uses the "mapleader" variable, the special string -"<Leader>" can be used. It is replaced with the string value of "mapleader". -If "mapleader" is not set or empty, a backslash is used instead. Example: > - :map <Leader>A oanother line<Esc> +To define a mapping which uses the "g:mapleader" variable, the special string +"<Leader>" can be used. It is replaced with the string value of +"g:mapleader". If "g:mapleader" is not set or empty, a backslash is used +instead. Example: > + map <Leader>A oanother line<Esc> Works like: > - :map \A oanother line<Esc> -But after: > - :let mapleader = "," + map \A oanother line<Esc> +But after: + let mapleader = "," It works like: > - :map ,A oanother line<Esc> + map ,A oanother line<Esc> -Note that the value of "mapleader" is used at the moment the mapping is -defined. Changing "mapleader" after that has no effect for already defined +Note that the value of "g:mapleader" is used at the moment the mapping is +defined. Changing "g:mapleader" after that has no effect for already defined mappings. *<LocalLeader>* *maplocalleader* diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index a7be9ff98f..546f92e92f 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -47,8 +47,10 @@ Input *terminal-input* To send input, enter |Terminal-mode| with |i|, |I|, |a|, |A| or |:startinsert|. In this mode all keys except <C-\> are sent to the underlying -program. If <C-\> is pressed, the next key is sent unless it is <C-N>. Use -<C-\><C-N> to return to normal-mode. |CTRL-\_CTRL-N| +program. If <C-\> is pressed, the next key is sent unless it is <C-N> or <C-O>. +Use <C-\><C-N> to return to normal mode. |CTRL-\_CTRL-N| +Use <C-\><C-O> to execute one normal mode command and then return to terminal +mode. *t_CTRL-\_CTRL-O* Terminal-mode forces these local options: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index eda596bf71..279f0878f1 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1314,14 +1314,14 @@ A jump table for the options with a short description can be found at |Q_op|. *'cmdheight'* *'ch'* 'cmdheight' 'ch' number (default 1) - global + global or local to tab page Number of screen lines to use for the command-line. Helps avoiding |hit-enter| prompts. The value of this option is stored with the tab page, so that each tab page can have a different value. - When 'cmdheight' is zero, it disables echo area and all outputs need - |hit-enter| prompt. + When 'cmdheight' is zero, there is no command-line unless it is being + used. Any messages will cause the |hit-enter| prompt. *'cmdwinheight'* *'cwh'* 'cmdwinheight' 'cwh' number (default 7) @@ -3575,7 +3575,7 @@ A jump table for the options with a short description can be found at |Q_op|. help. (Note that previously setting the global option to the empty value did this, which is now deprecated.) When the first character is ":", the command is invoked as a Vim - command prefixed with [count]. + Ex command prefixed with [count]. When "man" or "man -s" is used, Vim will automatically translate a [count] for the "K" command to a section number. See |option-backslash| about including spaces and backslashes. @@ -4214,14 +4214,14 @@ A jump table for the options with a short description can be found at |Q_op|. The 'mousemodel' option is set by the |:behave| command. - *mousescroll* + *'mousescroll'* 'mousescroll' string (default "ver:3,hor:6") global This option controls the number of lines / columns to scroll by when scrolling with a mouse. The option is a comma separated list of parts. Each part consists of a direction and a count as follows: direction:count,direction:count - Direction is one of either "hor" or "ver", "hor" controls horizontal + Direction is one of either "hor" or "ver". "hor" controls horizontal scrolling and "ver" controls vertical scrolling. Count sets the amount to scroll by for the given direction, it should be a non negative integer. Each direction should be set at most once. If a direction @@ -4854,7 +4854,7 @@ A jump table for the options with a short description can be found at |Q_op|. If 'rulerformat' is set, it will determine the contents of the ruler. Each window has its own ruler. If a window has a status line, the ruler is shown there. If a window doesn't have a status line and - 'cmdheight' is 0, the ruler is not shown. Otherwise it is shown in + 'cmdheight' is zero, the ruler is not shown. Otherwise it is shown in the last line of the screen. If the statusline is given by 'statusline' (i.e. not empty), this option takes precedence over 'ruler' and 'rulerformat'. @@ -5116,7 +5116,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'sessionoptions'* *'ssop'* 'sessionoptions' 'ssop' string (default: "blank,buffers,curdir,folds, - help,tabpages,winsize") + help,tabpages,winsize,terminal") global Changes the effect of the |:mksession| command. It is a comma- separated list of words. Each word enables saving and restoring @@ -5563,7 +5563,7 @@ A jump table for the options with a short description can be found at |Q_op|. global Show (partial) command in the last line of the screen. Set this option off if your terminal is slow. - The option is disabled if 'cmdheight' is 0. + The option has no effect when 'cmdheight' is zero. In Visual mode the size of the selected area is shown: - When selecting characters within a line, the number of characters. If the number of bytes is different it is also displayed: "2-6" @@ -5610,7 +5610,7 @@ A jump table for the options with a short description can be found at |Q_op|. global If in Insert, Replace or Visual mode put a message on the last line. The |hl-ModeMsg| highlight group determines the highlighting. - The option is disabled if 'cmdheight' is 0. + The option has no effect when 'cmdheight' is zero. *'showtabline'* *'stal'* 'showtabline' 'stal' number (default 1) diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 6f7b53722c..1eaa76264f 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -3917,7 +3917,7 @@ netrw: * Installed |g:netrw_clipboard| setting * Installed option bypass for |'guioptions'| a/A settings - * Changed popup_beval() to |popup_atcursor|() + * Changed popup_beval() to |popup_atcursor()| in netrw#ErrorMsg (lacygoill). Apparently popup_beval doesn't reliably close the popup when the mouse is moved. diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index bc45b0e511..23d5905ec3 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -92,7 +92,7 @@ zuW *zuG* *zuW* zuG Undo |zW| and |zG|, remove the word from the internal word list. Count used as with |zg|. - *:spe* *:spellgood* + *:spe* *:spellgood* *E1280* :[count]spe[llgood] {word} Add {word} as a good word to 'spellfile', like with |zg|. Without count the first name is used, with a diff --git a/runtime/doc/undo.txt b/runtime/doc/undo.txt index 67f24103cd..98ab60c7e7 100644 --- a/runtime/doc/undo.txt +++ b/runtime/doc/undo.txt @@ -108,13 +108,13 @@ change again. But you can do something like this: > After this a "u" command will undo the delete command and the previous change. - *undo-break* -To do the opposite, break a change into two undo blocks, in Insert mode use -CTRL-G u. This is useful if you want an insert command to be undoable in + *undo-break* *undo-close-block* +To do the opposite, use a new undo block for the next change, in Insert mode +use CTRL-G u. This is useful if you want an insert command to be undoable in parts. E.g., for each sentence. |i_CTRL-G_u| -Setting the value of 'undolevels' also breaks undo. Even when the new value -is equal to the old value: > +Setting the value of 'undolevels' also closes the undo block. Even when the +new value is equal to the old value: > let &undolevels = &undolevels ============================================================================== diff --git a/runtime/doc/usr_06.txt b/runtime/doc/usr_06.txt index b99e0fb482..8eda33b4f0 100644 --- a/runtime/doc/usr_06.txt +++ b/runtime/doc/usr_06.txt @@ -135,7 +135,6 @@ You could also write your own color scheme. This is how you do it: 2. Edit the color scheme file. These entries are useful: - term attributes in a B&W terminal cterm attributes in a color terminal ctermfg foreground color in a color terminal ctermbg background color in a color terminal diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 9eb6470962..cae9c76030 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -239,7 +239,8 @@ g8 Print the hex values of the bytes used in the Type |i| to enter |Terminal-mode|, then keys are sent to the job running in the terminal. Type <C-\><C-N> to - leave Terminal-mode. |CTRL-\_CTRL-N| + leave Terminal-mode. |CTRL-\_CTRL-N|. Type <C-\><C-O> + to execute a single normal mode command |t_CTRL-\_CTRL-O| Fails if changes have been made to the current buffer, unless 'hidden' is set. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index a74149d050..27c953a460 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -250,7 +250,7 @@ Input/Mappings: <M-1>, <M-BS>, <M-Del>, <M-Ins>, <M-/>, <M-\>, <M-Space>, <M-Enter>, etc. Case-sensitive: <M-a> and <M-A> are two different keycodes. - ALT behaves like <Esc> if not mapped. |i_ALT| |v_ALT| |c_ALT| + ALT may behave like <Esc> if not mapped. |i_ALT| |v_ALT| |c_ALT| Normal commands: |gO| shows a filetype-defined "outline" of the current buffer. diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index 905ae49887..5383ea4f72 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -161,9 +161,10 @@ If you want to highlight exactly the same area as the last time, you can use *v_<Esc>* <Esc> In Visual mode: Stop Visual mode. *v_META* *v_ALT* - ALT (|META|) acts like <Esc> if the chord is not mapped. + ALT (|META|) may act like <Esc> if the chord is not mapped. For example <A-x> acts like <Esc>x if <A-x> does not have a visual-mode mapping. + *v_CTRL-C* CTRL-C In Visual mode: Stop Visual mode. When insert mode is pending (the mode message shows diff --git a/runtime/filetype.vim b/runtime/filetype.vim index f28118e272..fbb4b9f6aa 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -260,7 +260,7 @@ au BufNewFile,BufRead *.bb,*.bbappend,*.bbclass,*/build/conf/*.conf,*/meta{-*,}/ au BufNewFile,BufRead */etc/blkid.tab,*/etc/blkid.tab.old setf xml " BSDL -au BufNewFile,BufRead *bsd,*.bsdl setf bsdl +au BufNewFile,BufRead *.bsd,*.bsdl setf bsdl " Bazel (http://bazel.io) autocmd BufRead,BufNewFile *.bzl,*.bazel,WORKSPACE setf bzl @@ -860,9 +860,13 @@ au BufNewFile,BufRead *.hb setf hb " Httest au BufNewFile,BufRead *.htt,*.htb setf httest -" i3 (and sway) -au BufNewFile,BufRead */i3/config,*/sway/config setf i3config -au BufNewFile,BufRead */.i3/config,*/.sway/config setf i3config +" i3 +au BufNewFile,BufRead */i3/config setf i3config +au BufNewFile,BufRead */.i3/config setf i3config + +" sway +au BufNewFile,BufRead */sway/config setf swayconfig +au BufNewFile,BufRead */.sway/config setf swayconfig " Icon au BufNewFile,BufRead *.icn setf icon @@ -1983,8 +1987,8 @@ au BufRead,BufNewFile *.ttl " Terminfo au BufNewFile,BufRead *.ti setf terminfo -" Terraform -au BufRead,BufNewFile *.tfvars setf terraform +" Terraform variables +au BufRead,BufNewFile *.tfvars setf terraform-vars " TeX au BufNewFile,BufRead *.latex,*.sty,*.dtx,*.ltx,*.bbl setf tex diff --git a/runtime/ftplugin/bitbake.vim b/runtime/ftplugin/bitbake.vim new file mode 100644 index 0000000000..99fe334627 --- /dev/null +++ b/runtime/ftplugin/bitbake.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: Bitbake +" Maintainer: Gregory Anders <greg@gpanders.com> +" Repository: https://github.com/openembedded/bitbake +" Latest Revision: 2022-07-23 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal commentstring=#%s +setlocal comments=:# +setlocal suffixesadd=.bb,.bbclass + +let b:undo_ftplugin = "setl cms< com< sua<" diff --git a/runtime/ftplugin/debchangelog.vim b/runtime/ftplugin/debchangelog.vim index a78f7811f1..cf8dd17c44 100644 --- a/runtime/ftplugin/debchangelog.vim +++ b/runtime/ftplugin/debchangelog.vim @@ -1,9 +1,9 @@ " Vim filetype plugin file (GUI menu, folding and completion) " Language: Debian Changelog -" Maintainer: Debian Vim Maintainers +" Maintainer: Debian Vim Maintainers <team+vim@tracker.debian.org> " Former Maintainers: Michael Piefel <piefel@informatik.hu-berlin.de> " Stefano Zacchiroli <zack@debian.org> -" Last Change: 2018-01-28 +" Last Change: 2022 Jul 25 " License: Vim License " URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/ftplugin/debchangelog.vim @@ -35,6 +35,11 @@ if exists('g:did_changelog_ftplugin') finish endif +" Make sure the '<' and 'C' flags are not included in 'cpoptions', otherwise +" <CR> would not be recognized. See ":help 'cpoptions'". +let s:cpo_save = &cpo +set cpo&vim + " Don't load another plugin (this is global) let g:did_changelog_ftplugin = 1 @@ -101,13 +106,13 @@ endfunction " These functions implement the menus function NewVersion() " The new entry is unfinalised and shall be changed - amenu disable Changelog.New\ Version - amenu enable Changelog.Add\ Entry - amenu enable Changelog.Close\ Bug - amenu enable Changelog.Set\ Distribution - amenu enable Changelog.Set\ Urgency - amenu disable Changelog.Unfinalise - amenu enable Changelog.Finalise + amenu disable &Changelog.&New\ Version + amenu enable &Changelog.&Add\ Entry + amenu enable &Changelog.&Close\ Bug + amenu enable &Changelog.Set\ &Distribution + amenu enable &Changelog.Set\ &Urgency + amenu disable &Changelog.U&nfinalise + amenu enable &Changelog.&Finalise call append(0, substitute(getline(1), '-\([[:digit:]]\+\))', '-$$\1)', '')) call append(1, '') call append(2, '') @@ -117,7 +122,9 @@ function NewVersion() normal! 1G0 call search(')') normal! h - normal! + " ':normal' doens't support key annotation (<c-a>) directly. + " Vim's manual recommends using ':exe' to use key annotation indirectly (backslash-escaping needed though). + exe "normal! \<c-a>" call setline(1, substitute(getline(1), '-\$\$', '-', '')) if exists('g:debchangelog_fold_enable') foldopen @@ -161,13 +168,13 @@ endfunction function <SID>UnfinaliseMenu() " This means the entry shall be changed - amenu disable Changelog.New\ Version - amenu enable Changelog.Add\ Entry - amenu enable Changelog.Close\ Bug - amenu enable Changelog.Set\ Distribution - amenu enable Changelog.Set\ Urgency - amenu disable Changelog.Unfinalise - amenu enable Changelog.Finalise + amenu disable &Changelog.&New\ Version + amenu enable &Changelog.&Add\ Entry + amenu enable &Changelog.&Close\ Bug + amenu enable &Changelog.Set\ &Distribution + amenu enable &Changelog.Set\ &Urgency + amenu disable &Changelog.U&nfinalise + amenu enable &Changelog.&Finalise endfunction function Unfinalise() @@ -179,13 +186,13 @@ endfunction function <SID>FinaliseMenu() " This means the entry should not be changed anymore - amenu enable Changelog.New\ Version - amenu disable Changelog.Add\ Entry - amenu disable Changelog.Close\ Bug - amenu disable Changelog.Set\ Distribution - amenu disable Changelog.Set\ Urgency - amenu enable Changelog.Unfinalise - amenu disable Changelog.Finalise + amenu enable &Changelog.&New\ Version + amenu disable &Changelog.&Add\ Entry + amenu disable &Changelog.&Close\ Bug + amenu disable &Changelog.Set\ &Distribution + amenu disable &Changelog.Set\ &Urgency + amenu enable &Changelog.U&nfinalise + amenu disable &Changelog.&Finalise endfunction function Finalise() @@ -198,26 +205,26 @@ endfunction function <SID>MakeMenu() amenu &Changelog.&New\ Version :call NewVersion()<CR> - amenu Changelog.&Add\ Entry :call AddEntry()<CR> - amenu Changelog.&Close\ Bug :call CloseBug()<CR> - menu Changelog.-sep- <nul> - - amenu Changelog.Set\ &Distribution.&unstable :call Distribution("unstable")<CR> - amenu Changelog.Set\ Distribution.&frozen :call Distribution("frozen")<CR> - amenu Changelog.Set\ Distribution.&stable :call Distribution("stable")<CR> - menu Changelog.Set\ Distribution.-sep- <nul> - amenu Changelog.Set\ Distribution.frozen\ unstable :call Distribution("frozen unstable")<CR> - amenu Changelog.Set\ Distribution.stable\ unstable :call Distribution("stable unstable")<CR> - amenu Changelog.Set\ Distribution.stable\ frozen :call Distribution("stable frozen")<CR> - amenu Changelog.Set\ Distribution.stable\ frozen\ unstable :call Distribution("stable frozen unstable")<CR> - - amenu Changelog.Set\ &Urgency.&low :call Urgency("low")<CR> - amenu Changelog.Set\ Urgency.&medium :call Urgency("medium")<CR> - amenu Changelog.Set\ Urgency.&high :call Urgency("high")<CR> - - menu Changelog.-sep- <nul> - amenu Changelog.U&nfinalise :call Unfinalise()<CR> - amenu Changelog.&Finalise :call Finalise()<CR> + amenu &Changelog.&Add\ Entry :call AddEntry()<CR> + amenu &Changelog.&Close\ Bug :call CloseBug()<CR> + menu &Changelog.-sep- <nul> + + amenu &Changelog.Set\ &Distribution.&unstable :call Distribution("unstable")<CR> + amenu &Changelog.Set\ &Distribution.&frozen :call Distribution("frozen")<CR> + amenu &Changelog.Set\ &Distribution.&stable :call Distribution("stable")<CR> + menu &Changelog.Set\ &Distribution.-sep- <nul> + amenu &Changelog.Set\ &Distribution.frozen\ unstable :call Distribution("frozen unstable")<CR> + amenu &Changelog.Set\ &Distribution.stable\ unstable :call Distribution("stable unstable")<CR> + amenu &Changelog.Set\ &Distribution.stable\ frozen :call Distribution("stable frozen")<CR> + amenu &Changelog.Set\ &Distribution.stable\ frozen\ unstable :call Distribution("stable frozen unstable")<CR> + + amenu &Changelog.Set\ &Urgency.&low :call Urgency("low")<CR> + amenu &Changelog.Set\ &Urgency.&medium :call Urgency("medium")<CR> + amenu &Changelog.Set\ &Urgency.&high :call Urgency("high")<CR> + + menu &Changelog.-sep- <nul> + amenu &Changelog.U&nfinalise :call Unfinalise()<CR> + amenu &Changelog.&Finalise :call Finalise()<CR> if <SID>Finalised() call <SID>FinaliseMenu() @@ -228,7 +235,7 @@ endfunction augroup changelogMenu au BufEnter * if &filetype == "debchangelog" | call <SID>MakeMenu() | endif -au BufLeave * if &filetype == "debchangelog" | silent! aunmenu Changelog | endif +au BufLeave * if &filetype == "debchangelog" | silent! aunmenu &Changelog | endif augroup END " }}} @@ -380,4 +387,8 @@ setlocal omnifunc=DebCompleteBugs " }}} +" Restore the previous value of 'cpoptions'. +let &cpo = s:cpo_save +unlet s:cpo_save + " vim: set foldmethod=marker: diff --git a/runtime/ftplugin/desktop.vim b/runtime/ftplugin/desktop.vim new file mode 100644 index 0000000000..bd6fd7097c --- /dev/null +++ b/runtime/ftplugin/desktop.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin file +" Language: XDG desktop entry +" Maintainer: Eisuke Kawashima ( e.kawaschima+vim AT gmail.com ) +" Last Change: 2022-07-26 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = v:true + +setl comments=:# +setl commentstring=#%s +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/expect.vim b/runtime/ftplugin/expect.vim new file mode 100644 index 0000000000..a4c6af96ce --- /dev/null +++ b/runtime/ftplugin/expect.vim @@ -0,0 +1,24 @@ +" Vim filetype plugin file +" Language: Expect +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Jul 16 + +if exists("b:did_ftplugin") + finish +endif + +" Syntax is similar to Tcl +runtime! ftplugin/tcl.vim + +let s:cpo_save = &cpo +set cpo&vim + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Expect Command Files (*.exp)\t*.exp\n" .. + \ "All Files (*.*)\t*.*\n" +endif + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: nowrap sw=2 sts=2 ts=8 diff --git a/runtime/ftplugin/html.vim b/runtime/ftplugin/html.vim index 3179aa2e88..94cb62653f 100644 --- a/runtime/ftplugin/html.vim +++ b/runtime/ftplugin/html.vim @@ -1,16 +1,14 @@ " Vim filetype plugin file -" Language: html -" -" This runtime file is looking for a new maintainer. -" -" Former maintainer: Dan Sharp -" Last Changed: 20 Jan 2009 - -if exists("b:did_ftplugin") | finish | endif +" Language: HTML +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Dan Sharp +" Last Changed: 2022 Jul 20 + +if exists("b:did_ftplugin") + finish +endif let b:did_ftplugin = 1 -" Make sure the continuation lines below do not cause problems in -" compatibility mode. let s:save_cpo = &cpo set cpo-=C @@ -18,36 +16,40 @@ setlocal matchpairs+=<:> setlocal commentstring=<!--%s--> setlocal comments=s:<!--,m:\ \ \ \ ,e:--> -if exists("g:ft_html_autocomment") && (g:ft_html_autocomment == 1) - setlocal formatoptions-=t formatoptions+=croql +let b:undo_ftplugin = "setlocal comments< commentstring< matchpairs<" + +if get(g:, "ft_html_autocomment", 0) + setlocal formatoptions-=t formatoptions+=croql + let b:undo_ftplugin ..= " | setlocal formatoptions<" endif if exists('&omnifunc') setlocal omnifunc=htmlcomplete#CompleteTags call htmlcomplete#DetectOmniFlavor() + let b:undo_ftplugin ..= " | setlocal omnifunc<" endif -" HTML: thanks to Johannes Zellner and Benji Fisher. -if exists("loaded_matchit") - let b:match_ignorecase = 1 - let b:match_words = '<:>,' . - \ '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,' . - \ '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,' . - \ '<\@<=\([^/][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>' +" HTML: thanks to Johannes Zellner and Benji Fisher. +if exists("loaded_matchit") && !exists("b:match_words") + let b:match_ignorecase = 1 + let b:match_words = '<!--:-->,' .. + \ '<:>,' .. + \ '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,' .. + \ '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,' .. + \ '<\@<=\([^/!][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>' + let b:html_set_match_words = 1 + let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words b:html_set_match_words" endif " Change the :browse e filter to primarily show HTML-related files. -if has("gui_win32") - let b:browsefilter="HTML Files (*.html,*.htm)\t*.htm;*.html\n" . - \ "JavaScript Files (*.js)\t*.js\n" . - \ "Cascading StyleSheets (*.css)\t*.css\n" . - \ "All Files (*.*)\t*.*\n" +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "HTML Files (*.html *.htm)\t*.htm;*.html\n" .. + \ "JavaScript Files (*.js)\t*.js\n" .. + \ "Cascading StyleSheets (*.css)\t*.css\n" .. + \ "All Files (*.*)\t*.*\n" + let b:html_set_browsefilter = 1 + let b:undo_ftplugin ..= " | unlet! b:browsefilter b:html_set_browsefilter" endif -" Undo the stuff we changed. -let b:undo_ftplugin = "setlocal commentstring< matchpairs< omnifunc< comments< formatoptions<" . - \ " | unlet! b:match_ignorecase b:match_skip b:match_words b:browsefilter" - -" Restore the saved compatibility options. let &cpo = s:save_cpo unlet s:save_cpo diff --git a/runtime/ftplugin/swayconfig.vim b/runtime/ftplugin/swayconfig.vim new file mode 100644 index 0000000000..45d6bdb3e5 --- /dev/null +++ b/runtime/ftplugin/swayconfig.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: sway config file +" Original Author: James Eapen <james.eapen@vai.org> +" Maintainer: James Eapen <james.eapen@vai.org> +" Version: 0.1 +" Last Change: 2022 June 07 + +if exists("b:did_ftplugin") + finish +endif + +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setlocal cms<" + +setlocal commentstring=#\ %s diff --git a/runtime/indent/bitbake.vim b/runtime/indent/bitbake.vim new file mode 100644 index 0000000000..f45ba74816 --- /dev/null +++ b/runtime/indent/bitbake.vim @@ -0,0 +1,22 @@ +" Vim indent file +" Language: BitBake +" Copyright: Copyright (C) 2019 Agilent Technologies, Inc. +" Maintainer: Chris Laplante <chris.laplante@agilent.com> +" License: You may redistribute this under the same terms as Vim itself + +if exists("b:did_indent") + finish +endif + +runtime! indent/sh.vim + +setlocal indentexpr=bitbake#Indent(v:lnum) +setlocal autoindent +setlocal nolisp +setlocal shiftwidth=4 +setlocal expandtab +setlocal indentkeys+=<:>,=elif,=except,0=\" + +let b:undo_indent .= ' inde< ai< lisp< sw< et< indk<' + +let b:did_indent = 1 diff --git a/runtime/indent/expect.vim b/runtime/indent/expect.vim new file mode 100644 index 0000000000..f2a1f05917 --- /dev/null +++ b/runtime/indent/expect.vim @@ -0,0 +1,11 @@ +" Vim indent file +" Language: Expect +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Jul 16 + +if exists("b:did_indent") + finish +endif + +" Syntax is similar to Tcl +runtime! indent/tcl.vim diff --git a/runtime/indent/html.vim b/runtime/indent/html.vim index a3c32d6342..65e0ffc40c 100644 --- a/runtime/indent/html.vim +++ b/runtime/indent/html.vim @@ -600,7 +600,7 @@ func s:Alien3() endif if b:hi_indent.scripttype == "javascript" " indent for further lines - return eval(b:hi_js1indent) + GetJavascriptIndent() + return GetJavascriptIndent() else return -1 endif diff --git a/runtime/indent/javascript.vim b/runtime/indent/javascript.vim index f3bf96aa97..8077442ed0 100644 --- a/runtime/indent/javascript.vim +++ b/runtime/indent/javascript.vim @@ -473,6 +473,12 @@ function GetJavascriptIndent() elseif num return s:Nat(num_ind + get(l:,'case_offset',s:sw()) + l:switch_offset + b_l + is_op) endif + + let nest = get(get(b:, 'hi_indent', {}), 'blocklnr') + if nest + return indent(nextnonblank(nest + 1)) + b_l + is_op + endif + return b_l + is_op endfunction diff --git a/runtime/indent/python.vim b/runtime/indent/python.vim index 668122993e..8c3d0b0670 100644 --- a/runtime/indent/python.vim +++ b/runtime/indent/python.vim @@ -14,7 +14,7 @@ let b:did_indent = 1 setlocal nolisp " Make sure lisp indenting doesn't supersede us setlocal autoindent " indentexpr isn't much help otherwise -setlocal indentexpr=GetPythonIndent(v:lnum) +setlocal indentexpr=python#GetIndent(v:lnum) setlocal indentkeys+=<:>,=elif,=except let b:undo_indent = "setl ai< inde< indk< lisp<" @@ -23,206 +23,11 @@ let b:undo_indent = "setl ai< inde< indk< lisp<" if exists("*GetPythonIndent") finish endif -let s:keepcpo= &cpo -set cpo&vim - -" Come here when loading the script the first time. - -let s:maxoff = 50 " maximum number of lines to look backwards for () - -" See if the specified line is already user-dedented from the expected value. -function s:Dedented(lnum, expected) - return indent(a:lnum) <= a:expected - shiftwidth() -endfunction +" Keep this for backward compatibility, new scripts should use +" python#GetIndent() function GetPythonIndent(lnum) - - " If this line is explicitly joined: If the previous line was also joined, - " line it up with that one, otherwise add two 'shiftwidth' - if getline(a:lnum - 1) =~ '\\$' - if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$' - return indent(a:lnum - 1) - endif - return indent(a:lnum - 1) + (exists("g:pyindent_continue") ? eval(g:pyindent_continue) : (shiftwidth() * 2)) - endif - - " If the start of the line is in a string don't change the indent. - if has('syntax_items') - \ && synIDattr(synID(a:lnum, 1, 1), "name") =~ "String$" - return -1 - endif - - " Search backwards for the previous non-empty line. - let plnum = prevnonblank(v:lnum - 1) - - if plnum == 0 - " This is the first non-empty line, use zero indent. - return 0 - endif - - call cursor(plnum, 1) - - " Identing inside parentheses can be very slow, regardless of the searchpair() - " timeout, so let the user disable this feature if he doesn't need it - let disable_parentheses_indenting = get(g:, "pyindent_disable_parentheses_indenting", 0) - - if disable_parentheses_indenting == 1 - let plindent = indent(plnum) - let plnumstart = plnum - else - " searchpair() can be slow sometimes, limit the time to 150 msec or what is - " put in g:pyindent_searchpair_timeout - let searchpair_stopline = 0 - let searchpair_timeout = get(g:, 'pyindent_searchpair_timeout', 150) - - " If the previous line is inside parenthesis, use the indent of the starting - " line. - " Trick: use the non-existing "dummy" variable to break out of the loop when - " going too far back. - let parlnum = searchpair('(\|{\|\[', '', ')\|}\|\]', 'nbW', - \ "line('.') < " . (plnum - s:maxoff) . " ? dummy :" - \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" - \ . " =~ '\\(Comment\\|Todo\\|String\\)$'", - \ searchpair_stopline, searchpair_timeout) - if parlnum > 0 - let plindent = indent(parlnum) - let plnumstart = parlnum - else - let plindent = indent(plnum) - let plnumstart = plnum - endif - - " When inside parenthesis: If at the first line below the parenthesis add - " two 'shiftwidth', otherwise same as previous line. - " i = (a - " + b - " + c) - call cursor(a:lnum, 1) - let p = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', - \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" - \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" - \ . " =~ '\\(Comment\\|Todo\\|String\\)$'", - \ searchpair_stopline, searchpair_timeout) - if p > 0 - if p == plnum - " When the start is inside parenthesis, only indent one 'shiftwidth'. - let pp = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', - \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" - \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" - \ . " =~ '\\(Comment\\|Todo\\|String\\)$'", - \ searchpair_stopline, searchpair_timeout) - if pp > 0 - return indent(plnum) + (exists("g:pyindent_nested_paren") ? eval(g:pyindent_nested_paren) : shiftwidth()) - endif - return indent(plnum) + (exists("g:pyindent_open_paren") ? eval(g:pyindent_open_paren) : (shiftwidth() * 2)) - endif - if plnumstart == p - return indent(plnum) - endif - return plindent - endif - - endif - - - " Get the line and remove a trailing comment. - " Use syntax highlighting attributes when possible. - let pline = getline(plnum) - let pline_len = strlen(pline) - if has('syntax_items') - " If the last character in the line is a comment, do a binary search for - " the start of the comment. synID() is slow, a linear search would take - " too long on a long line. - if synIDattr(synID(plnum, pline_len, 1), "name") =~ "\\(Comment\\|Todo\\)$" - let min = 1 - let max = pline_len - while min < max - let col = (min + max) / 2 - if synIDattr(synID(plnum, col, 1), "name") =~ "\\(Comment\\|Todo\\)$" - let max = col - else - let min = col + 1 - endif - endwhile - let pline = strpart(pline, 0, min - 1) - endif - else - let col = 0 - while col < pline_len - if pline[col] == '#' - let pline = strpart(pline, 0, col) - break - endif - let col = col + 1 - endwhile - endif - - " If the previous line ended with a colon, indent this line - if pline =~ ':\s*$' - return plindent + shiftwidth() - endif - - " If the previous line was a stop-execution statement... - if getline(plnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>' - " See if the user has already dedented - if s:Dedented(a:lnum, indent(plnum)) - " If so, trust the user - return -1 - endif - " If not, recommend one dedent - return indent(plnum) - shiftwidth() - endif - - " If the current line begins with a keyword that lines up with "try" - if getline(a:lnum) =~ '^\s*\(except\|finally\)\>' - let lnum = a:lnum - 1 - while lnum >= 1 - if getline(lnum) =~ '^\s*\(try\|except\)\>' - let ind = indent(lnum) - if ind >= indent(a:lnum) - return -1 " indent is already less than this - endif - return ind " line up with previous try or except - endif - let lnum = lnum - 1 - endwhile - return -1 " no matching "try"! - endif - - " If the current line begins with a header keyword, dedent - if getline(a:lnum) =~ '^\s*\(elif\|else\)\>' - - " Unless the previous line was a one-liner - if getline(plnumstart) =~ '^\s*\(for\|if\|elif\|try\)\>' - return plindent - endif - - " Or the user has already dedented - if s:Dedented(a:lnum, plindent) - return -1 - endif - - return plindent - shiftwidth() - endif - - " When after a () construct we probably want to go back to the start line. - " a = (b - " + c) - " here - if parlnum > 0 - " ...unless the user has already dedented - if s:Dedented(a:lnum, plindent) - return -1 - else - return plindent - endif - endif - - return -1 - + return python#GetIndent(a:lnum) endfunction -let &cpo = s:keepcpo -unlet s:keepcpo - " vim:sw=2 diff --git a/runtime/indent/testdir/bitbake.in b/runtime/indent/testdir/bitbake.in new file mode 100644 index 0000000000..afd19be182 --- /dev/null +++ b/runtime/indent/testdir/bitbake.in @@ -0,0 +1,19 @@ +# vim: set filetype=bitbake : + +# START_INDENT +FOO = " \ + bar \ + baz \ + qux \ + " + +do_configure() { +oe_conf +} + +python do_task() { +def foo(x): +if y: +print(x) +} +# END_INDENT diff --git a/runtime/indent/testdir/bitbake.ok b/runtime/indent/testdir/bitbake.ok new file mode 100644 index 0000000000..1bc5a18c6f --- /dev/null +++ b/runtime/indent/testdir/bitbake.ok @@ -0,0 +1,19 @@ +# vim: set filetype=bitbake : + +# START_INDENT +FOO = " \ + bar \ + baz \ + qux \ +" + +do_configure() { + oe_conf +} + +python do_task() { + def foo(x): + if y: + print(x) +} +# END_INDENT diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 442d7b07d8..b8a7f71145 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -288,6 +288,9 @@ end --- Execute Vim script commands. --- +--- Note that `vim.cmd` can be indexed with a command name to return a callable function to the +--- command. +--- --- Example: --- <pre> --- vim.cmd('echo 42') @@ -297,7 +300,23 @@ end --- autocmd FileType c setlocal cindent --- augroup END --- ]]) ---- vim.cmd({ cmd = 'echo', args = { '"foo"' } }) +--- +--- -- Ex command :echo "foo" +--- -- Note string literals need to be double quoted. +--- vim.cmd('echo "foo"') +--- vim.cmd { cmd = 'echo', args = { '"foo"' } } +--- vim.cmd.echo({ args = { '"foo"' } }) +--- vim.cmd.echo('"foo"') +--- +--- -- Ex command :write! myfile.txt +--- vim.cmd('write! myfile.txt') +--- vim.cmd { cmd = 'write', args = { "myfile.txt" }, bang = true } +--- vim.cmd.write { args = { "myfile.txt" }, bang = true } +--- vim.cmd.write { "myfile.txt", bang = true } +--- +--- -- Ex command :colorscheme blue +--- vim.cmd('colorscheme blue') +--- vim.cmd.colorscheme('blue') --- </pre> --- ---@param command string|table Command(s) to execute. @@ -307,14 +326,47 @@ end --- If a table, executes a single command. In this case, it is an alias --- to |nvim_cmd()| where `opts` is empty. ---@see |ex-cmd-index| -function vim.cmd(command) - if type(command) == 'table' then - return vim.api.nvim_cmd(command, {}) - else - return vim.api.nvim_exec(command, false) - end +function vim.cmd(command) -- luacheck: no unused + error(command) -- Stub for gen_vimdoc.py end +local VIM_CMD_ARG_MAX = 20 + +vim.cmd = setmetatable({}, { + __call = function(_, command) + if type(command) == 'table' then + return vim.api.nvim_cmd(command, {}) + else + return vim.api.nvim_exec(command, false) + end + end, + __index = function(t, command) + t[command] = function(...) + local opts + if select('#', ...) == 1 and type(select(1, ...)) == 'table' then + opts = select(1, ...) + + -- Move indexed positions in opts to opt.args + if opts[1] and not opts.args then + opts.args = {} + for i = 1, VIM_CMD_ARG_MAX do + if not opts[i] then + break + end + opts.args[i] = opts[i] + opts[i] = nil + end + end + else + opts = { args = { ... } } + end + opts.cmd = command + return vim.api.nvim_cmd(opts, {}) + end + return t[command] + end, +}) + -- These are the vim.env/v/g/o/bo/wo variable magic accessors. do local validate = vim.validate diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index ae20b5c517..3f71d4f70d 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -586,12 +586,12 @@ end --- --- For example, if a user enables virtual text globally with --- <pre> ---- vim.diagnostic.config({virtual_text = true}) +--- vim.diagnostic.config({ virtual_text = true }) --- </pre> --- --- and a diagnostic producer sets diagnostics with --- <pre> ---- vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false}) +--- vim.diagnostic.set(ns, 0, diagnostics, { virtual_text = false }) --- </pre> --- --- then virtual text will not be enabled for those diagnostics. @@ -1525,8 +1525,8 @@ end --- <pre> --- local s = "WARNING filename:27:3: Variable 'foo' does not exist" --- local pattern = "^(%w+) %w+:(%d+):(%d+): (.+)$" ---- local groups = {"severity", "lnum", "col", "message"} ---- vim.diagnostic.match(s, pattern, groups, {WARNING = vim.diagnostic.WARN}) +--- local groups = { "severity", "lnum", "col", "message" } +--- vim.diagnostic.match(s, pattern, groups, { WARNING = vim.diagnostic.WARN }) --- </pre> --- ---@param str string String to parse diagnostics from. diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 70c8cd15eb..1b209e6a9d 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -176,6 +176,7 @@ local extension = { bbappend = 'bitbake', bbclass = 'bitbake', bl = 'blank', + bsd = 'bsdl', bsdl = 'bsdl', bst = 'bst', btm = function(path, bufnr) @@ -968,7 +969,7 @@ local extension = { txi = 'texinfo', texinfo = 'texinfo', text = 'text', - tfvars = 'terraform', + tfvars = 'terraform-vars', tla = 'tla', tli = 'tli', toml = 'toml', @@ -1383,10 +1384,6 @@ local filename = { ['/etc/host.conf'] = 'hostconf', ['/etc/hosts.allow'] = 'hostsaccess', ['/etc/hosts.deny'] = 'hostsaccess', - ['/i3/config'] = 'i3config', - ['/sway/config'] = 'i3config', - ['/.sway/config'] = 'i3config', - ['/.i3/config'] = 'i3config', ['/.icewm/menu'] = 'icemenu', ['.indent.pro'] = 'indent', indentrc = 'indent', @@ -1668,7 +1665,6 @@ local pattern = { ['.*/build/conf/.*%.conf'] = 'bitbake', ['.*/meta/conf/.*%.conf'] = 'bitbake', ['.*/meta%-.*/conf/.*%.conf'] = 'bitbake', - ['.*bsd'] = 'bsdl', ['bzr_log%..*'] = 'bzr', ['.*enlightenment/.*%.cfg'] = 'c', ['cabal%.project%..*'] = starsetf('cabalproject'), @@ -1835,9 +1831,7 @@ local pattern = { ['.*/etc/hosts%.allow'] = 'hostsaccess', ['.*%.html%.m4'] = 'htmlm4', ['.*/%.i3/config'] = 'i3config', - ['.*/sway/config'] = 'i3config', ['.*/i3/config'] = 'i3config', - ['.*/%.sway/config'] = 'i3config', ['.*/%.icewm/menu'] = 'icemenu', ['.*/etc/initng/.*/.*%.i'] = 'initng', ['JAM.*%..*'] = starsetf('jam'), @@ -2076,6 +2070,8 @@ local pattern = { end, ['.*/etc/sudoers'] = 'sudoers', ['svn%-commit.*%.tmp'] = 'svn', + ['.*/sway/config'] = 'swayconfig', + ['.*/%.sway/config'] = 'swayconfig', ['.*%.swift%.gyb'] = 'swiftgyb', ['.*%.[Ss][Yy][Ss]'] = function(path, bufnr) return require('vim.filetype.detect').sys(bufnr) @@ -2241,30 +2237,30 @@ end --- <pre> --- vim.filetype.add({ --- extension = { ---- foo = "fooscript", +--- foo = 'fooscript', --- bar = function(path, bufnr) --- if some_condition() then ---- return "barscript", function(bufnr) +--- return 'barscript', function(bufnr) --- -- Set a buffer variable --- vim.b[bufnr].barscript_version = 2 --- end --- end ---- return "bar" +--- return 'bar' --- end, --- }, --- filename = { ---- [".foorc"] = "toml", ---- ["/etc/foo/config"] = "toml", +--- ['.foorc'] = 'toml', +--- ['/etc/foo/config'] = 'toml', --- }, --- pattern = { ---- [".*/etc/foo/.*"] = "fooscript", +--- ['.*/etc/foo/.*'] = 'fooscript', --- -- Using an optional priority ---- [".*/etc/foo/.*%.conf"] = { "dosini", { priority = 10 } }, ---- ["README.(%a+)$"] = function(path, bufnr, ext) ---- if ext == "md" then ---- return "markdown" ---- elseif ext == "rst" then ---- return "rst" +--- ['.*/etc/foo/.*%.conf'] = { 'dosini', { priority = 10 } }, +--- ['README.(%a+)$'] = function(path, bufnr, ext) +--- if ext == 'md' then +--- return 'markdown' +--- elseif ext == 'rst' then +--- return 'rst' --- end --- end, --- }, @@ -2279,9 +2275,9 @@ end --- priority = -math.huge, --- function(path, bufnr) --- local content = vim.filetype.getlines(bufnr, 1) ---- if vim.filetype.matchregex(content, { [[^#!.*\\<mine\\>]] }) then +--- if vim.filetype.matchregex(content, [[^#!.*\\<mine\\>]]) then --- return 'mine' ---- elseif vim.filetype.matchregex(content, { [[\\<drawing\\>]] }) then +--- elseif vim.filetype.matchregex(content, [[\\<drawing\\>]]) then --- return 'drawing' --- end --- end, diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 8c10517687..14f076717f 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -554,7 +554,7 @@ function M.inc(bufnr) -- headers so assume POV-Ray elseif findany(lines, { '^%s{', '^%s%(%*' }) or matchregex(lines, pascal_keywords) then return 'pascal' - elseif findany(lines, { '^%s*inherit ', '^%s*require ', '^%s*%w+%s+= ' }) then + elseif findany(lines, { '^%s*inherit ', '^%s*require ', '^%s*%u[%w_:${}]*%s+%??[?:+]?= ' }) then return 'bitbake' else local syntax = M.asm_syntax(bufnr) diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index f4c2b507a9..7265beb56b 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -31,22 +31,19 @@ local keymap = {} --- vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end) --- </pre> --- ----@param mode string|table Same mode short names as |nvim_set_keymap()|. +---@param mode string|table Same mode short names as |nvim_set_keymap()|. --- Can also be list of modes to create mapping on multiple modes. ----@param lhs string Left-hand side |{lhs}| of the mapping. +---@param lhs string Left-hand side |{lhs}| of the mapping. ---@param rhs string|function Right-hand side |{rhs}| of the mapping. Can also be a Lua function. ---- If a Lua function and `opts.expr == true`, returning `nil` is ---- equivalent to an empty string. -- ---@param opts table A table of |:map-arguments| such as "silent". In addition to the options --- listed in |nvim_set_keymap()|, this table also accepts the following keys: --- - buffer: (number or boolean) Add a mapping to the given buffer. When "true" --- or 0, use the current buffer. ---- - replace_keycodes: (boolean, default true) When both this and expr is "true", ---- |nvim_replace_termcodes()| is applied to the result of Lua expr maps. --- - remap: (boolean) Make the mapping recursive. This is the --- inverse of the "noremap" option from |nvim_set_keymap()|. --- Default `false`. +--- - replace_keycodes: (boolean) defaults to true if "expr" is true. ---@see |nvim_set_keymap()| function keymap.set(mode, lhs, rhs, opts) vim.validate({ @@ -60,22 +57,9 @@ function keymap.set(mode, lhs, rhs, opts) local is_rhs_luaref = type(rhs) == 'function' mode = type(mode) == 'string' and { mode } or mode - if is_rhs_luaref and opts.expr then - local user_rhs = rhs - rhs = function() - local res = user_rhs() - if res == nil then - -- TODO(lewis6991): Handle this in C? - return '' - elseif opts.replace_keycodes ~= false then - return vim.api.nvim_replace_termcodes(res, true, true, true) - else - return res - end - end + if opts.expr and opts.replace_keycodes ~= false then + opts.replace_keycodes = true end - -- clear replace_keycodes from opts table - opts.replace_keycodes = nil if opts.remap == nil then -- default remap value is false diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 61586ca44f..bf2201d9c8 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -371,7 +371,9 @@ do state_by_client[client.id] = state end if not state.buffers[bufnr] then - local buf_state = {} + local buf_state = { + name = api.nvim_buf_get_name(bufnr), + } state.buffers[bufnr] = buf_state if use_incremental_sync then buf_state.lines = nvim_buf_get_lines(bufnr, 0, -1, true) @@ -382,6 +384,15 @@ do end ---@private + function changetracking._get_and_set_name(client, bufnr, name) + local state = state_by_client[client.id] or {} + local buf_state = (state.buffers or {})[bufnr] + local old_name = buf_state.name + buf_state.name = name + return old_name + end + + ---@private function changetracking.reset_buf(client, bufnr) changetracking.flush(client, bufnr) local state = state_by_client[client.id] @@ -1405,6 +1416,19 @@ local function text_document_did_save_handler(bufnr) local uri = vim.uri_from_bufnr(bufnr) local text = once(buf_get_full_text) for_each_buffer_client(bufnr, function(client) + local name = api.nvim_buf_get_name(bufnr) + local old_name = changetracking._get_and_set_name(client, bufnr, name) + if old_name and name ~= old_name then + client.notify('textDocument/didOpen', { + textDocument = { + version = 0, + uri = uri, + languageId = client.config.get_language_id(bufnr, vim.bo[bufnr].filetype), + text = buf_get_full_text(bufnr), + }, + }) + util.buf_versions[bufnr] = 0 + end local save_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'save') if save_capability then local included_text diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 50a51e897c..63f4688d94 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -11,8 +11,8 @@ local M = {} --- buffer. --- ---@param method (string) LSP method name ----@param params (optional, table) Parameters to send to the server ----@param handler (optional, functionnil) See |lsp-handler|. Follows |lsp-handler-resolution| +---@param params (table|nil) Parameters to send to the server +---@param handler (function|nil) See |lsp-handler|. Follows |lsp-handler-resolution| -- ---@returns 2-tuple: --- - Map of client-id:request-id pairs for all successful requests. @@ -61,6 +61,7 @@ end --- ---@param options table|nil additional options --- - reuse_win: (boolean) Jump to existing window if buffer is already open. +--- - on_list: (function) handler for list results. See |on-list-handler| function M.declaration(options) local params = util.make_position_params() request_with_options('textDocument/declaration', params, options) @@ -70,6 +71,7 @@ end --- ---@param options table|nil additional options --- - reuse_win: (boolean) Jump to existing window if buffer is already open. +--- - on_list: (function) handler for list results. See |on-list-handler| function M.definition(options) local params = util.make_position_params() request_with_options('textDocument/definition', params, options) @@ -79,6 +81,7 @@ end --- ---@param options table|nil additional options --- - reuse_win: (boolean) Jump to existing window if buffer is already open. +--- - on_list: (function) handler for list results. See |on-list-handler| function M.type_definition(options) local params = util.make_position_params() request_with_options('textDocument/typeDefinition', params, options) @@ -86,9 +89,12 @@ end --- Lists all the implementations for the symbol under the cursor in the --- quickfix window. -function M.implementation() +--- +---@param options table|nil additional options +--- - on_list: (function) handler for list results. See |on-list-handler| +function M.implementation(options) local params = util.make_position_params() - request('textDocument/implementation', params) + request_with_options('textDocument/implementation', params, options) end --- Displays signature information about the symbol under the cursor in a @@ -151,7 +157,7 @@ end --- - formatting_options (table|nil): --- Can be used to specify FormattingOptions. Some unspecified options will be --- automatically derived from the current Neovim options. ---- @see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting +--- See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions --- - timeout_ms (integer|nil, default 1000): --- Time in milliseconds to block for formatting requests. No effect if async=true --- - bufnr (number|nil): @@ -496,20 +502,24 @@ end --- ---@param context (table) Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references -function M.references(context) +---@param options table|nil additional options +--- - on_list: (function) handler for list results. See |on-list-handler| +function M.references(context, options) validate({ context = { context, 't', true } }) local params = util.make_position_params() params.context = context or { includeDeclaration = true, } - request('textDocument/references', params) + request_with_options('textDocument/references', params, options) end --- Lists all symbols in the current buffer in the quickfix window. --- -function M.document_symbol() +---@param options table|nil additional options +--- - on_list: (function) handler for list results. See |on-list-handler| +function M.document_symbol(options) local params = { textDocument = util.make_text_document_params() } - request('textDocument/documentSymbol', params) + request_with_options('textDocument/documentSymbol', params, options) end ---@private @@ -648,13 +658,15 @@ end --- string means no filtering is done. --- ---@param query (string, optional) -function M.workspace_symbol(query) +---@param options table|nil additional options +--- - on_list: (function) handler for list results. See |on-list-handler| +function M.workspace_symbol(query, options) query = query or npcall(vim.fn.input, 'Query: ') if query == nil then return end local params = { query = query } - request('workspace/symbol', params) + request_with_options('workspace/symbol', params, options) end --- Send request to the server to resolve document highlights for the current @@ -830,20 +842,27 @@ end --- cursor position. --- ---@param options table|nil Optional table which holds the following optional fields: ---- - context (table|nil): ---- Corresponds to `CodeActionContext` of the LSP specification: ---- - diagnostics (table|nil): ---- LSP `Diagnostic[]`. Inferred from the current ---- position if not provided. ---- - only (table|nil): ---- List of LSP `CodeActionKind`s used to filter the code actions. ---- Most language servers support values like `refactor` ---- or `quickfix`. ---- - filter (function|nil): ---- Predicate function taking an `CodeAction` and returning a boolean. ---- - apply (boolean|nil): ---- When set to `true`, and there is just one remaining action ---- (after filtering), the action is applied without user query. +--- - context: (table|nil) +--- Corresponds to `CodeActionContext` of the LSP specification: +--- - diagnostics (table|nil): +--- LSP `Diagnostic[]`. Inferred from the current +--- position if not provided. +--- - only (table|nil): +--- List of LSP `CodeActionKind`s used to filter the code actions. +--- Most language servers support values like `refactor` +--- or `quickfix`. +--- - filter: (function|nil) +--- Predicate taking an `CodeAction` and returning a boolean. +--- - apply: (boolean|nil) +--- When set to `true`, and there is just one remaining action +--- (after filtering), the action is applied without user query. +--- +--- - range: (table|nil) +--- Range for which code actions should be requested. +--- If in visual mode this defaults to the active selection. +--- Table must contain `start` and `end` keys with {row, col} tuples +--- using mark-like indexing. See |api-indexing| +--- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction function M.code_action(options) validate({ options = { options, 't', true } }) @@ -858,7 +877,34 @@ function M.code_action(options) local bufnr = api.nvim_get_current_buf() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr) end - local params = util.make_range_params() + local params + local mode = api.nvim_get_mode().mode + if options.range then + assert(type(options.range) == 'table', 'code_action range must be a table') + local start = assert(options.range.start, 'range must have a `start` property') + local end_ = assert(options.range['end'], 'range must have a `end` property') + params = util.make_given_range_params(start, end_) + elseif mode == 'v' or mode == 'V' then + -- [bufnum, lnum, col, off]; both row and column 1-indexed + local start = vim.fn.getpos('v') + local end_ = vim.fn.getpos('.') + local start_row = start[2] + local start_col = start[3] + local end_row = end_[2] + local end_col = end_[3] + + -- A user can start visual selection at the end and move backwards + -- Normalize the range to start < end + if start_row == end_row and end_col < start_col then + end_col, start_col = start_col, end_col + elseif end_row < start_row then + start_row, end_row = end_row, start_row + start_col, end_col = end_col, start_col + end + params = util.make_given_range_params({ start_row, start_col - 1 }, { end_row, end_col - 1 }) + else + params = util.make_range_params() + end params.context = context code_action_request(params, options) end @@ -879,6 +925,7 @@ end ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_code_action(context, start_pos, end_pos) + vim.deprecate('vim.lsp.buf.range_code_action', 'vim.lsp.buf.code_action', '0.9.0') validate({ context = { context, 't', true } }) context = context or {} if not context.diagnostics then diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 3b869d8f5c..1e6ac8dddf 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -189,19 +189,17 @@ M['textDocument/references'] = function(_, result, ctx, config) else local client = vim.lsp.get_client_by_id(ctx.client_id) config = config or {} + local title = 'References' + local items = util.locations_to_items(result, client.offset_encoding) + if config.loclist then - vim.fn.setloclist(0, {}, ' ', { - title = 'References', - items = util.locations_to_items(result, client.offset_encoding), - context = ctx, - }) + vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) api.nvim_command('lopen') + elseif config.on_list then + assert(type(config.on_list) == 'function', 'on_list is not a function') + config.on_list({ title = title, items = items, context = ctx }) else - vim.fn.setqflist({}, ' ', { - title = 'References', - items = util.locations_to_items(result, client.offset_encoding), - context = ctx, - }) + vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) api.nvim_command('botright copen') end end @@ -224,19 +222,17 @@ local function response_to_list(map_result, entity, title_fn) vim.notify('No ' .. entity .. ' found') else config = config or {} + local title = title_fn(ctx) + local items = map_result(result, ctx.bufnr) + if config.loclist then - vim.fn.setloclist(0, {}, ' ', { - title = title_fn(ctx), - items = map_result(result, ctx.bufnr), - context = ctx, - }) + vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) api.nvim_command('lopen') + elseif config.on_list then + assert(type(config.on_list) == 'function', 'on_list is not a function') + config.on_list({ title = title, items = items, context = ctx }) else - vim.fn.setqflist({}, ' ', { - title = title_fn(ctx), - items = map_result(result, ctx.bufnr), - context = ctx, - }) + vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) api.nvim_command('botright copen') end end @@ -261,6 +257,7 @@ end) --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename M['textDocument/rename'] = function(_, result, ctx, _) if not result then + vim.notify("Language server couldn't provide rename result", vim.log.levels.INFO) return end local client = vim.lsp.get_client_by_id(ctx.client_id) @@ -354,11 +351,16 @@ local function location_handler(_, result, ctx, config) util.jump_to_location(result[1], client.offset_encoding, config.reuse_win) if #result > 1 then - vim.fn.setqflist({}, ' ', { - title = 'LSP locations', - items = util.locations_to_items(result, client.offset_encoding), - }) - api.nvim_command('botright copen') + local title = 'LSP locations' + local items = util.locations_to_items(result, client.offset_encoding) + + if config.on_list then + assert(type(config.on_list) == 'function', 'on_list is not a function') + config.on_list({ title = title, items = items }) + else + vim.fn.setqflist({}, ' ', { title = title, items = items }) + api.nvim_command('botright copen') + end end else util.jump_to_location(result, client.offset_encoding, config.reuse_win) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 6ecb9959d5..27da60b4ae 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -759,6 +759,7 @@ function protocol.make_client_capabilities() }, hierarchicalWorkspaceSymbolSupport = true, }, + configuration = true, workspaceFolders = true, applyEdit = true, workspaceEdit = { diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 70f5010256..8e89d92a56 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1821,7 +1821,7 @@ function M.try_trim_markdown_code_blocks(lines) end ---@private ----@param window (optional, number): window handle or 0 for current, defaults to current +---@param window number|nil: window handle or 0 for current, defaults to current ---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` local function make_position_param(window, offset_encoding) window = window or 0 @@ -1841,7 +1841,7 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ----@param window (optional, number): window handle or 0 for current, defaults to current +---@param window number|nil: window handle or 0 for current, defaults to current ---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams @@ -1894,8 +1894,8 @@ end --- `textDocument/codeAction`, `textDocument/colorPresentation`, --- `textDocument/rangeFormatting`. --- ----@param window (optional, number): window handle or 0 for current, defaults to current ----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` +---@param window number|nil: window handle or 0 for current, defaults to current +---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of buffer of `window` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params(window, offset_encoding) @@ -1911,12 +1911,12 @@ end --- Using the given range in the current buffer, creates an object that --- is similar to |vim.lsp.util.make_range_params()|. --- ----@param start_pos ({number, number}, optional) mark-indexed position. ----Defaults to the start of the last visual selection. ----@param end_pos ({number, number}, optional) mark-indexed position. ----Defaults to the end of the last visual selection. ----@param bufnr (optional, number): buffer handle or 0 for current, defaults to current ----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `bufnr` +---@param start_pos number[]|nil {row, col} mark-indexed position. +--- Defaults to the start of the last visual selection. +---@param end_pos number[]|nil {row, col} mark-indexed position. +--- Defaults to the end of the last visual selection. +---@param bufnr number|nil buffer handle or 0 for current, defaults to current +---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of `bufnr` ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) @@ -1956,7 +1956,7 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ----@param bufnr (optional, number): Buffer handle, defaults to current +---@param bufnr number|nil: Buffer handle, defaults to current ---@returns `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) @@ -2000,7 +2000,7 @@ end --- Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer. --- ----@param buf buffer id (0 for current) +---@param buf number buffer number (0 for current) ---@param row 0-indexed line ---@param col 0-indexed byte offset in line ---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf` diff --git a/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim b/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim index 6175d1d9a6..04b48b9ce8 100644 --- a/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim +++ b/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim @@ -7,88 +7,98 @@ " Attached is a Vim script file for turning gvim into a shell script editor. " It may also be used as an example how to use menus in Vim. " -" Written by: Lennart Schultz <les@dmi.min.dk> +" Maintainer: Ada (Haowen) Yu <me@yuhaowen.com> +" Original author: Lennart Schultz <les@dmi.min.dk> (mail unreachable) -imenu Stmts.for for in
do
doneki kk0elli -imenu Stmts.case case in
) ;;
esacbki k0elli -imenu Stmts.if if
then
fiki kk0elli -imenu Stmts.if-else if
then
else
fiki kki kk0elli -imenu Stmts.elif elif
then
ki kk0elli -imenu Stmts.while while
do
doneki kk0elli -imenu Stmts.break break -imenu Stmts.continue continue -imenu Stmts.function () {
}ki k0i -imenu Stmts.return return -imenu Stmts.return-true return 0 -imenu Stmts.return-false return 1 -imenu Stmts.exit exit -imenu Stmts.shift shift -imenu Stmts.trap trap -imenu Test.existence [ -e ]hi -imenu Test.existence - file [ -f ]hi -imenu Test.existence - file (not empty) [ -s ]hi -imenu Test.existence - directory [ -d ]hi -imenu Test.existence - executable [ -x ]hi -imenu Test.existence - readable [ -r ]hi -imenu Test.existence - writable [ -w ]hi -imenu Test.String is empty [ x = "x$" ]hhi -imenu Test.String is not empty [ x != "x$" ]hhi -imenu Test.Strings is equal [ "" = "" ]hhhhhhhi -imenu Test.Strings is not equal [ "" != "" ]hhhhhhhhi -imenu Test.Values is greater than [ -gt ]hhhhhhi -imenu Test.Values is greater equal [ -ge ]hhhhhhi -imenu Test.Values is equal [ -eq ]hhhhhhi -imenu Test.Values is not equal [ -ne ]hhhhhhi -imenu Test.Values is less than [ -lt ]hhhhhhi -imenu Test.Values is less equal [ -le ]hhhhhhi -imenu ParmSub.Substitute word if parm not set ${:-}hhi -imenu ParmSub.Set parm to word if not set ${:=}hhi -imenu ParmSub.Substitute word if parm set else nothing ${:+}hhi -imenu ParmSub.If parm not set print word and exit ${:?}hhi -imenu SpShVars.Number of positional parameters ${#} -imenu SpShVars.All positional parameters (quoted spaces) ${*} -imenu SpShVars.All positional parameters (unquoted spaces) ${@} -imenu SpShVars.Flags set ${-} -imenu SpShVars.Return code of last command ${?} -imenu SpShVars.Process number of this shell ${$} -imenu SpShVars.Process number of last background command ${!} -imenu Environ.HOME ${HOME} -imenu Environ.PATH ${PATH} -imenu Environ.CDPATH ${CDPATH} -imenu Environ.MAIL ${MAIL} -imenu Environ.MAILCHECK ${MAILCHECK} -imenu Environ.PS1 ${PS1} -imenu Environ.PS2 ${PS2} -imenu Environ.IFS ${IFS} -imenu Environ.SHACCT ${SHACCT} -imenu Environ.SHELL ${SHELL} -imenu Environ.LC_CTYPE ${LC_CTYPE} -imenu Environ.LC_MESSAGES ${LC_MESSAGES} -imenu Builtins.cd cd -imenu Builtins.echo echo -imenu Builtins.eval eval -imenu Builtins.exec exec -imenu Builtins.export export -imenu Builtins.getopts getopts -imenu Builtins.hash hash -imenu Builtins.newgrp newgrp -imenu Builtins.pwd pwd -imenu Builtins.read read -imenu Builtins.readonly readonly -imenu Builtins.return return -imenu Builtins.times times -imenu Builtins.type type -imenu Builtins.umask umask -imenu Builtins.wait wait -imenu Set.set set -imenu Set.unset unset -imenu Set.mark modified or modified variables set -a -imenu Set.exit when command returns non-zero exit code set -e -imenu Set.Disable file name generation set -f -imenu Set.remember function commands set -h -imenu Set.All keyword arguments are placed in the environment set -k -imenu Set.Read commands but do not execute them set -n -imenu Set.Exit after reading and executing one command set -t -imenu Set.Treat unset variables as an error when substituting set -u -imenu Set.Print shell input lines as they are read set -v -imenu Set.Print commands and their arguments as they are executed set -x +" Make sure the '<' and 'C' flags are not included in 'cpoptions', otherwise +" <CR> would not be recognized. See ":help 'cpoptions'". +let s:cpo_save = &cpo +set cpo&vim + +imenu ShellMenu.Statements.for for in <CR>do<CR><CR>done<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.case case in<CR>) ;;<CR>esac<esc>bki <esc>k0elli +imenu ShellMenu.Statements.if if <CR>then<CR><CR>fi<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.if-else if <CR>then<CR><CR>else<CR><CR>fi<esc>ki <esc>kki <esc>kk0elli +imenu ShellMenu.Statements.elif elif <CR>then<CR><CR><esc>ki <esc>kk0elli +imenu ShellMenu.Statements.while while do<CR><CR>done<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.break break +imenu ShellMenu.Statements.continue continue +imenu ShellMenu.Statements.function () {<CR><CR>}<esc>ki <esc>k0i +imenu ShellMenu.Statements.return return +imenu ShellMenu.Statements.return-true return 0 +imenu ShellMenu.Statements.return-false return 1 +imenu ShellMenu.Statements.exit exit +imenu ShellMenu.Statements.shift shift +imenu ShellMenu.Statements.trap trap +imenu ShellMenu.Test.Existence [ -e ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ file [ -f ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ file\ (not\ empty) [ -s ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ directory [ -d ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ executable [ -x ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ readable [ -r ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ writable [ -w ]<esc>hi +imenu ShellMenu.Test.String\ is\ empty [ x = "x$" ]<esc>hhi +imenu ShellMenu.Test.String\ is\ not\ empty [ x != "x$" ]<esc>hhi +imenu ShellMenu.Test.Strings\ are\ equal [ "" = "" ]<esc>hhhhhhhi +imenu ShellMenu.Test.Strings\ are\ not\ equal [ "" != "" ]<esc>hhhhhhhhi +imenu ShellMenu.Test.Value\ is\ greater\ than [ -gt ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ greater\ equal [ -ge ]<esc>hhhhhhi +imenu ShellMenu.Test.Values\ are\ equal [ -eq ]<esc>hhhhhhi +imenu ShellMenu.Test.Values\ are\ not\ equal [ -ne ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ less\ than [ -lt ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ less\ equal [ -le ]<esc>hhhhhhi +imenu ShellMenu.ParmSub.Substitute\ word\ if\ parm\ not\ set ${:-}<esc>hhi +imenu ShellMenu.ParmSub.Set\ parm\ to\ word\ if\ not\ set ${:=}<esc>hhi +imenu ShellMenu.ParmSub.Substitute\ word\ if\ parm\ set\ else\ nothing ${:+}<esc>hhi +imenu ShellMenu.ParmSub.If\ parm\ not\ set\ print\ word\ and\ exit ${:?}<esc>hhi +imenu ShellMenu.SpShVars.Number\ of\ positional\ parameters ${#} +imenu ShellMenu.SpShVars.All\ positional\ parameters\ (quoted\ spaces) ${*} +imenu ShellMenu.SpShVars.All\ positional\ parameters\ (unquoted\ spaces) ${@} +imenu ShellMenu.SpShVars.Flags\ set ${-} +imenu ShellMenu.SpShVars.Return\ code\ of\ last\ command ${?} +imenu ShellMenu.SpShVars.Process\ number\ of\ this\ shell ${$} +imenu ShellMenu.SpShVars.Process\ number\ of\ last\ background\ command ${!} +imenu ShellMenu.Environ.HOME ${HOME} +imenu ShellMenu.Environ.PATH ${PATH} +imenu ShellMenu.Environ.CDPATH ${CDPATH} +imenu ShellMenu.Environ.MAIL ${MAIL} +imenu ShellMenu.Environ.MAILCHECK ${MAILCHECK} +imenu ShellMenu.Environ.PS1 ${PS1} +imenu ShellMenu.Environ.PS2 ${PS2} +imenu ShellMenu.Environ.IFS ${IFS} +imenu ShellMenu.Environ.SHACCT ${SHACCT} +imenu ShellMenu.Environ.SHELL ${SHELL} +imenu ShellMenu.Environ.LC_CTYPE ${LC_CTYPE} +imenu ShellMenu.Environ.LC_MESSAGES ${LC_MESSAGES} +imenu ShellMenu.Builtins.cd cd +imenu ShellMenu.Builtins.echo echo +imenu ShellMenu.Builtins.eval eval +imenu ShellMenu.Builtins.exec exec +imenu ShellMenu.Builtins.export export +imenu ShellMenu.Builtins.getopts getopts +imenu ShellMenu.Builtins.hash hash +imenu ShellMenu.Builtins.newgrp newgrp +imenu ShellMenu.Builtins.pwd pwd +imenu ShellMenu.Builtins.read read +imenu ShellMenu.Builtins.readonly readonly +imenu ShellMenu.Builtins.return return +imenu ShellMenu.Builtins.times times +imenu ShellMenu.Builtins.type type +imenu ShellMenu.Builtins.umask umask +imenu ShellMenu.Builtins.wait wait +imenu ShellMenu.Set.set set +imenu ShellMenu.Set.unset unset +imenu ShellMenu.Set.Mark\ created\ or\ modified\ variables\ for\ export set -a +imenu ShellMenu.Set.Exit\ when\ command\ returns\ non-zero\ status set -e +imenu ShellMenu.Set.Disable\ file\ name\ expansion set -f +imenu ShellMenu.Set.Locate\ and\ remember\ commands\ when\ being\ looked\ up set -h +imenu ShellMenu.Set.All\ assignment\ statements\ are\ placed\ in\ the\ environment\ for\ a\ command set -k +imenu ShellMenu.Set.Read\ commands\ but\ do\ not\ execute\ them set -n +imenu ShellMenu.Set.Exit\ after\ reading\ and\ executing\ one\ command set -t +imenu ShellMenu.Set.Treat\ unset\ variables\ as\ an\ error\ when\ substituting set -u +imenu ShellMenu.Set.Print\ shell\ input\ lines\ as\ they\ are\ read set -v +imenu ShellMenu.Set.Print\ commands\ and\ their\ arguments\ as\ they\ are\ executed set -x + +" Restore the previous value of 'cpoptions'. +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/synmenu.vim b/runtime/synmenu.vim index d1e4becb2b..ba9e6a198d 100644 --- a/runtime/synmenu.vim +++ b/runtime/synmenu.vim @@ -18,6 +18,10 @@ fun! SetSyn(name) else let name = a:name endif + if a:name == "whitespace" + " do not replace the filetype but add whitespace on top + let name = &ft .. ".whitespace" + endif if !exists("s:syntax_menu_synonly") exe "set ft=" . name if exists("g:syntax_manual") diff --git a/runtime/syntax/autohotkey.vim b/runtime/syntax/autohotkey.vim index c6a68f7a21..a888394923 100644 --- a/runtime/syntax/autohotkey.vim +++ b/runtime/syntax/autohotkey.vim @@ -2,7 +2,7 @@ " Language: AutoHotkey script file " Maintainer: Michael Wong " https://github.com/mmikeww/autohotkey.vim -" Latest Revision: 2017-04-03 +" Latest Revision: 2022-07-25 " Previous Maintainers: SungHyun Nam <goweol@gmail.com> " Nikolai Weibull <now@bitwi.se> @@ -31,7 +31,7 @@ syn region autohotkeyString \ matchgroup=autohotkeyStringDelimiter \ start=+"+ \ end=+"+ - \ contains=autohotkeyEscape + \ contains=autohotkeyEscape,autohotkeyMatchClass syn match autohotkeyVariable \ display @@ -49,9 +49,9 @@ syn keyword autohotkeyBuiltinVariable \ A_Sec A_MSec A_Now A_NowUTC A_TickCount \ A_IsSuspended A_IsPaused A_IsCritical A_BatchLines A_TitleMatchMode A_TitleMatchModeSpeed \ A_DetectHiddenWindows A_DetectHiddenText A_AutoTrim A_StringCaseSense - \ A_FileEncoding A_FormatInteger A_FormatFloat A_KeyDelay A_WinDelay A_ControlDelay - \ A_SendMode A_SendLevel A_StoreCapsLockMode A_KeyDelay A_KeyDelayDuration - \ A_KeyDelayPlay A_KeyDelayPlayDuration A_MouseDelayPlay + \ A_FileEncoding A_FormatInteger A_FormatFloat A_WinDelay A_ControlDelay + \ A_SendMode A_SendLevel A_StoreCapsLockMode A_KeyDelay A_KeyDuration + \ A_KeyDelayPlay A_KeyDurationPlay A_MouseDelayPlay \ A_MouseDelay A_DefaultMouseSpeed A_RegView A_IconHidden A_IconTip A_IconFile \ A_CoordModeToolTip A_CoordModePixel A_CoordModeMouse A_CoordModeCaret A_CoordModeMenu \ A_IconNumber @@ -73,6 +73,7 @@ syn keyword autohotkeyBuiltinVariable \ A_LoopFileShortName A_LoopFileDir A_LoopFileTimeModified A_LoopFileTimeCreated \ A_LoopFileTimeAccessed A_LoopFileAttrib A_LoopFileSize A_LoopFileSizeKB A_LoopFileSizeMB \ A_LoopRegType A_LoopRegKey A_LoopRegSubKey A_LoopRegTimeModified + \ A_TimeIdleKeyboard A_TimeIdleMouse A_ListLines A_ComSpec A_LoopFilePath A_Args syn match autohotkeyBuiltinVariable \ contained @@ -118,6 +119,7 @@ syn keyword autohotkeyCommand \ WinMinimizeAll WinMinimizeAllUndo WinMove WinRestore WinSet \ WinSetTitle WinShow WinWait WinWaitActive WinWaitNotActive WinWaitClose \ SetCapsLockState SetNumLockState SetScrollLockState + \ Hotstring LoadPicture MenuGetHandle MenuGetName OnError OnClipboardChange syn keyword autohotkeyFunction \ InStr RegExMatch RegExReplace StrLen SubStr Asc Chr Func @@ -127,7 +129,7 @@ syn keyword autohotkeyFunction \ IsFunc Trim LTrim RTrim IsObject Object Array FileOpen \ ComObjActive ComObjArray ComObjConnect ComObjCreate ComObjGet \ ComObjError ComObjFlags ComObjQuery ComObjType ComObjValue ComObject - \ Format Exception + \ Format Exception Ord InputHook syn keyword autohotkeyStatement \ Break Continue Exit ExitApp Gosub Goto OnExit Pause Return @@ -140,7 +142,8 @@ syn keyword autohotkeyConditional \ IfExist IfNotExist If IfEqual IfLess IfGreater Else \ IfWinExist IfWinNotExist IfWinActive IfWinNotActive \ IfNotEqual IfLessOrEqual IfGreaterOrEqual - \ while until for in try catch finally + \ while until for in try catch finally not + \ switch case default syn match autohotkeyPreProcStart \ nextgroup= diff --git a/runtime/syntax/bitbake.vim b/runtime/syntax/bitbake.vim new file mode 100644 index 0000000000..30f34474ad --- /dev/null +++ b/runtime/syntax/bitbake.vim @@ -0,0 +1,126 @@ +" Vim syntax file +" Language: BitBake bb/bbclasses/inc +" Author: Chris Larson <kergoth@handhelds.org> +" Ricardo Salveti <rsalveti@rsalveti.net> +" Copyright: Copyright (C) 2004 Chris Larson <kergoth@handhelds.org> +" Copyright (C) 2008 Ricardo Salveti <rsalveti@rsalveti.net> +" +" This file is licensed under the MIT license, see COPYING.MIT in +" this source distribution for the terms. +" +" Syntax highlighting for bb, bbclasses and inc files. +" +" It's an entirely new type, just has specific syntax in shell and python code + +if v:version < 600 + finish +endif +if exists("b:current_syntax") + finish +endif + +syn include @python syntax/python.vim +unlet! b:current_syntax + +" BitBake syntax + +" Matching case +syn case match + +" Indicates the error when nothing is matched +syn match bbUnmatched "." + +" Comments +syn cluster bbCommentGroup contains=bbTodo,@Spell +syn keyword bbTodo COMBAK FIXME TODO XXX contained +syn match bbComment "#.*$" contains=@bbCommentGroup + +" String helpers +syn match bbQuote +['"]+ contained +syn match bbDelimiter "[(){}=]" contained +syn match bbArrayBrackets "[\[\]]" contained + +" BitBake strings +syn match bbContinue "\\$" +syn region bbString matchgroup=bbQuote start=+"+ skip=+\\$+ end=+"+ contained contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell +syn region bbString matchgroup=bbQuote start=+'+ skip=+\\$+ end=+'+ contained contains=bbTodo,bbContinue,bbVarDeref,bbVarPyValue,@Spell + +" Vars definition +syn match bbExport "^export" nextgroup=bbIdentifier skipwhite +syn keyword bbExportFlag export contained nextgroup=bbIdentifier skipwhite +syn match bbIdentifier "[a-zA-Z0-9\-_\.\/\+]\+" display contained +syn match bbVarDeref "${[a-zA-Z0-9\-_:\.\/\+]\+}" contained +syn match bbVarEq "\(:=\|+=\|=+\|\.=\|=\.\|?=\|??=\|=\)" contained nextgroup=bbVarValue +syn match bbVarDef "^\(export\s*\)\?\([a-zA-Z0-9\-_\.\/\+][${}a-zA-Z0-9\-_:\.\/\+]*\)\s*\(:=\|+=\|=+\|\.=\|=\.\|?=\|??=\|=\)\@=" contains=bbExportFlag,bbIdentifier,bbOverrideOperator,bbVarDeref nextgroup=bbVarEq +syn match bbVarValue ".*$" contained contains=bbString,bbVarDeref,bbVarPyValue +syn region bbVarPyValue start=+${@+ skip=+\\$+ end=+}+ contained contains=@python + +" Vars metadata flags +syn match bbVarFlagDef "^\([a-zA-Z0-9\-_\.]\+\)\(\[[a-zA-Z0-9\-_\.+]\+\]\)\@=" contains=bbIdentifier nextgroup=bbVarFlagFlag +syn region bbVarFlagFlag matchgroup=bbArrayBrackets start="\[" end="\]\s*\(:=\|=\|.=\|=.|+=\|=+\|?=\)\@=" contained contains=bbIdentifier nextgroup=bbVarEq + +" Includes and requires +syn keyword bbInclude inherit include require contained +syn match bbIncludeRest ".*$" contained contains=bbString,bbVarDeref +syn match bbIncludeLine "^\(inherit\|include\|require\)\s\+" contains=bbInclude nextgroup=bbIncludeRest + +" Add taks and similar +syn keyword bbStatement addtask deltask addhandler after before EXPORT_FUNCTIONS contained +syn match bbStatementRest ".*$" skipwhite contained contains=bbStatement +syn match bbStatementLine "^\(addtask\|deltask\|addhandler\|after\|before\|EXPORT_FUNCTIONS\)\s\+" contains=bbStatement nextgroup=bbStatementRest + +" OE Important Functions +syn keyword bbOEFunctions do_fetch do_unpack do_patch do_configure do_compile do_stage do_install do_package contained + +" Generic Functions +syn match bbFunction "\h[0-9A-Za-z_\-\.]*" display contained contains=bbOEFunctions + +syn keyword bbOverrideOperator append prepend remove contained + +" BitBake shell metadata +syn include @shell syntax/sh.vim +unlet! b:current_syntax + +syn keyword bbShFakeRootFlag fakeroot contained +syn match bbShFuncDef "^\(fakeroot\s*\)\?\([\.0-9A-Za-z_:${}\-\.]\+\)\(python\)\@<!\(\s*()\s*\)\({\)\@=" contains=bbShFakeRootFlag,bbFunction,bbOverrideOperator,bbVarDeref,bbDelimiter nextgroup=bbShFuncRegion skipwhite +syn region bbShFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" contained contains=@shell + +" Python value inside shell functions +syn region shDeref start=+${@+ skip=+\\$+ excludenl end=+}+ contained contains=@python + +" BitBake python metadata +syn keyword bbPyFlag python contained +syn match bbPyFuncDef "^\(fakeroot\s*\)\?\(python\)\(\s\+[0-9A-Za-z_:${}\-\.]\+\)\?\(\s*()\s*\)\({\)\@=" contains=bbShFakeRootFlag,bbPyFlag,bbFunction,bbOverrideOperator,bbVarDeref,bbDelimiter nextgroup=bbPyFuncRegion skipwhite +syn region bbPyFuncRegion matchgroup=bbDelimiter start="{\s*$" end="^}\s*$" contained contains=@python + +" BitBake 'def'd python functions +syn keyword bbPyDef def contained +syn region bbPyDefRegion start='^\(def\s\+\)\([0-9A-Za-z_-]\+\)\(\s*(.*)\s*\):\s*$' end='^\(\s\|$\)\@!' contains=@python + +" Highlighting Definitions +hi def link bbUnmatched Error +hi def link bbInclude Include +hi def link bbTodo Todo +hi def link bbComment Comment +hi def link bbQuote String +hi def link bbString String +hi def link bbDelimiter Keyword +hi def link bbArrayBrackets Statement +hi def link bbContinue Special +hi def link bbExport Type +hi def link bbExportFlag Type +hi def link bbIdentifier Identifier +hi def link bbVarDeref PreProc +hi def link bbVarDef Identifier +hi def link bbVarValue String +hi def link bbShFakeRootFlag Type +hi def link bbFunction Function +hi def link bbPyFlag Type +hi def link bbPyDef Statement +hi def link bbStatement Statement +hi def link bbStatementRest Identifier +hi def link bbOEFunctions Special +hi def link bbVarPyValue PreProc +hi def link bbOverrideOperator Operator + +let b:current_syntax = "bitbake" diff --git a/runtime/syntax/debchangelog.vim b/runtime/syntax/debchangelog.vim index 2efd919772..9bd836801e 100644 --- a/runtime/syntax/debchangelog.vim +++ b/runtime/syntax/debchangelog.vim @@ -3,7 +3,7 @@ " Maintainer: Debian Vim Maintainers " Former Maintainers: Gerfried Fuchs <alfie@ist.org> " Wichert Akkerman <wakkerma@debian.org> -" Last Change: 2022 May 01 +" Last Change: 2022 Jul 25 " URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debchangelog.vim " Standard syntax initialization @@ -20,22 +20,22 @@ let s:binNMU='binary-only=yes' let s:cpo = &cpo set cpo-=C let s:supported = [ - \ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', - \ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm', - \ 'trixie', 'sid', 'rc-buggy', + \ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy', + \ 'buster', 'bullseye', 'bookworm', 'trixie', \ - \ 'trusty', 'xenial', 'bionic', 'focal', 'impish', 'jammy', 'kinetic', + \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'kinetic', \ 'devel' \ ] let s:unsupported = [ \ 'frozen', 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato', \ 'woody', 'sarge', 'etch', 'lenny', 'squeeze', 'wheezy', + \ 'jessie', 'stretch', \ \ 'warty', 'hoary', 'breezy', 'dapper', 'edgy', 'feisty', \ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid', \ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy', \ 'utopic', 'vivid', 'wily', 'yakkety', 'zesty', 'artful', 'cosmic', - \ 'disco', 'eoan', 'hirsute', 'groovy' + \ 'disco', 'eoan', 'hirsute', 'impish', 'groovy' \ ] let &cpo=s:cpo diff --git a/runtime/syntax/debsources.vim b/runtime/syntax/debsources.vim index e3ec6e6598..ea9e59ea8e 100644 --- a/runtime/syntax/debsources.vim +++ b/runtime/syntax/debsources.vim @@ -2,7 +2,7 @@ " Language: Debian sources.list " Maintainer: Debian Vim Maintainers " Former Maintainer: Matthijs Mohlmann <matthijs@cacholong.nl> -" Last Change: 2022 May 01 +" Last Change: 2022 Jul 25 " URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debsources.vim " Standard syntax initialization @@ -22,22 +22,22 @@ syn match debsourcesComment /#.*/ contains=@Spell let s:cpo = &cpo set cpo-=C let s:supported = [ - \ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', - \ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm', - \ 'trixie', 'sid', 'rc-buggy', + \ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy', + \ 'buster', 'bullseye', 'bookworm', 'trixie', \ - \ 'trusty', 'xenial', 'bionic', 'focal', 'impish', 'jammy', 'kinetic', + \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'kinetic', \ 'devel' \ ] let s:unsupported = [ \ 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato', \ 'woody', 'sarge', 'etch', 'lenny', 'squeeze', 'wheezy', + \ 'jessie', 'stretch', \ \ 'warty', 'hoary', 'breezy', 'dapper', 'edgy', 'feisty', \ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid', \ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy', \ 'utopic', 'vivid', 'wily', 'yakkety', 'zesty', 'artful', 'cosmic', - \ 'disco', 'eoan', 'hirsute', 'groovy' + \ 'disco', 'eoan', 'hirsute', 'impish', 'groovy' \ ] let &cpo=s:cpo diff --git a/runtime/syntax/html.vim b/runtime/syntax/html.vim index 8ccb5574e7..9061bdee90 100644 --- a/runtime/syntax/html.vim +++ b/runtime/syntax/html.vim @@ -1,12 +1,9 @@ " Vim syntax file -" Language: HTML -" Previous Maintainer: Jorge Maldonado Ventura <jorgesumle@freakspot.net> -" Previous Maintainer: Claudio Fleiner <claudio@fleiner.com> -" Repository: https://notabug.org/jorgesumle/vim-html-syntax -" Last Change: 2021 Mar 02 -" Included patch #7900 to fix comments -" Included patch #7916 to fix a few more things -" +" Language: HTML +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainers: Jorge Maldonado Ventura <jorgesumle@freakspot.net> +" Claudio Fleiner <claudio@fleiner.com> +" Last Change: 2022 Jul 20 " Please check :help html.vim for some comments and a description of the options @@ -23,6 +20,9 @@ set cpo&vim syntax spell toplevel +syn include @htmlXml syntax/xml.vim +unlet b:current_syntax + syn case ignore " mark illegal characters @@ -30,13 +30,13 @@ syn match htmlError "[<>&]" " tags -syn region htmlString contained start=+"+ end=+"+ contains=htmlSpecialChar,javaScriptExpression,@htmlPreproc -syn region htmlString contained start=+'+ end=+'+ contains=htmlSpecialChar,javaScriptExpression,@htmlPreproc -syn match htmlValue contained "=[\t ]*[^'" \t>][^ \t>]*"hs=s+1 contains=javaScriptExpression,@htmlPreproc -syn region htmlEndTag start=+</+ end=+>+ contains=htmlTagN,htmlTagError -syn region htmlTag start=+<[^/]+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent,htmlCssDefinition,@htmlPreproc,@htmlArgCluster -syn match htmlTagN contained +<\s*[-a-zA-Z0-9]\++hs=s+1 contains=htmlTagName,htmlSpecialTagName,@htmlTagNameCluster -syn match htmlTagN contained +</\s*[-a-zA-Z0-9]\++hs=s+2 contains=htmlTagName,htmlSpecialTagName,@htmlTagNameCluster +syn region htmlString contained start=+"+ end=+"+ contains=htmlSpecialChar,javaScriptExpression,@htmlPreproc +syn region htmlString contained start=+'+ end=+'+ contains=htmlSpecialChar,javaScriptExpression,@htmlPreproc +syn match htmlValue contained "=[\t ]*[^'" \t>][^ \t>]*"hs=s+1 contains=javaScriptExpression,@htmlPreproc +syn region htmlEndTag start=+</+ end=+>+ contains=htmlTagN,htmlTagError +syn region htmlTag start=+<[^/]+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent,htmlCssDefinition,@htmlPreproc,@htmlArgCluster +syn match htmlTagN contained +<\s*[-a-zA-Z0-9]\++hs=s+1 contains=htmlTagName,htmlSpecialTagName,@htmlTagNameCluster +syn match htmlTagN contained +</\s*[-a-zA-Z0-9]\++hs=s+2 contains=htmlTagName,htmlSpecialTagName,@htmlTagNameCluster syn match htmlTagError contained "[^>]<"ms=s+1 @@ -47,13 +47,13 @@ syn keyword htmlTagName contained cite code dd dfn dir div dl dt font syn keyword htmlTagName contained form hr html img syn keyword htmlTagName contained input isindex kbd li link map menu syn keyword htmlTagName contained meta ol option param pre p samp span -syn keyword htmlTagName contained select small sub sup +syn keyword htmlTagName contained select small strike sub sup syn keyword htmlTagName contained table td textarea th tr tt ul var xmp -syn match htmlTagName contained "\<\(b\|i\|u\|h[1-6]\|em\|strong\|head\|body\|title\)\>" +syn match htmlTagName contained "\<\%(b\|i\|u\|h[1-6]\|em\|strong\|head\|body\|title\)\>" " new html 4.0 tags -syn keyword htmlTagName contained abbr acronym bdo button col label -syn keyword htmlTagName contained colgroup fieldset iframe ins legend +syn keyword htmlTagName contained abbr acronym bdo button col colgroup +syn keyword htmlTagName contained del fieldset iframe ins label legend syn keyword htmlTagName contained object optgroup q s tbody tfoot thead " new html 5 tags @@ -65,6 +65,15 @@ syn keyword htmlTagName contained progress rb rp rt rtc ruby section syn keyword htmlTagName contained slot source summary template time track syn keyword htmlTagName contained video wbr +" svg and math tags +syn keyword htmlMathTagName contained math +syn keyword htmlSvgTagName contained svg + +syn region htmlMath start="<math>" end="</math>" contains=@htmlXml transparent keepend +syn region htmlSvg start="<svg>" end="</svg>" contains=@htmlXml transparent keepend + +syn cluster xmlTagHook add=htmlMathTagName,htmlSvgTagName + " legal arg names syn keyword htmlArg contained action syn keyword htmlArg contained align alink alt archive background bgcolor @@ -77,7 +86,7 @@ syn keyword htmlArg contained marginwidth maxlength method name prompt syn keyword htmlArg contained rel rev rows rowspan scrolling selected shape syn keyword htmlArg contained size src start target text type url syn keyword htmlArg contained usemap ismap valign value vlink vspace width wrap -syn match htmlArg contained "\<\(http-equiv\|href\|title\)="me=e-1 +syn match htmlArg contained "\<\%(http-equiv\|href\|title\)="me=e-1 " aria attributes exe 'syn match htmlArg contained "\<aria-\%(' . join([ @@ -95,15 +104,15 @@ syn keyword htmlArg contained role " Netscape extensions syn keyword htmlTagName contained frame noframes frameset nobr blink syn keyword htmlTagName contained layer ilayer nolayer spacer -syn keyword htmlArg contained frameborder noresize pagex pagey above below -syn keyword htmlArg contained left top visibility clip id noshade -syn match htmlArg contained "\<z-index\>" +syn keyword htmlArg contained frameborder noresize pagex pagey above below +syn keyword htmlArg contained left top visibility clip id noshade +syn match htmlArg contained "\<z-index\>" " Microsoft extensions syn keyword htmlTagName contained marquee " html 4.0 arg names -syn match htmlArg contained "\<\(accept-charset\|label\)\>" +syn match htmlArg contained "\<\%(accept-charset\|label\)\>" syn keyword htmlArg contained abbr accept accesskey axis char charoff charset syn keyword htmlArg contained cite classid codetype compact data datetime syn keyword htmlArg contained declare defer dir disabled for frame @@ -113,51 +122,57 @@ syn keyword htmlArg contained rules scheme scope span standby style syn keyword htmlArg contained summary tabindex valuetype version " html 5 arg names -syn keyword htmlArg contained allowfullscreen async autocomplete autofocus -syn keyword htmlArg contained autoplay challenge contenteditable contextmenu -syn keyword htmlArg contained controls crossorigin default dirname download -syn keyword htmlArg contained draggable dropzone form formaction formenctype -syn keyword htmlArg contained formmethod formnovalidate formtarget hidden -syn keyword htmlArg contained high icon inputmode keytype kind list loop low -syn keyword htmlArg contained max min minlength muted nonce novalidate open -syn keyword htmlArg contained optimum pattern placeholder poster preload -syn keyword htmlArg contained radiogroup required reversed sandbox spellcheck -syn keyword htmlArg contained sizes srcset srcdoc srclang step title translate -syn keyword htmlArg contained typemustmatch +syn keyword htmlArg contained allow autocapitalize as blocking decoding +syn keyword htmlArg contained enterkeyhint imagesizes imagesrcset inert +syn keyword htmlArg contained integrity is itemid itemprop itemref itemscope +syn keyword htmlArg contained itemtype loading nomodule ping playsinline +syn keyword htmlArg contained referrerpolicy slot allowfullscreen async +syn keyword htmlArg contained autocomplete autofocus autoplay challenge +syn keyword htmlArg contained contenteditable contextmenu controls crossorigin +syn keyword htmlArg contained default dirname download draggable dropzone form +syn keyword htmlArg contained formaction formenctype formmethod formnovalidate +syn keyword htmlArg contained formtarget hidden high icon inputmode keytype +syn keyword htmlArg contained kind list loop low max min minlength muted nonce +syn keyword htmlArg contained novalidate open optimum pattern placeholder +syn keyword htmlArg contained poster preload radiogroup required reversed +syn keyword htmlArg contained sandbox spellcheck sizes srcset srcdoc srclang +syn keyword htmlArg contained step title translate typemustmatch +syn match htmlArg contained "\<data-\h\%(\w\|[-.]\)*\%(\_s*=\)\@=" " special characters syn match htmlSpecialChar "&#\=[0-9A-Za-z]\{1,8};" " Comments (the real ones or the old netscape ones) if exists("html_wrong_comments") - syn region htmlComment start=+<!--+ end=+--\s*>+ contains=@Spell + syn region htmlComment start=+<!--+ end=+--\s*>+ contains=@Spell else " The HTML 5.2 syntax 8.2.4.41: bogus comment is parser error; browser skips until next > - syn region htmlComment start=+<!+ end=+>+ contains=htmlCommentError keepend + syn region htmlComment start=+<!+ end=+>+ contains=htmlCommentError keepend " Idem 8.2.4.42,51: Comment starts with <!-- and ends with --> " Idem 8.2.4.43,44: Except <!--> and <!---> are parser errors " Idem 8.2.4.52: dash-dash-bang (--!>) is error ignored by parser, also closes comment - syn region htmlComment matchgroup=htmlComment start=+<!--\%(-\?>\)\@!+ end=+--!\?>+ contains=htmlCommentNested,@htmlPreProc,@Spell keepend + syn region htmlComment matchgroup=htmlComment start=+<!--\%(-\?>\)\@!+ end=+--!\?>+ contains=htmlCommentNested,@htmlPreProc,@Spell keepend " Idem 8.2.4.49: nested comment is parser error, except <!--> is all right syn match htmlCommentNested contained "<!-->\@!" syn match htmlCommentError contained "[^><!]" endif -syn region htmlComment start=+<!DOCTYPE+ end=+>+ keepend +syn region htmlComment start=+<!DOCTYPE+ end=+>+ keepend " server-parsed commands syn region htmlPreProc start=+<!--#+ end=+-->+ contains=htmlPreStmt,htmlPreError,htmlPreAttr -syn match htmlPreStmt contained "<!--#\(config\|echo\|exec\|fsize\|flastmod\|include\|printenv\|set\|if\|elif\|else\|endif\|geoguide\)\>" +syn match htmlPreStmt contained "<!--#\%(config\|echo\|exec\|fsize\|flastmod\|include\|printenv\|set\|if\|elif\|else\|endif\|geoguide\)\>" syn match htmlPreError contained "<!--#\S*"ms=s+4 syn match htmlPreAttr contained "\w\+=[^"]\S\+" contains=htmlPreProcAttrError,htmlPreProcAttrName syn region htmlPreAttr contained start=+\w\+="+ skip=+\\\\\|\\"+ end=+"+ contains=htmlPreProcAttrName keepend syn match htmlPreProcAttrError contained "\w\+="he=e-1 -syn match htmlPreProcAttrName contained "\(expr\|errmsg\|sizefmt\|timefmt\|var\|cgi\|cmd\|file\|virtual\|value\)="he=e-1 +syn match htmlPreProcAttrName contained "\%(expr\|errmsg\|sizefmt\|timefmt\|var\|cgi\|cmd\|file\|virtual\|value\)="he=e-1 if !exists("html_no_rendering") " rendering syn cluster htmlTop contains=@Spell,htmlTag,htmlEndTag,htmlSpecialChar,htmlPreProc,htmlComment,htmlLink,javaScript,@htmlPreproc syn region htmlStrike start="<del\>" end="</del\_s*>"me=s-1 contains=@htmlTop + syn region htmlStrike start="<s\>" end="</s\_s*>"me=s-1 contains=@htmlTop syn region htmlStrike start="<strike\>" end="</strike\_s*>"me=s-1 contains=@htmlTop syn region htmlBold start="<b\>" end="</b\_s*>"me=s-1 contains=@htmlTop,htmlBoldUnderline,htmlBoldItalic @@ -200,26 +215,26 @@ if !exists("html_no_rendering") syn region htmlTitle start="<title\>" end="</title\_s*>"me=s-1 contains=htmlTag,htmlEndTag,htmlSpecialChar,htmlPreProc,htmlComment,javaScript,@htmlPreproc endif -syn keyword htmlTagName contained noscript -syn keyword htmlSpecialTagName contained script style +syn keyword htmlTagName contained noscript +syn keyword htmlSpecialTagName contained script style if main_syntax != 'java' || exists("java_javascript") " JAVA SCRIPT syn include @htmlJavaScript syntax/javascript.vim unlet b:current_syntax syn region javaScript start=+<script\_[^>]*>+ keepend end=+</script\_[^>]*>+me=s-1 contains=@htmlJavaScript,htmlCssStyleComment,htmlScriptTag,@htmlPreproc - syn region htmlScriptTag contained start=+<script+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent + syn region htmlScriptTag contained start=+<script+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent hi def link htmlScriptTag htmlTag " html events (i.e. arguments that include javascript commands) if exists("html_extended_events") - syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*'+ end=+'+ contains=htmlEventSQ - syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*"+ end=+"+ contains=htmlEventDQ + syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*'+ end=+'+ contains=htmlEventSQ + syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*"+ end=+"+ contains=htmlEventDQ else - syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*'+ end=+'+ keepend contains=htmlEventSQ - syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*"+ end=+"+ keepend contains=htmlEventDQ + syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*'+ end=+'+ keepend contains=htmlEventSQ + syn region htmlEvent contained start=+\<on\a\+\s*=[\t ]*"+ end=+"+ keepend contains=htmlEventDQ endif - syn region htmlEventSQ contained start=+'+ms=s+1 end=+'+me=s-1 contains=@htmlJavaScript - syn region htmlEventDQ contained start=+"+ms=s+1 end=+"+me=s-1 contains=@htmlJavaScript + syn region htmlEventSQ contained start=+'+ms=s+1 end=+'+me=s-1 contains=@htmlJavaScript + syn region htmlEventDQ contained start=+"+ms=s+1 end=+"+me=s-1 contains=@htmlJavaScript hi def link htmlEventSQ htmlEvent hi def link htmlEventDQ htmlEvent @@ -234,15 +249,15 @@ if main_syntax != 'java' || exists("java_vb") syn region javaScript start=+<script \_[^>]*language *=\_[^>]*vbscript\_[^>]*>+ keepend end=+</script\_[^>]*>+me=s-1 contains=@htmlVbScript,htmlCssStyleComment,htmlScriptTag,@htmlPreproc endif -syn cluster htmlJavaScript add=@htmlPreproc +syn cluster htmlJavaScript add=@htmlPreproc if main_syntax != 'java' || exists("java_css") " embedded style sheets - syn keyword htmlArg contained media + syn keyword htmlArg contained media syn include @htmlCss syntax/css.vim unlet b:current_syntax syn region cssStyle start=+<style+ keepend end=+</style>+ contains=@htmlCss,htmlTag,htmlEndTag,htmlCssStyleComment,@htmlPreproc - syn match htmlCssStyleComment contained "\(<!--\|-->\)" + syn match htmlCssStyleComment contained "\%(<!--\|-->\)" syn region htmlCssDefinition matchgroup=htmlArg start='style="' keepend matchgroup=htmlString end='"' contains=css.*Attr,css.*Prop,cssComment,cssLength,cssColor,cssURL,cssImportant,cssError,cssString,@htmlPreproc hi def link htmlStyleArg htmlString endif @@ -258,68 +273,70 @@ if main_syntax == "html" endif " The default highlighting. -hi def link htmlTag Function -hi def link htmlEndTag Identifier -hi def link htmlArg Type -hi def link htmlTagName htmlStatement -hi def link htmlSpecialTagName Exception -hi def link htmlValue String -hi def link htmlSpecialChar Special +hi def link htmlTag Function +hi def link htmlEndTag Identifier +hi def link htmlArg Type +hi def link htmlTagName htmlStatement +hi def link htmlSpecialTagName Exception +hi def link htmlMathTagName htmlTagName +hi def link htmlSvgTagName htmlTagName +hi def link htmlValue String +hi def link htmlSpecialChar Special if !exists("html_no_rendering") - hi def link htmlH1 Title - hi def link htmlH2 htmlH1 - hi def link htmlH3 htmlH2 - hi def link htmlH4 htmlH3 - hi def link htmlH5 htmlH4 - hi def link htmlH6 htmlH5 - hi def link htmlHead PreProc - hi def link htmlTitle Title - hi def link htmlBoldItalicUnderline htmlBoldUnderlineItalic - hi def link htmlUnderlineBold htmlBoldUnderline - hi def link htmlUnderlineItalicBold htmlBoldUnderlineItalic - hi def link htmlUnderlineBoldItalic htmlBoldUnderlineItalic - hi def link htmlItalicUnderline htmlUnderlineItalic - hi def link htmlItalicBold htmlBoldItalic - hi def link htmlItalicBoldUnderline htmlBoldUnderlineItalic - hi def link htmlItalicUnderlineBold htmlBoldUnderlineItalic - hi def link htmlLink Underlined - hi def link htmlLeadingSpace None + hi def link htmlH1 Title + hi def link htmlH2 htmlH1 + hi def link htmlH3 htmlH2 + hi def link htmlH4 htmlH3 + hi def link htmlH5 htmlH4 + hi def link htmlH6 htmlH5 + hi def link htmlHead PreProc + hi def link htmlTitle Title + hi def link htmlBoldItalicUnderline htmlBoldUnderlineItalic + hi def link htmlUnderlineBold htmlBoldUnderline + hi def link htmlUnderlineItalicBold htmlBoldUnderlineItalic + hi def link htmlUnderlineBoldItalic htmlBoldUnderlineItalic + hi def link htmlItalicUnderline htmlUnderlineItalic + hi def link htmlItalicBold htmlBoldItalic + hi def link htmlItalicBoldUnderline htmlBoldUnderlineItalic + hi def link htmlItalicUnderlineBold htmlBoldUnderlineItalic + hi def link htmlLink Underlined + hi def link htmlLeadingSpace None if !exists("html_my_rendering") - hi def htmlBold term=bold cterm=bold gui=bold - hi def htmlBoldUnderline term=bold,underline cterm=bold,underline gui=bold,underline - hi def htmlBoldItalic term=bold,italic cterm=bold,italic gui=bold,italic + hi def htmlBold term=bold cterm=bold gui=bold + hi def htmlBoldUnderline term=bold,underline cterm=bold,underline gui=bold,underline + hi def htmlBoldItalic term=bold,italic cterm=bold,italic gui=bold,italic hi def htmlBoldUnderlineItalic term=bold,italic,underline cterm=bold,italic,underline gui=bold,italic,underline - hi def htmlUnderline term=underline cterm=underline gui=underline - hi def htmlUnderlineItalic term=italic,underline cterm=italic,underline gui=italic,underline - hi def htmlItalic term=italic cterm=italic gui=italic + hi def htmlUnderline term=underline cterm=underline gui=underline + hi def htmlUnderlineItalic term=italic,underline cterm=italic,underline gui=italic,underline + hi def htmlItalic term=italic cterm=italic gui=italic if v:version > 800 || v:version == 800 && has("patch1038") - hi def htmlStrike term=strikethrough cterm=strikethrough gui=strikethrough + hi def htmlStrike term=strikethrough cterm=strikethrough gui=strikethrough else - hi def htmlStrike term=underline cterm=underline gui=underline + hi def htmlStrike term=underline cterm=underline gui=underline endif endif endif -hi def link htmlPreStmt PreProc -hi def link htmlPreError Error -hi def link htmlPreProc PreProc -hi def link htmlPreAttr String +hi def link htmlPreStmt PreProc +hi def link htmlPreError Error +hi def link htmlPreProc PreProc +hi def link htmlPreAttr String hi def link htmlPreProcAttrName PreProc hi def link htmlPreProcAttrError Error -hi def link htmlString String -hi def link htmlStatement Statement -hi def link htmlComment Comment -hi def link htmlCommentNested htmlError -hi def link htmlCommentError htmlError -hi def link htmlTagError htmlError -hi def link htmlEvent javaScript -hi def link htmlError Error - -hi def link javaScript Special +hi def link htmlString String +hi def link htmlStatement Statement +hi def link htmlComment Comment +hi def link htmlCommentNested htmlError +hi def link htmlCommentError htmlError +hi def link htmlTagError htmlError +hi def link htmlEvent javaScript +hi def link htmlError Error + +hi def link javaScript Special hi def link javaScriptExpression javaScript hi def link htmlCssStyleComment Comment -hi def link htmlCssDefinition Special +hi def link htmlCssDefinition Special let b:current_syntax = "html" diff --git a/runtime/syntax/i3config.vim b/runtime/syntax/i3config.vim index 0018081da7..caef244ce5 100644 --- a/runtime/syntax/i3config.vim +++ b/runtime/syntax/i3config.vim @@ -17,6 +17,9 @@ endif scriptencoding utf-8 +" Error +syn match i3ConfigError /.*/ + " Todo syn keyword i3ConfigTodo TODO FIXME XXX contained @@ -54,8 +57,8 @@ syn match i3ConfigInclude /^\s*include\s\+.*$/ contains=i3ConfigIncludeKeyword,i " Gaps syn keyword i3ConfigGapStyleKeyword inner outer horizontal vertical top right bottom left current all set plus minus toggle up down contained syn match i3ConfigGapStyle /^\s*\(gaps\)\s\+\(inner\|outer\|horizontal\|vertical\|left\|top\|right\|bottom\)\(\s\+\(current\|all\)\)\?\(\s\+\(set\|plus\|minus\|toggle\)\)\?\(\s\+\(-\?\d\+\|\$.*\)\)$/ contains=i3ConfigGapStyleKeyword,i3ConfigNumber,i3ConfigVariable -syn keyword i3ConfigSmartGapKeyword on inverse_outer contained -syn match i3ConfigSmartGap /^\s*smart_gaps\s\+\(on\|inverse_outer\)\s\?$/ contains=i3ConfigSmartGapKeyword +syn keyword i3ConfigSmartGapKeyword on inverse_outer off contained +syn match i3ConfigSmartGap /^\s*smart_gaps\s\+\(on\|inverse_outer\|off\)\s\?$/ contains=i3ConfigSmartGapKeyword syn keyword i3ConfigSmartBorderKeyword on no_gaps contained syn match i3ConfigSmartBorder /^\s*smart_borders\s\+\(on\|no_gaps\)\s\?$/ contains=i3ConfigSmartBorderKeyword @@ -74,7 +77,7 @@ syn match i3ConfigBind /^\s*\(bindsym\|bindcode\)\s\+.*$/ contains=i3ConfigVaria syn keyword i3ConfigSizeSpecial x contained syn match i3ConfigNegativeSize /-/ contained syn match i3ConfigSize /-\?\d\+\s\?x\s\?-\?\d\+/ contained contains=i3ConfigSizeSpecial,i3ConfigNumber,i3ConfigNegativeSize -syn match i3ConfigFloating /^\s*floating_modifier\s\+\$\w\+\d\?/ contains=i3ConfigVariable +syn match i3ConfigFloatingModifier /^\s*floating_modifier\s\+\$\w\+\d\?/ contains=i3ConfigVariable syn match i3ConfigFloating /^\s*floating_\(maximum\|minimum\)_size\s\+-\?\d\+\s\?x\s\?-\?\d\+/ contains=i3ConfigSize " Orientation @@ -183,6 +186,7 @@ syn region i3ConfigBlock start=+^\s*[^#]*s\?{$+ end=+^\s*[^#]*}$+ contains=i3Con syn region i3ConfigLineCont start=/^.*\\$/ end=/^.*$/ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend " Define the highlighting. +hi def link i3ConfigError Error hi def link i3ConfigTodo Todo hi def link i3ConfigComment Comment hi def link i3ConfigFontContent Type @@ -213,6 +217,7 @@ hi def link i3ConfigTimeUnit Constant hi def link i3ConfigModifier Constant hi def link i3ConfigString Constant hi def link i3ConfigNegativeSize Constant +hi def link i3ConfigInclude Constant hi def link i3ConfigFontSeparator Special hi def link i3ConfigVariableModifier Special hi def link i3ConfigSizeSpecial Special @@ -233,6 +238,7 @@ hi def link i3ConfigLayout Identifier hi def link i3ConfigBorderStyle Identifier hi def link i3ConfigEdge Identifier hi def link i3ConfigFloating Identifier +hi def link i3ConfigFloatingModifier Identifier hi def link i3ConfigCommandKeyword Identifier hi def link i3ConfigNoFocusKeyword Identifier hi def link i3ConfigInitializeKeyword Identifier diff --git a/runtime/syntax/make.vim b/runtime/syntax/make.vim index d0d7f1523b..68f7ee21ea 100644 --- a/runtime/syntax/make.vim +++ b/runtime/syntax/make.vim @@ -3,7 +3,7 @@ " Maintainer: Roland Hieber <rohieb+vim-iR0jGdkV@rohieb.name>, <https://github.com/rohieb> " Previous Maintainer: Claudio Fleiner <claudio@fleiner.com> " URL: https://github.com/vim/vim/blob/master/runtime/syntax/make.vim -" Last Change: 2020 May 03 +" Last Change: 2020 Oct 16 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -45,19 +45,19 @@ syn match makeImplicit "^\.[A-Za-z0-9_./\t -]\+\s*:$"me=e-1 syn match makeImplicit "^\.[A-Za-z0-9_./\t -]\+\s*:[^=]"me=e-2 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="[^\\]$" + \ start="^[~A-Za-z0-9_./$()%-][A-Za-z0-9_./\t $()%-]*&\?:\?:\{1,2}[^:=]"rs=e-1 + \ end="[^\\]$" \ keepend contains=makeIdent,makeSpecTarget,makeNextLine,makeComment,makeDString \ skipnl nextGroup=makeCommands -syn match makeTarget "^[~A-Za-z0-9_./$()%*@-][A-Za-z0-9_./\t $()%*@-]*::\=\s*$" +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 + \ start="^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\|ONESHELL\)\>\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*$" +syn match makeSpecTarget "^\.\(SUFFIXES\|PHONY\|DEFAULT\|PRECIOUS\|IGNORE\|SILENT\|EXPORT_ALL_VARIABLES\|KEEP_STATE\|LIBPATTERNS\|NOTPARALLEL\|DELETE_ON_ERROR\|INTERMEDIATE\|POSIX\|SECONDARY\|ONESHELL\)\>\s*::\=\s*$" \ contains=makeIdent,makeComment \ skipnl nextgroup=makeCommands,makeCommandError diff --git a/runtime/syntax/python.vim b/runtime/syntax/python.vim index 2293163a5b..ef4da1b448 100644 --- a/runtime/syntax/python.vim +++ b/runtime/syntax/python.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Python " Maintainer: Zvezdan Petkovic <zpetkovic@acm.org> -" Last Change: 2021 Dec 10 +" Last Change: 2022 Jun 28 " Credits: Neil Schemenauer <nas@python.ca> " Dmitry Vasiliev " @@ -84,13 +84,19 @@ syn keyword pythonStatement as assert break continue del global syn keyword pythonStatement lambda nonlocal pass return with yield syn keyword pythonStatement class def nextgroup=pythonFunction skipwhite syn keyword pythonConditional elif else if -syn keyword pythonConditional case match 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 +" Soft keywords +" These keywords do not mean anything unless used in the right context +" See https://docs.python.org/3/reference/lexical_analysis.html#soft-keywords +" for more on this. +syn match pythonConditional "^\s*\zscase\%(\s\+.*:.*$\)\@=" +syn match pythonConditional "^\s*\zsmatch\%(\s\+.*:\s*\%(#.*\)\=$\)\@=" + " Decorators " A dot must be allowed because of @MyClass.myfunc decorators. syn match pythonDecorator "@" display contained diff --git a/runtime/syntax/swayconfig.vim b/runtime/syntax/swayconfig.vim new file mode 100644 index 0000000000..2abfa38bd9 --- /dev/null +++ b/runtime/syntax/swayconfig.vim @@ -0,0 +1,92 @@ +" Vim syntax file +" Language: sway window manager config +" Original Author: James Eapen <james.eapen@vai.org> +" Maintainer: James Eapen <james.eapen@vai.org> +" Version: 0.11.0 +" Last Change: 2022 Jun 07 + +" References: +" http://i3wm.org/docs/userguide.html#configuring +" https://github.com/swaywm/sway/blob/b69d637f7a34e239e48a4267ae94a5e7087b5834/sway/sway.5.scd +" http://vimdoc.sourceforge.net/htmldoc/syntax.html +" +" +" Quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +runtime! syntax/i3config.vim + +scriptencoding utf-8 + +" Error +"syn match swayConfigError /.*/ + +" Group mode/bar +syn keyword swayConfigBlockKeyword set input contained +syn region swayConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,swayConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend + +" binding +syn keyword swayConfigBindKeyword bindswitch bindgesture contained +syn match swayConfigBind /^\s*\(bindswitch\)\s\+.*$/ contains=i3ConfigVariable,i3ConfigBindKeyword,swayConfigBindKeyword,i3ConfigVariableAndModifier,i3ConfigNumber,i3ConfigUnit,i3ConfigUnitOr,i3ConfigBindArgument,i3ConfigModifier,i3ConfigAction,i3ConfigString,i3ConfigGapStyleKeyword,i3ConfigBorderStyleKeyword + +" bindgestures +syn keyword swayConfigBindGestureCommand swipe pinch hold contained +syn keyword swayConfigBindGestureDirection up down left right next prev contained +syn keyword swayConfigBindGesturePinchDirection inward outward clockwise counterclockwise contained +syn match swayConfigBindGestureHold /^\s*\(bindgesture\)\s\+hold\(:[1-5]\)\?\s\+.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,i3ConfigWorkspaceKeyword,i3ConfigAction +syn match swayConfigBindGestureSwipe /^\s*\(bindgesture\)\s\+swipe\(:[1-5]\)\?:\(up\|down\|left\|right\)\s\+.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,i3ConfigWorkspaceKeyword,i3ConfigAction +syn match swayConfigBindGesturePinch /^\s*\(bindgesture\)\s\+\(pinch\):.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,swayConfigBindGesturePinchDirection,i3ConfigWorkspaceKeyword,i3ConfigAction + +" floating +syn keyword swayConfigFloatingKeyword floating contained +syn match swayConfigFloating /^\s*floating\s\+\(enable\|disable\|toggle\)\s*$/ contains=swayConfigFloatingKeyword + +syn clear i3ConfigFloatingModifier +syn keyword swayConfigFloatingModifier floating_modifier contained +syn match swayConfigFloatingMouseAction /^\s\?.*floating_modifier\s.*\(normal\|inverted\)$/ contains=swayConfigFloatingModifier,i3ConfigVariable + +" Gaps +syn clear i3ConfigSmartBorderKeyword +syn clear i3ConfigSmartBorder +syn keyword swayConfigSmartBorderKeyword on no_gaps off contained +syn match swayConfigSmartBorder /^\s*smart_borders\s\+\(on\|no_gaps\|off\)\s\?$/ contains=swayConfigSmartBorderKeyword + +" Changing colors +syn keyword swayConfigClientColorKeyword focused_tab_title contained +syn match swayConfigClientColor /^\s*client.\w\+\s\+.*$/ contains=i3ConfigClientColorKeyword,i3ConfigColor,i3ConfigVariable,i3ConfigClientColorKeyword,swayConfigClientColorKeyword + +" set display outputs +syn match swayConfigOutput /^\s*output\s\+.*$/ contains=i3ConfigOutput + +" set display focus +syn keyword swayConfigFocusKeyword focus contained +syn keyword swayConfigFocusType output contained +syn match swayConfigFocus /^\s*focus\soutput\s.*$/ contains=swayConfigFocusKeyword,swayConfigFocusType + +" xwayland +syn keyword swayConfigXwaylandKeyword xwayland contained +syn match swayConfigXwaylandModifier /^\s*xwayland\s\+\(enable\|disable\|force\)\s\?$/ contains=swayConfigXwaylandKeyword + +"hi def link swayConfigError Error +hi def link i3ConfigFloating Error +hi def link swayConfigFloating Type +hi def link swayConfigFloatingMouseAction Type +hi def link swayConfigFocusKeyword Type +hi def link swayConfigSmartBorderKeyword Type +hi def link swayConfigBindGestureCommand Identifier +hi def link swayConfigBindGestureDirection Constant +hi def link swayConfigBindGesturePinchDirection Constant +hi def link swayConfigBindKeyword Identifier +hi def link swayConfigBlockKeyword Identifier +hi def link swayConfigClientColorKeyword Identifier +hi def link swayConfigFloatingKeyword Identifier +hi def link swayConfigFloatingModifier Identifier +hi def link swayConfigFocusType Identifier +hi def link swayConfigSmartBorder Identifier +hi def link swayConfigXwaylandKeyword Identifier +hi def link swayConfigXwaylandModifier Type +hi def link swayConfigBindGesture PreProc + +let b:current_syntax = "swayconfig" diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 22fd155d32..c17742ddaf 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -897,6 +897,8 @@ def fmt_doxygen_xml_as_vimhelp(filename, target): doc = fmt_node_as_vimhelp(fn['desc_node'], fmt_vimhelp=True) if not doc and fn['brief_desc_node']: doc = fmt_node_as_vimhelp(fn['brief_desc_node']) + if not doc and name.startswith("nvim__"): + continue if not doc: doc = 'TODO: Documentation' @@ -947,7 +949,8 @@ def fmt_doxygen_xml_as_vimhelp(filename, target): func_doc = "\n".join(split_lines) - if name.startswith(CONFIG[target]['fn_name_prefix']): + if (name.startswith(CONFIG[target]['fn_name_prefix']) + and name != "nvim_error_event"): fns_txt[name] = func_doc return ('\n\n'.join(list(fns_txt.values())), diff --git a/scripts/lua2dox_filter b/scripts/lua2dox_filter index 8760f12176..22484a807f 100755 --- a/scripts/lua2dox_filter +++ b/scripts/lua2dox_filter @@ -36,22 +36,14 @@ test_executable(){ ##! \brief sets the lua interpreter set_lua(){ - if test -z "${EXE}" - then + if test -z "${EXE}"; then test_executable 'luajit' fi - if test -z "${EXE}" - then - test_executable 'texlua' - fi - - if test -z "${EXE}" - then + if test -z "${EXE}"; then test_executable 'lua' fi - #echo "final EXE=\"${EXE}\"" - } +} ##! \brief makes canonical name of file ##! diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index a931231fbd..610c20eb48 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -380,7 +380,7 @@ run_analysis() {( --sourcetree-root . || true rm -rf PVS-studio.{xml,err,tsk,html.d} - local plog_args="PVS-studio.log --srcRoot . --excludedCodes V011,V1042,V1051,V1074,V002" + local plog_args="PVS-studio.log --srcRoot . --excludedCodes V002,V011,V1028,V1042,V1051,V1074" plog-converter $plog_args --renderTypes xml --output PVS-studio.xml plog-converter $plog_args --renderTypes errorfile --output PVS-studio.err plog-converter $plog_args --renderTypes tasklist --output PVS-studio.tsk diff --git a/scripts/uncrustify.sh b/scripts/uncrustify.sh deleted file mode 100755 index ac5d542c29..0000000000 --- a/scripts/uncrustify.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# Check that you have uncrustify -hash uncrustify - -COMMITISH="${1:-master}" -for file in $(git diff --diff-filter=d --name-only $COMMITISH | grep '\.[ch]$'); do - uncrustify -c src/uncrustify.cfg -l C --replace --no-backup "$file" -done diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index d5424f51ab..3825d9f0ea 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -229,6 +229,10 @@ preprocess_patch() { LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/evalfunc\.c/\1\/eval\/funcs\.c/g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename evalvars.c to eval/vars.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/evalvars\.c/\1\/eval\/vars\.c/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename userfunc.c to eval/userfunc.c LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/userfunc\.c/\1\/eval\/userfunc\.c/g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 360993de68..017883a913 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -156,34 +156,10 @@ endforeach() list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) -# Legacy files that do not yet pass -Wconversion. -set(CONV_SOURCES - lua/treesitter.c - mbyte.c - memline.c - regexp.c - screen.c - search.c - spell.c - spellfile.c - syntax.c - window.c) -foreach(sfile ${CONV_SOURCES}) - if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}") - message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") - endif() -endforeach() - if(NOT MSVC) - set_source_files_properties( - ${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") - # xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306 set_source_files_properties( ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion") - - set_source_files_properties( - eval/funcs.c PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") endif() if(NOT "${MIN_LOG_LEVEL}" MATCHES "^$") @@ -230,6 +206,7 @@ add_custom_target(update_version_stamp -DNVIM_VERSION_PATCH=${NVIM_VERSION_PATCH} -DNVIM_VERSION_PRERELEASE=${NVIM_VERSION_PRERELEASE} -DOUTPUT=${NVIM_VERSION_GIT_H} + -DCMAKE_MESSAGE_LOG_LEVEL=${CMAKE_MESSAGE_LOG_LEVEL} -P ${PROJECT_SOURCE_DIR}/cmake/GenerateVersion.cmake WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} BYPRODUCTS ${NVIM_VERSION_GIT_H}) @@ -268,7 +245,7 @@ foreach(sfile ${NVIM_SOURCES} set(depends "${HEADER_GENERATOR}" "${sfile}") if("${f}" STREQUAL "version.c") # Ensure auto/versiondef_git.h exists after "make clean". - list(APPEND depends "${NVIM_VERSION_GIT_H}") + list(APPEND depends update_version_stamp "${NVIM_VERSION_GIT_H}") endif() add_custom_command( OUTPUT "${gf_c_h}" "${gf_h_h}" @@ -789,12 +766,27 @@ foreach(sfile ${LINT_NVIM_SOURCES}) endforeach() add_custom_target(lintc DEPENDS ${LINT_TARGETS}) +add_custom_target(uncrustify-version + COMMAND ${CMAKE_COMMAND} + -D UNCRUSTIFY_PRG=${UNCRUSTIFY_PRG} + -D CONFIG_FILE=${PROJECT_SOURCE_DIR}/src/uncrustify.cfg + -P ${PROJECT_SOURCE_DIR}/cmake/CheckUncrustifyVersion.cmake) + add_glob_targets( TARGET lintuncrustify COMMAND ${UNCRUSTIFY_PRG} FLAGS -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check FILES ${LINT_NVIM_SOURCES} ) +add_dependencies(lintuncrustify uncrustify-version) + +add_custom_target(formatc + COMMAND ${CMAKE_COMMAND} + -D FORMAT_PRG=${UNCRUSTIFY_PRG} + -D LANG=c + -P ${PROJECT_SOURCE_DIR}/cmake/Format.cmake + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) +add_dependencies(formatc uncrustify-version) add_custom_target( lintcfull diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 806b649ce6..1b1a161226 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -538,9 +538,9 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In Integer end_row, Integer end_col, ArrayOf(String) replacement, Error *err) FUNC_API_SINCE(7) { - FIXED_TEMP_ARRAY(scratch, 1); + MAXSIZE_TEMP_ARRAY(scratch, 1); if (replacement.size == 0) { - scratch.items[0] = STRING_OBJ(STATIC_CSTR_AS_STRING("")); + ADD_C(scratch, STRING_OBJ(STATIC_CSTR_AS_STRING(""))); replacement = scratch; } @@ -932,7 +932,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// @param mode Mode short-name ("n", "i", "v", ...) /// @param buffer Buffer handle, or 0 for current buffer /// @param[out] err Error details, if any -/// @returns Array of maparg()-like dictionaries describing mappings. +/// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key holds the associated buffer handle. ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, String mode, Error *err) FUNC_API_SINCE(3) diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 4c2404a0d8..33efa6b326 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -306,7 +306,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error char *cmdline = NULL; char *cmdname = NULL; - char **args = NULL; + ArrayOf(String) args; size_t argc = 0; String retv = (String)STRING_INIT; @@ -412,22 +412,15 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (!argc_valid) { - argc = 0; // Ensure that args array isn't erroneously freed at the end. VALIDATION_ERROR("Incorrect number of arguments supplied"); } - if (argc != 0) { - args = xcalloc(argc, sizeof(char *)); - - for (size_t i = 0; i < argc; i++) { - args[i] = string_to_cstr(cmd->args.data.array.items[i].data.string); - } - } + args = cmd->args.data.array; } // Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()` // since it only ever checks the first argument. - set_cmd_addr_type(&ea, argc > 0 ? args[0] : NULL); + set_cmd_addr_type(&ea, argc > 0 ? args.items[0].data.string.data : NULL); if (HAS_KEY(cmd->range)) { if (!(ea.argt & EX_RANGE)) { @@ -600,7 +593,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error OBJ_TO_CMOD_FLAG(CMOD_SILENT, mods.silent, false, "'mods.silent'"); OBJ_TO_CMOD_FLAG(CMOD_ERRSILENT, mods.emsg_silent, false, "'mods.emsg_silent'"); - OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods.silent, false, "'mods.unsilent'"); + OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods.unsilent, false, "'mods.unsilent'"); OBJ_TO_CMOD_FLAG(CMOD_SANDBOX, mods.sandbox, false, "'mods.sandbox'"); OBJ_TO_CMOD_FLAG(CMOD_NOAUTOCMD, mods.noautocmd, false, "'mods.noautocmd'"); OBJ_TO_CMOD_FLAG(CMOD_BROWSE, mods.browse, false, "'mods.browse'"); @@ -676,10 +669,6 @@ end: xfree(cmdname); xfree(ea.args); xfree(ea.arglens); - for (size_t i = 0; i < argc; i++) { - xfree(args[i]); - } - xfree(args); return retv; @@ -704,12 +693,11 @@ static bool string_iswhite(String str) } /// Build cmdline string for command, used by `nvim_cmd()`. -/// -/// @return OK or FAIL. -static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args, - size_t argc) +static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, + ArrayOf(String) args, size_t argc) { StringBuilder cmdline = KV_INITIAL_VALUE; + kv_resize(cmdline, 32); // Make it big enough to handle most typical commands // Add command modifiers if (cmdinfo->cmdmod.cmod_tab != 0) { @@ -779,11 +767,11 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin // Keep the index of the position where command name starts, so eap->cmd can point to it. size_t cmdname_idx = cmdline.size; - kv_printf(cmdline, "%s", eap->cmd); + kv_concat(cmdline, eap->cmd); // Command bang. if (eap->argt & EX_BANG && eap->forceit) { - kv_printf(cmdline, "!"); + kv_concat(cmdline, "!"); } // Command register. @@ -791,29 +779,35 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin kv_printf(cmdline, " %c", eap->regname); } - // Iterate through each argument and store the starting index and length of each argument - size_t *argidx = xcalloc(argc, sizeof(size_t)); eap->argc = argc; eap->arglens = xcalloc(argc, sizeof(size_t)); + size_t argstart_idx = cmdline.size; for (size_t i = 0; i < argc; i++) { - argidx[i] = cmdline.size + 1; // add 1 to account for the space. - eap->arglens[i] = STRLEN(args[i]); - kv_printf(cmdline, " %s", args[i]); + String s = args.items[i].data.string; + eap->arglens[i] = s.size; + kv_concat(cmdline, " "); + kv_concat_len(cmdline, s.data, s.size); } + // Done appending to cmdline, ensure it is NUL terminated + kv_push(cmdline, NUL); + // Now that all the arguments are appended, use the command index and argument indices to set the // values of eap->cmd, eap->arg and eap->args. eap->cmd = cmdline.items + cmdname_idx; eap->args = xcalloc(argc, sizeof(char *)); + size_t offset = argstart_idx; for (size_t i = 0; i < argc; i++) { - eap->args[i] = cmdline.items + argidx[i]; + offset++; // Account for space + eap->args[i] = cmdline.items + offset; + offset += eap->arglens[i]; } // If there isn't an argument, make eap->arg point to end of cmdline. - eap->arg = argc > 0 ? eap->args[0] : cmdline.items + cmdline.size; + eap->arg = argc > 0 ? eap->args[0] : + cmdline.items + cmdline.size - 1; // Subtract 1 to account for NUL // Finally, make cmdlinep point to the cmdline string. *cmdlinep = cmdline.items; - xfree(argidx); // Replace, :make and :grep with 'makeprg' and 'grepprg'. char *p = replace_makeprg(eap, eap->arg, cmdlinep); diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 1f1fa1e63a..a764fb069b 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -39,6 +39,7 @@ return { "unique"; "callback"; "desc"; + "replace_keycodes"; }; get_commands = { "builtin"; diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 4ed676e613..867584dd71 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -104,20 +104,20 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) long numval = 0; char *stringval = NULL; - int result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, from, - true, err); + getoption_T result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, + from, true, err); if (ERROR_SET(err)) { return rv; } switch (result) { - case 0: + case gov_string: rv = STRING_OBJ(cstr_as_string(stringval)); break; - case 1: + case gov_number: rv = INTEGER_OBJ(numval); break; - case 2: + case gov_bool: switch (numval) { case 0: case 1: @@ -280,7 +280,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err) return get_option_from(buf, SREQ_BUF, name, err); } -/// Sets a buffer option value. Passing 'nil' as value deletes the option (only +/// Sets a buffer option value. Passing `nil` as value deletes the option (only /// works if there's a global fallback) /// /// @param channel_id @@ -318,7 +318,7 @@ Object nvim_win_get_option(Window window, String name, Error *err) return get_option_from(win, SREQ_WIN, name, err); } -/// Sets a window option value. Passing 'nil' as value deletes the option(only +/// Sets a window option value. Passing `nil` as value deletes the option (only /// works if there's a global fallback) /// /// @param channel_id @@ -338,7 +338,7 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object set_option_to(channel_id, win, SREQ_WIN, name, value, err); } -/// Gets the value of a global or local(buffer, window) option. +/// Gets the value of a global or local (buffer, window) option. /// /// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer /// to the window or buffer. @@ -393,7 +393,7 @@ Object get_option_from(void *from, int type, String name, Error *err) return rv; } -/// Sets the value of a global or local(buffer, window) option. +/// Sets the value of a global or local (buffer, window) option. /// /// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer /// to the window or buffer. @@ -483,8 +483,8 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object }); } -static int access_option_value(char *key, long *numval, char **stringval, int opt_flags, bool get, - Error *err) +static getoption_T access_option_value(char *key, long *numval, char **stringval, int opt_flags, + bool get, Error *err) { if (get) { return get_option_value(key, numval, stringval, opt_flags); @@ -501,13 +501,13 @@ static int access_option_value(char *key, long *numval, char **stringval, int op } } -static int access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, - int opt_type, void *from, bool get, Error *err) +static getoption_T access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, + int opt_type, void *from, bool get, Error *err) { bool need_switch = false; switchwin_T switchwin; aco_save_T aco; - int result = 0; + getoption_T result = 0; try_start(); switch (opt_type) { diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b1e0dd364c..9c7e59e4b3 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -36,6 +36,7 @@ typedef enum { kMessageTypeRequest = 0, kMessageTypeResponse = 1, kMessageTypeNotification = 2, + kMessageTypeRedrawEvent = 3, } MessageType; /// Mask for all internal calls diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index a4348d8b44..1441da853c 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -74,18 +74,18 @@ #define ADD_C(array, item) \ kv_push_c(array, item) -#define FIXED_TEMP_ARRAY(name, fixsize) \ - Array name = ARRAY_DICT_INIT; \ - Object name##__items[fixsize]; \ - name.size = fixsize; \ - name.items = name##__items; \ - #define MAXSIZE_TEMP_ARRAY(name, maxsize) \ Array name = ARRAY_DICT_INIT; \ Object name##__items[maxsize]; \ name.capacity = maxsize; \ name.items = name##__items; \ +#define MAXSIZE_TEMP_DICT(name, maxsize) \ + Dictionary name = ARRAY_DICT_INIT; \ + KeyValuePair name##__items[maxsize]; \ + name.capacity = maxsize; \ + name.items = name##__items; \ + #define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 }) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 54ce838b9b..6239e414a7 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -27,7 +27,7 @@ typedef struct { uint64_t channel_id; #define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data. - /// guranteed size available for each new event (so packing of simple events + /// guaranteed size available for each new event (so packing of simple events /// and the header of grid_line will never fail) #define EVENT_BUF_SIZE 256 char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data @@ -43,7 +43,7 @@ typedef struct { // We start packing the two outermost msgpack arrays before knowing the total // number of elements. Thus track the location where array size will need - // to be written in the msgpack buffer, once the specifc array is finished. + // to be written in the msgpack buffer, once the specific array is finished. char *nevents_pos; char *ncalls_pos; uint32_t nevents; ///< number of distinct events (top-level args to "redraw" @@ -748,8 +748,10 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt UIData *data = ui->data; Array args = data->call_buf; ADD_C(args, INTEGER_OBJ(id)); - ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); - ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); + MAXSIZE_TEMP_DICT(rgb, 16); + MAXSIZE_TEMP_DICT(cterm, 16); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&rgb, rgb_attrs, true))); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&cterm, cterm_attrs, false))); if (ui->ui_ext[kUIHlState]) { ADD_C(args, ARRAY_OBJ(info)); @@ -758,9 +760,6 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt } push_call(ui, "hl_attr_define", args); - // TODO(bfredl): could be elided - api_free_dictionary(kv_A(args, 1).data.dictionary); - api_free_dictionary(kv_A(args, 2).data.dictionary); } static void remote_ui_highlight_set(UI *ui, int id) @@ -772,11 +771,9 @@ static void remote_ui_highlight_set(UI *ui, int id) return; } data->hl_id = id; - Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb); - - ADD_C(args, DICTIONARY_OBJ(hl)); + MAXSIZE_TEMP_DICT(dict, 16); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&dict, syn_attr2entry(id), ui->rgb))); push_call(ui, "highlight_set", args); - api_free_dictionary(kv_A(args, 0).data.dictionary); } /// "true" cursor used only for input focus @@ -963,7 +960,7 @@ static Array translate_contents(UI *ui, Array contents) Array new_item = ARRAY_DICT_INIT; int attr = (int)item.items[0].data.integer; if (attr) { - Dictionary rgb_attrs = hlattrs2dict(syn_attr2entry(attr), ui->rgb); + Dictionary rgb_attrs = hlattrs2dict(NULL, syn_attr2entry(attr), ui->rgb); ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); } else { ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 56516b2ac7..e2f58dba62 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -37,6 +37,7 @@ #include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" #include "nvim/mark.h" @@ -472,10 +473,10 @@ Object nvim_exec_lua(String code, Array args, Error *err) Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) FUNC_API_SINCE(7) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = STRING_OBJ(msg); - args.items[1] = INTEGER_OBJ(log_level); - args.items[2] = DICTIONARY_OBJ(opts); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, STRING_OBJ(msg)); + ADD_C(args, INTEGER_OBJ(log_level)); + ADD_C(args, DICTIONARY_OBJ(opts)); return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err); } @@ -1009,10 +1010,10 @@ static void term_write(char *buf, size_t size, void *data) if (cb == LUA_NOREF) { return; } - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = INTEGER_OBJ((Integer)chan->id); - args.items[1] = BUFFER_OBJ(terminal_buf(chan->term)); - args.items[2] = STRING_OBJ(((String){ .data = buf, .size = size })); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, INTEGER_OBJ((Integer)chan->id)); + ADD_C(args, BUFFER_OBJ(terminal_buf(chan->term))); + ADD_C(args, STRING_OBJ(((String){ .data = buf, .size = size }))); textlock++; nlua_call_ref(cb, "input", args, false, NULL); textlock--; @@ -1410,7 +1411,7 @@ Dictionary nvim_get_mode(void) /// Gets a list of global (non-buffer-local) |mapping| definitions. /// /// @param mode Mode short-name ("n", "i", "v", ...) -/// @returns Array of maparg()-like dictionaries describing mappings. +/// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key is always zero. ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) FUNC_API_SINCE(3) @@ -1422,8 +1423,8 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) /// /// To set a buffer-local mapping, use |nvim_buf_set_keymap()|. /// -/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} -/// or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual. +/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} or {rhs}. +/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual. /// /// Example: /// <pre> @@ -1440,13 +1441,15 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) /// or "!" for |:map!|, or empty string for |:map|. /// @param lhs Left-hand-side |{lhs}| of the mapping. /// @param rhs Right-hand-side |{rhs}| of the mapping. -/// @param opts Optional parameters map: keys are |:map-arguments|, values -/// are booleans (default false). Accepts all |:map-arguments| as -/// keys excluding |<buffer>| but including |noremap| and "desc". -/// Unknown key is an error. "desc" can be used to give a -/// description to the mapping. When called from Lua, also accepts a -/// "callback" key that takes a Lua function to call when the -/// mapping is executed. +/// @param opts Optional parameters map: keys are |:map-arguments|, values are booleans (default +/// false). Accepts all |:map-arguments| as keys excluding |<buffer>| but including +/// |noremap| and "desc". Unknown key is an error. +/// "desc" can be used to give a description to the mapping. +/// When called from Lua, also accepts a "callback" key that takes a Lua function to +/// call when the mapping is executed. +/// When "expr" is true, "replace_keycodes" (boolean) can be used to replace keycodes +/// in the resulting string (see |nvim_replace_termcodes()|), and a Lua callback +/// returning `nil` is equivalent to returning an empty string. /// @param[out] err Error details, if any. void nvim_set_keymap(uint64_t channel_id, String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err) @@ -2256,3 +2259,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * return result; } + +void nvim_error_event(uint64_t channel_id, Integer lvl, String data) + FUNC_API_REMOTE_ONLY +{ + // TODO(bfredl): consider printing message to user, as will be relevant + // if we fork nvim processes as async workers + ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : ""); +} diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index d51079b515..2b4c9c5b9c 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -14,10 +14,12 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/map.h" #include "nvim/option.h" @@ -1835,9 +1837,13 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force } ap->last = true; + // Make sure cursor and topline are valid. The first time the current + // values are saved, restored by reset_lnums(). When nested only the + // values are corrected when needed. if (nesting == 1) { - // make sure cursor and topline are valid check_lnums(true); + } else { + check_lnums_nested(true); } // Execute the autocmd. The `getnextac` callback handles iteration. @@ -2051,8 +2057,8 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc) break; } - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = DICTIONARY_OBJ(data); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, DICTIONARY_OBJ(data)); Object result = nlua_call_ref(callback.data.luaref, NULL, args, true, NULL); if (result.type == kObjectTypeBoolean) { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f937450107..6dd71e92a6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -37,6 +37,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" @@ -2482,6 +2483,9 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T wip->wi_mark.view = mark_view_make(win->w_topline, wip->wi_mark.mark); } } + if (win != NULL) { + wip->wi_changelistidx = win->w_changelistidx; + } if (copy_options && win != NULL) { // Save the window-specific option values. copy_winopt(&win->w_onebuf_opt, &wip->wi_opt); @@ -2585,6 +2589,9 @@ void get_winopts(buf_T *buf) } else { copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt); } + if (wip != NULL) { + curwin->w_changelistidx = wip->wi_changelistidx; + } if (curwin->w_float_config.style == kWinStyleMinimal) { didset_window_options(curwin); @@ -3281,7 +3288,7 @@ void maketitle(void) len = (int)STRLEN(buf_p); if (len > 100) { len -= 100; - len += mb_tail_off(buf_p, buf_p + len) + 1; + len += utf_cp_tail_off(buf_p, buf_p + len) + 1; buf_p += len; } STRCPY(icon_str, buf_p); @@ -3463,7 +3470,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san // Proceed character by character through the statusline format string // fmt_p is the current position in the input buffer - for (char *fmt_p = usefmt; *fmt_p;) { + for (char *fmt_p = usefmt; *fmt_p != NUL;) { if (curitem == (int)stl_items_len) { size_t new_len = stl_items_len * 3 / 2; @@ -3477,7 +3484,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san stl_items_len = new_len; } - if (*fmt_p != NUL && *fmt_p != '%') { + if (*fmt_p != '%') { prevchar_isflag = prevchar_isitem = false; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 4e890f7d10..b7f66e6dba 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -289,6 +289,7 @@ struct wininfo_S { winopt_T wi_opt; // local window options bool wi_fold_manual; // copy of w_fold_manual garray_T wi_folds; // clone of w_folds + int wi_changelistidx; // copy of w_changelistidx }; /* @@ -368,6 +369,7 @@ struct mapblock { char m_expr; // <expr> used, m_str is an expression sctx_T m_script_ctx; // SCTX where map was defined char *m_desc; // description of keymap + bool m_replace_keycodes; // replace termcodes in lua function }; /// Used for highlighting in the status line. @@ -444,11 +446,11 @@ typedef struct { // "containedin" argument int b_syn_sync_flags; // flags about how to sync int16_t b_syn_sync_id; // group to sync on - long b_syn_sync_minlines; // minimal sync lines offset - long b_syn_sync_maxlines; // maximal sync lines offset - long b_syn_sync_linebreaks; // offset for multi-line pattern - char_u *b_syn_linecont_pat; // line continuation pattern - regprog_T *b_syn_linecont_prog; // line continuation program + linenr_T b_syn_sync_minlines; // minimal sync lines offset + linenr_T b_syn_sync_maxlines; // maximal sync lines offset + linenr_T b_syn_sync_linebreaks; // offset for multi-line pattern + char_u *b_syn_linecont_pat; // line continuation pattern + regprog_T *b_syn_linecont_prog; // line continuation program syn_time_T b_syn_linecont_time; int b_syn_linecont_ic; // ignore-case flag for above int b_syn_topgrp; // for ":syntax include" @@ -582,9 +584,9 @@ struct file_buffer { linenr_T b_mod_top; // topmost lnum that was changed linenr_T b_mod_bot; // lnum below last changed line, AFTER the // change - long b_mod_xlines; // number of extra buffer lines inserted; + linenr_T b_mod_xlines; // number of extra buffer lines inserted; // negative when lines were deleted - wininfo_T *b_wininfo; // list of last used info for each window + wininfo_T *b_wininfo; // list of last used info for each window disptick_T b_mod_tick_syn; // last display tick syntax was updated disptick_T b_mod_tick_decor; // last display tick decoration providers // where invoked @@ -946,16 +948,15 @@ struct diffblock_S { typedef struct tabpage_S tabpage_T; struct tabpage_S { handle_T handle; - tabpage_T *tp_next; ///< next tabpage or NULL - frame_T *tp_topframe; ///< topframe for the windows - win_T *tp_curwin; ///< current window in this Tab page - win_T *tp_prevwin; ///< previous window in this Tab page - win_T *tp_firstwin; ///< first window in this Tab page - win_T *tp_lastwin; ///< last window in this Tab page - long tp_old_Rows; ///< Rows when Tab page was left - long tp_old_Columns; ///< Columns when Tab page was left - long tp_ch_used; ///< value of 'cmdheight' when frame size - ///< was set + tabpage_T *tp_next; ///< next tabpage or NULL + frame_T *tp_topframe; ///< topframe for the windows + win_T *tp_curwin; ///< current window in this Tab page + win_T *tp_prevwin; ///< previous window in this Tab page + win_T *tp_firstwin; ///< first window in this Tab page + win_T *tp_lastwin; ///< last window in this Tab page + long tp_old_Rows_avail; ///< ROWS_AVAIL when Tab page was left + long tp_old_Columns; ///< Columns when Tab page was left + long tp_ch_used; ///< value of 'cmdheight' when frame size was set diff_T *tp_first_diff; buf_T *(tp_diffbuf[DB_COUNT]); diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index 47b88945c7..14973502ab 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -316,23 +316,23 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; if (cb.on_bytes != LUA_NOREF && (cb.preview || !cmdpreview)) { - FIXED_TEMP_ARRAY(args, 11); + MAXSIZE_TEMP_ARRAY(args, 11); // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); + ADD_C(args, BUFFER_OBJ(buf->handle)); // next argument is b:changedtick - args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf)); - - args.items[2] = INTEGER_OBJ(start_row); - args.items[3] = INTEGER_OBJ(start_col); - args.items[4] = INTEGER_OBJ(start_byte); - args.items[5] = INTEGER_OBJ(old_row); - args.items[6] = INTEGER_OBJ(old_col); - args.items[7] = INTEGER_OBJ(old_byte); - args.items[8] = INTEGER_OBJ(new_row); - args.items[9] = INTEGER_OBJ(new_col); - args.items[10] = INTEGER_OBJ(new_byte); + ADD_C(args, INTEGER_OBJ(buf_get_changedtick(buf))); + + ADD_C(args, INTEGER_OBJ(start_row)); + ADD_C(args, INTEGER_OBJ(start_col)); + ADD_C(args, INTEGER_OBJ(start_byte)); + ADD_C(args, INTEGER_OBJ(old_row)); + ADD_C(args, INTEGER_OBJ(old_col)); + ADD_C(args, INTEGER_OBJ(old_byte)); + ADD_C(args, INTEGER_OBJ(new_row)); + ADD_C(args, INTEGER_OBJ(new_col)); + ADD_C(args, INTEGER_OBJ(new_byte)); textlock++; Object res = nlua_call_ref(cb.on_bytes, "bytes", args, true, NULL); @@ -361,13 +361,13 @@ void buf_updates_changedtick(buf_T *buf) BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; if (cb.on_changedtick != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 2); + MAXSIZE_TEMP_ARRAY(args, 2); // the first argument is always the buffer handle - args.items[0] = BUFFER_OBJ(buf->handle); + ADD_C(args, BUFFER_OBJ(buf->handle)); // next argument is b:changedtick - args.items[1] = INTEGER_OBJ(buf_get_changedtick(buf)); + ADD_C(args, INTEGER_OBJ(buf_get_changedtick(buf))); textlock++; Object res = nlua_call_ref(cb.on_changedtick, "changedtick", diff --git a/src/nvim/change.c b/src/nvim/change.c index 4568b71fd9..c063ece907 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -17,6 +17,7 @@ #include "nvim/fold.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/move.h" @@ -966,10 +967,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) int extra_len = 0; // length of p_extra string int lead_len; // length of comment leader int comment_start = 0; // start index of the comment leader - char_u *lead_flags; // position in 'comments' for comment leader - char_u *leader = NULL; // copy of comment leader + char *lead_flags; // position in 'comments' for comment leader + char *leader = NULL; // copy of comment leader char_u *allocated = NULL; // allocated memory - char_u *p; + char *p; char_u saved_char = NUL; // init for GCC pos_T *pos; bool do_si = may_do_si(); @@ -1007,9 +1008,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // autoindent etc) a bit later. replace_push(NUL); // Call twice because BS over NL expects it replace_push(NUL); - p = saved_line + curwin->w_cursor.col; + p = (char *)saved_line + curwin->w_cursor.col; while (*p != NUL) { - p += replace_push_mb(p); + p += replace_push_mb((char_u *)p); } saved_line[curwin->w_cursor.col] = NUL; } @@ -1017,8 +1018,8 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if ((State & MODE_INSERT) && (State & VREPLACE_FLAG) == 0) { p_extra = saved_line + curwin->w_cursor.col; if (do_si) { // need first char after new line break - p = (char_u *)skipwhite((char *)p_extra); - first_char = *p; + p = skipwhite((char *)p_extra); + first_char = (unsigned char)(*p); } extra_len = (int)STRLEN(p_extra); saved_char = *p_extra; @@ -1054,12 +1055,12 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // "if (condition) {" if (!trunc_line && do_si && *saved_line != NUL && (p_extra == NULL || first_char != '{')) { - char_u *ptr; + char *ptr; old_cursor = curwin->w_cursor; - ptr = saved_line; + ptr = (char *)saved_line; if (flags & OPENLINE_DO_COM) { - lead_len = get_leader_len((char *)ptr, NULL, false, true); + lead_len = get_leader_len(ptr, NULL, false, true); } else { lead_len = 0; } @@ -1067,12 +1068,12 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // 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); + ptr = (char *)ml_get(--curwin->w_cursor.lnum); } newindent = get_indent(); } if (flags & OPENLINE_DO_COM) { - lead_len = get_leader_len((char *)ptr, NULL, false, true); + lead_len = get_leader_len(ptr, NULL, false, true); } else { lead_len = 0; } @@ -1083,7 +1084,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // */ // #define IN_THE_WAY // This should line up here; - p = (char_u *)skipwhite((char *)ptr); + p = skipwhite(ptr); if (p[0] == '/' && p[1] == '*') { p++; } @@ -1107,7 +1108,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) while (p > ptr && ascii_iswhite(*p)) { p--; } - char_u last_char = *p; + char last_char = *p; // find the character just before the '{' or ';' if (last_char == '{' || last_char == ';') { @@ -1129,7 +1130,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if ((pos = findmatch(NULL, '(')) != NULL) { curwin->w_cursor.lnum = pos->lnum; newindent = get_indent(); - ptr = get_cursor_line_ptr(); + ptr = (char *)get_cursor_line_ptr(); } } // If last character is '{' do indent, without @@ -1141,7 +1142,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Don't do this if the previous line ended in ';' or // '}'. } else if (last_char != ';' && last_char != '}' - && cin_is_cinword(ptr)) { + && cin_is_cinword((char_u *)ptr)) { did_si = true; } } @@ -1158,7 +1159,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } else { was_backslashed = false; } - ptr = ml_get(++curwin->w_cursor.lnum); + ptr = (char *)ml_get(++curwin->w_cursor.lnum); } if (was_backslashed) { newindent = 0; // Got to end of file @@ -1166,7 +1167,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) newindent = get_indent(); } } - p = (char_u *)skipwhite((char *)ptr); + p = skipwhite(ptr); if (*p == '}') { // if line starts with '}': do indent did_si = true; } else { // can delete indent when '{' typed @@ -1191,14 +1192,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // This may then be inserted in front of the new line. end_comment_pending = NUL; if (flags & OPENLINE_DO_COM) { - lead_len = get_leader_len((char *)saved_line, (char **)&lead_flags, dir == BACKWARD, true); + lead_len = get_leader_len((char *)saved_line, &lead_flags, dir == BACKWARD, true); if (lead_len == 0 && curbuf->b_p_cin && do_cindent && dir == FORWARD && (!has_format_option(FO_NO_OPEN_COMS) || (flags & OPENLINE_FORMAT))) { // Check for a line comment after code. comment_start = check_linecomment(saved_line); if (comment_start != MAXCOL) { - lead_len = get_leader_len((char *)saved_line + comment_start, - (char **)&lead_flags, false, true); + lead_len = get_leader_len((char *)saved_line + comment_start, &lead_flags, false, true); if (lead_len != 0) { lead_len += comment_start; if (did_do_comment != NULL) { @@ -1211,11 +1211,11 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) lead_len = 0; } if (lead_len > 0) { - char_u *lead_repl = NULL; // replaces comment leader + char *lead_repl = NULL; // replaces comment leader int lead_repl_len = 0; // length of *lead_repl char_u lead_middle[COM_MAX_LEN]; // middle-comment string char_u lead_end[COM_MAX_LEN]; // end-comment string - char_u *comment_end = NULL; // where lead_end has been found + char_u *comment_end = NULL; // where lead_end has been found int extra_space = false; // append extra space int current_flag; int require_blank = false; // requires blank after middle @@ -1229,7 +1229,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) continue; } if (*p == COM_START || *p == COM_MIDDLE) { - current_flag = *p; + current_flag = (unsigned char)(*p); if (*p == COM_START) { // Doing "O" on a start of comment does not insert leader. if (dir == BACKWARD) { @@ -1238,7 +1238,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } // find start of middle part - (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ","); + (void)copy_option_part(&p, (char *)lead_middle, COM_MAX_LEN, ","); require_blank = false; } @@ -1249,7 +1249,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } p++; } - (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ","); + (void)copy_option_part(&p, (char *)lead_middle, COM_MAX_LEN, ","); while (*p && p[-1] != ':') { // find end of end flags // Check whether we allow automatic ending of comments @@ -1258,7 +1258,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } p++; } - size_t n = copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ","); + size_t n = copy_option_part(&p, (char *)lead_end, COM_MAX_LEN, ","); if (end_comment_pending == -1) { // we can set it now end_comment_pending = lead_end[n - 1]; @@ -1267,9 +1267,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // If the end of the comment is in the same line, don't use // the comment leader. if (dir == FORWARD) { - for (p = saved_line + lead_len; *p; p++) { + for (p = (char *)saved_line + lead_len; *p; p++) { if (STRNCMP(p, lead_end, n) == 0) { - comment_end = p; + comment_end = (char_u *)p; lead_len = 0; break; } @@ -1279,7 +1279,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Doing "o" on a start of comment inserts the middle leader. if (lead_len > 0) { if (current_flag == COM_START) { - lead_repl = lead_middle; + lead_repl = (char *)lead_middle; lead_repl_len = (int)STRLEN(lead_middle); } @@ -1309,10 +1309,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Doing "O" on the end of a comment inserts the middle leader. // Find the string for the middle leader, searching backwards. - while (p > curbuf->b_p_com && *p != ',') { + while (p > (char *)curbuf->b_p_com && *p != ',') { p--; } - for (lead_repl = p; lead_repl > curbuf->b_p_com + for (lead_repl = p; lead_repl > (char *)curbuf->b_p_com && lead_repl[-1] != ':'; lead_repl--) {} lead_repl_len = (int)(p - lead_repl); @@ -1321,7 +1321,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) extra_space = true; // Check whether we allow automatic ending of comments - for (p2 = p; *p2 && *p2 != ':'; p2++) { + for (p2 = (char_u *)p; *p2 && *p2 != ':'; p2++) { if (*p2 == COM_AUTO_END) { end_comment_pending = -1; // means we want to set it } @@ -1341,7 +1341,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if (dir == BACKWARD) { lead_len = 0; } else { - lead_repl = (char_u *)""; + lead_repl = ""; lead_repl_len = 0; } break; @@ -1357,7 +1357,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) + 1; assert(bytes >= 0); leader = xmalloc((size_t)bytes); - allocated = leader; // remember to free it later + allocated = (char_u *)leader; // remember to free it later STRLCPY(leader, saved_line, lead_len + 1); @@ -1375,9 +1375,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) for (p = lead_flags; *p != NUL && *p != ':';) { if (*p == COM_RIGHT || *p == COM_LEFT) { - c = *p++; + c = (unsigned char)(*p++); } else if (ascii_isdigit(*p) || *p == '-') { - off = getdigits_int((char **)&p, true, 0); + off = getdigits_int(&p, true, 0); } else { p++; } @@ -1391,15 +1391,14 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Compute the length of the replaced characters in // screen characters, not bytes. { - int repl_size = vim_strnsize(lead_repl, - lead_repl_len); + int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len); int old_size = 0; - char_u *endp = p; + char *endp = p; int l; while (old_size < repl_size && p > leader) { MB_PTR_BACK(leader, p); - old_size += ptr2cells((char *)p); + old_size += ptr2cells(p); } l = lead_repl_len - (int)(endp - p); if (l != 0) { @@ -1415,11 +1414,11 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // blank-out any other chars from the old leader. while (--p >= leader) { - int l = utf_head_off(leader, p); + int l = utf_head_off((char_u *)leader, (char_u *)p); if (l > 1) { p -= l; - if (ptr2cells((char *)p) > 1) { + if (ptr2cells(p) > 1) { p[1] = ' '; l--; } @@ -1432,19 +1431,18 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } } } else { // left adjusted leader - p = (char_u *)skipwhite((char *)leader); + p = skipwhite(leader); // Compute the length of the replaced characters in // screen characters, not bytes. Move the part that is // not to be overwritten. { - int repl_size = vim_strnsize(lead_repl, - lead_repl_len); + int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len); int i; int l; for (i = 0; i < lead_len && p[i] != NUL; i += l) { - l = utfc_ptr2len((char *)p + i); - if (vim_strnsize(p, i + l) > repl_size) { + l = utfc_ptr2len(p + i); + if (vim_strnsize((char_u *)p, i + l) > repl_size) { break; } } @@ -1466,10 +1464,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) lead_len--; memmove(p, p + 1, (size_t)(leader + lead_len - p)); } else { - int l = utfc_ptr2len((char *)p); + int l = utfc_ptr2len(p); if (l > 1) { - if (ptr2cells((char *)p) > 1) { + if (ptr2cells(p) > 1) { // Replace a double-wide char with // two spaces l--; @@ -1487,7 +1485,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Recompute the indent, it may have changed. if (curbuf->b_p_ai || do_si) { - newindent = get_indent_str_vtab(leader, + newindent = get_indent_str_vtab((char_u *)leader, curbuf->b_p_ts, curbuf->b_p_vts_array, false); } @@ -1505,7 +1503,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) while (off > 0 && lead_len > 0 && leader[lead_len - 1] == ' ') { // Don't do it when there is a tab before the space - if (vim_strchr(skipwhite((char *)leader), '\t') != NULL) { + if (vim_strchr(skipwhite(leader), '\t') != NULL) { break; } lead_len--; @@ -1604,7 +1602,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } } STRCAT(leader, p_extra); - p_extra = leader; + p_extra = (char_u *)leader; did_ai = true; // So truncating blanks works with comments less_cols -= lead_len; } else { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 028dd70eb2..a26a7f6aaf 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1351,10 +1351,10 @@ bool try_getdigits(char **pp, intmax_t *nr) /// @param def Default value, if parsing fails or overflow occurs. /// /// @return Number read from the string, or `def` on parse failure or overflow. -intmax_t getdigits(char_u **pp, bool strict, intmax_t def) +intmax_t getdigits(char **pp, bool strict, intmax_t def) { intmax_t number; - int ok = try_getdigits((char **)pp, &number); + int ok = try_getdigits(pp, &number); if (strict && !ok) { abort(); } @@ -1366,7 +1366,7 @@ intmax_t getdigits(char_u **pp, bool strict, intmax_t def) /// @see getdigits int getdigits_int(char **pp, bool strict, int def) { - intmax_t number = getdigits((char_u **)pp, strict, def); + intmax_t number = getdigits(pp, strict, def); #if SIZEOF_INTMAX_T > SIZEOF_INT if (strict) { assert(number >= INT_MIN && number <= INT_MAX); @@ -1380,7 +1380,7 @@ int getdigits_int(char **pp, bool strict, int def) /// Gets a long number from a string. /// /// @see getdigits -long getdigits_long(char_u **pp, bool strict, long def) +long getdigits_long(char **pp, bool strict, long def) { intmax_t number = getdigits(pp, strict, def); #if SIZEOF_INTMAX_T > SIZEOF_LONG @@ -1398,7 +1398,7 @@ long getdigits_long(char_u **pp, bool strict, long def) /// @see getdigits int32_t getdigits_int32(char **pp, bool strict, long def) { - intmax_t number = getdigits((char_u **)pp, strict, def); + intmax_t number = getdigits(pp, strict, def); #if SIZEOF_INTMAX_T > SIZEOF_INT32_T if (strict) { assert(number >= INT32_MIN && number <= INT32_MAX); diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 62cf60e03b..9c33b1a806 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -43,7 +43,7 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] = }; /// Converts cursor_shapes into an Array of Dictionaries -/// @param arena initialized arena where memory will be alocated +/// @param arena initialized arena where memory will be allocated /// /// @return Array of the form {[ "cursor_shape": ... ], ...} Array mode_style_array(Arena *arena) diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index 0f6a260247..04d875c4e3 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -63,9 +63,9 @@ void decor_providers_start(DecorProviders *providers, int type, char **err) bool active; if (p->redraw_start != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 2); - args.items[0] = INTEGER_OBJ((int)display_tick); - args.items[1] = INTEGER_OBJ(type); + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, INTEGER_OBJ((int)display_tick)); + ADD_C(args, INTEGER_OBJ(type)); active = decor_provider_invoke(p->ns_id, "start", p->redraw_start, args, true, err); } else { active = true; @@ -96,12 +96,12 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, for (size_t k = 0; k < kv_size(*providers); k++) { DecorProvider *p = kv_A(*providers, k); if (p && p->redraw_win != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 4); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(wp->w_buffer->handle); + MAXSIZE_TEMP_ARRAY(args, 4); + ADD_C(args, WINDOW_OBJ(wp->handle)); + ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); // TODO(bfredl): we are not using this, but should be first drawn line? - args.items[2] = INTEGER_OBJ(wp->w_topline - 1); - args.items[3] = INTEGER_OBJ(knownmax); + ADD_C(args, INTEGER_OBJ(wp->w_topline - 1)); + ADD_C(args, INTEGER_OBJ(knownmax)); if (decor_provider_invoke(p->ns_id, "win", p->redraw_win, args, true, err)) { kvi_push(*line_providers, p); } @@ -124,10 +124,10 @@ void providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool * for (size_t k = 0; k < kv_size(*providers); k++) { DecorProvider *p = kv_A(*providers, k); if (p && p->redraw_line != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(wp->w_buffer->handle); - args.items[2] = INTEGER_OBJ(row); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, WINDOW_OBJ(wp->handle)); + ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); + ADD_C(args, INTEGER_OBJ(row)); if (decor_provider_invoke(p->ns_id, "line", p->redraw_line, args, true, err)) { *has_decor = true; } else { @@ -150,8 +150,8 @@ void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers, char **er for (size_t i = 0; i < kv_size(*providers); i++) { DecorProvider *p = kv_A(*providers, i); if (p && p->redraw_buf != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = BUFFER_OBJ(buf->handle); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, BUFFER_OBJ(buf->handle)); decor_provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true, err); } } @@ -167,8 +167,8 @@ void decor_providers_invoke_end(DecorProviders *providers, char **err) for (size_t i = 0; i < kv_size(*providers); i++) { DecorProvider *p = kv_A(*providers, i); if (p && p->active && p->redraw_end != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = INTEGER_OBJ((int)display_tick); + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, INTEGER_OBJ((int)display_tick)); decor_provider_invoke(p->ns_id, "end", p->redraw_end, args, true, err); } } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 75021e90d6..849204f789 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -115,7 +115,10 @@ void diff_buf_delete(buf_T *buf) tp->tp_diff_invalid = true; if (tp == curtab) { - diff_redraw(true); + // don't redraw right away, more might change or buffer state + // is invalid right now + need_diff_redraw = true; + redraw_later(curwin, VALID); } } } @@ -369,9 +372,8 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T // 2. 3. 4. 5.: inserted/deleted lines touching this diff. if (deleted > 0) { + off = 0; if (dp->df_lnum[idx] >= line1) { - off = dp->df_lnum[idx] - lnum_deleted; - if (last <= line2) { // 4. delete all lines of diff if ((dp->df_next != NULL) @@ -388,14 +390,13 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T dp->df_count[idx] = 0; } else { // 5. delete lines at or just before top of diff + off = dp->df_lnum[idx] - lnum_deleted; n = off; dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; check_unchanged = true; } dp->df_lnum[idx] = line1; } else { - off = 0; - if (last < line2) { // 2. delete at end of diff dp->df_count[idx] -= last - lnum_deleted + 1; @@ -418,10 +419,13 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T } } - int i; - for (i = 0; i < DB_COUNT; i++) { + for (int i = 0; i < DB_COUNT; i++) { if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { - dp->df_lnum[i] -= off; + if (dp->df_lnum[i] > off) { + dp->df_lnum[i] -= off; + } else { + dp->df_lnum[i] = 1; + } dp->df_count[i] += n; } } @@ -648,9 +652,11 @@ void diff_redraw(bool dofold) need_diff_redraw = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (!wp->w_p_diff) { + // when closing windows or wiping buffers skip invalid window + if (!wp->w_p_diff || !buf_valid(wp->w_buffer)) { continue; } + redraw_later(wp, SOME_VALID); if (wp != curwin) { wp_other = wp; @@ -659,8 +665,8 @@ void diff_redraw(bool dofold) foldUpdateAll(wp); } - // A change may have made filler lines invalid, need to take care - // of that for other windows. + // A change may have made filler lines invalid, need to take care of + // that for other windows. int n = diff_check(wp, wp->w_topline); if (((wp != curwin) && (wp->w_topfill > 0)) || (n > 0)) { @@ -677,6 +683,7 @@ void diff_redraw(bool dofold) check_topfill(wp, false); } } + if (wp_other != NULL && curwin->w_p_scb) { if (used_max_fill_curwin) { // The current window was set to use the maximum number of filler @@ -1421,7 +1428,7 @@ void diff_win_options(win_T *wp, int addbuf) } wp->w_p_fdc_save = vim_strsave(wp->w_p_fdc); } - xfree(wp->w_p_fdc); + free_string_option(wp->w_p_fdc); wp->w_p_fdc = (char_u *)xstrdup("2"); assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9); snprintf((char *)wp->w_p_fdc, STRLEN(wp->w_p_fdc) + 1, "%d", diff_foldcolumn); @@ -2485,6 +2492,17 @@ void nv_diffgetput(bool put, size_t count) ex_diffgetput(&ea); } +/// Return true if "diff" appears in the list of diff blocks of the current tab. +static bool valid_diff(diff_T *diff) +{ + for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { + if (dp == diff) { + return true; + } + } + return false; +} + /// ":diffget" and ":diffput" /// /// @param eap @@ -2694,8 +2712,9 @@ void ex_diffgetput(exarg_T *eap) for (i = 0; i < count; i++) { // remember deleting the last line of the buffer buf_empty = curbuf->b_ml.ml_line_count == 1; - ml_delete(lnum, false); - added--; + if (ml_delete(lnum, false) == OK) { + added--; + } } for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) { @@ -2742,10 +2761,9 @@ void ex_diffgetput(exarg_T *eap) } } - // Adjust marks. This will change the following entries! if (added != 0) { - mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, added, - kExtmarkUndo); + // Adjust marks. This will change the following entries! + mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, added, kExtmarkUndo); if (curwin->w_cursor.lnum >= lnum) { // Adjust the cursor position if it's in/after the changed // lines. @@ -2762,7 +2780,15 @@ void ex_diffgetput(exarg_T *eap) // Diff is deleted, update folds in other windows. diff_fold_update(dfree, idx_to); xfree(dfree); - } else { + } + + // mark_adjust() may have made "dp" invalid. We don't know where + // to continue then, bail out. + if (added != 0 && !valid_diff(dp)) { + break; + } + + if (dfree == NULL) { // mark_adjust() may have changed the count in a wrong way dp->df_count[idx_to] = new_count; } @@ -3042,8 +3068,8 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) // change: {first}[,{last}]c{first}[,{last}] // append: {first}a{first}[,{last}] // delete: {first}[,{last}]d{first} - char_u *p = line; - linenr_T f1 = getdigits_int32((char **)&p, true, 0); + char *p = (char *)line; + linenr_T f1 = getdigits_int32(&p, true, 0); if (*p == ',') { p++; l1 = getdigits(&p, true, 0); @@ -3053,7 +3079,7 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) if (*p != 'a' && *p != 'c' && *p != 'd') { return FAIL; // invalid diff format } - int difftype = *p++; + int difftype = (uint8_t)(*p++); long f2 = getdigits(&p, true, 0); if (*p == ',') { p++; @@ -3090,7 +3116,7 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk) { // Parse unified diff hunk header: // @@ -oldline,oldcount +newline,newcount @@ - char_u *p = line; + char *p = (char *)line; if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { long oldcount; long newline; diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 355900c93f..733b3d3d5d 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1662,28 +1662,28 @@ bool check_digraph_chars_valid(int char1, int char2) /// format: {c1}{c2} char {c1}{c2} char ... /// /// @param str -void putdigraph(char_u *str) +void putdigraph(char *str) { while (*str != NUL) { - str = (char_u *)skipwhite((char *)str); + str = skipwhite(str); if (*str == NUL) { return; } - char_u char1 = *str++; - char_u char2 = *str++; + uint8_t char1 = (uint8_t)(*str++); + uint8_t char2 = (uint8_t)(*str++); if (!check_digraph_chars_valid(char1, char2)) { return; } - str = (char_u *)skipwhite((char *)str); + str = skipwhite(str); if (!ascii_isdigit(*str)) { emsg(_(e_number_exp)); return; } - int n = getdigits_int((char **)&str, true, 0); + int n = getdigits_int(&str, true, 0); registerdigraph(char1, char2, n); } @@ -2121,7 +2121,7 @@ void ex_loadkeymap(exarg_T *eap) vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s %s", ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from, ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to); - (void)do_map(0, buf, MODE_LANGMAP, false); + (void)do_map(MAPTYPE_MAP, buf, MODE_LANGMAP, false); } p_cpo = save_cpo; @@ -2158,7 +2158,7 @@ static void keymap_unload(void) for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) { vim_snprintf(buf, sizeof(buf), "<buffer> %s", kp[i].from); - (void)do_map(1, (char_u *)buf, MODE_LANGMAP, false); + (void)do_map(MAPTYPE_UNMAP, (char_u *)buf, MODE_LANGMAP, false); } keymap_ga_clear(&curbuf->b_kmap_ga); diff --git a/src/nvim/digraph.h b/src/nvim/digraph.h index 039fc3370d..71330ae9b1 100644 --- a/src/nvim/digraph.h +++ b/src/nvim/digraph.h @@ -1,7 +1,6 @@ #ifndef NVIM_DIGRAPH_H #define NVIM_DIGRAPH_H -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0571e71cb5..5861e4c52b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -18,7 +18,6 @@ #include "nvim/digraph.h" #include "nvim/edit.h" #include "nvim/eval.h" -#include "nvim/eval/typval.h" #include "nvim/event/loop.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -29,6 +28,7 @@ #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/main.h" #include "nvim/mapping.h" @@ -48,180 +48,18 @@ #include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" -#include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/tag.h" #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/vim.h" #include "nvim/window.h" -// Definitions used for CTRL-X submode. -// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] -// and ctrl_x_mode_names[]. - -#define CTRL_X_WANT_IDENT 0x100 - -#define CTRL_X_NORMAL 0 ///< CTRL-N CTRL-P completion, default -#define CTRL_X_NOT_DEFINED_YET 1 -#define CTRL_X_SCROLL 2 -#define CTRL_X_WHOLE_LINE 3 -#define CTRL_X_FILES 4 -#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT) -#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT) -#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT) -#define CTRL_X_FINISHED 8 -#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT) -#define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT) -#define CTRL_X_CMDLINE 11 -#define CTRL_X_FUNCTION 12 -#define CTRL_X_OMNI 13 -#define CTRL_X_SPELL 14 -#define CTRL_X_LOCAL_MSG 15 ///< only used in "ctrl_x_msgs" -#define CTRL_X_EVAL 16 ///< for builtin function complete() -#define CTRL_X_CMDLINE_CTRL_X 17 ///< CTRL-X typed in CTRL_X_CMDLINE - -#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] -#define CTRL_X_MODE_LINE_OR_EVAL(m) \ - ((m) == CTRL_X_WHOLE_LINE || (m) == CTRL_X_EVAL) - -// Message for CTRL-X mode, index is ctrl_x_mode. -static char *ctrl_x_msgs[] = -{ - N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. - N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), - NULL, // CTRL_X_SCROLL: depends on state - N_(" Whole line completion (^L^N^P)"), - N_(" File name completion (^F^N^P)"), - N_(" Tag completion (^]^N^P)"), - N_(" Path pattern completion (^N^P)"), - N_(" Definition completion (^D^N^P)"), - NULL, // CTRL_X_FINISHED - N_(" Dictionary completion (^K^N^P)"), - N_(" Thesaurus completion (^T^N^P)"), - N_(" Command-line completion (^V^N^P)"), - N_(" User defined completion (^U^N^P)"), - N_(" Omni completion (^O^N^P)"), - N_(" Spelling suggestion (s^N^P)"), - N_(" Keyword Local completion (^N^P)"), - NULL, // CTRL_X_EVAL doesn't use msg. - N_(" Command-line completion (^V^N^P)"), -}; - -static char *ctrl_x_mode_names[] = { - "keyword", - "ctrl_x", - "scroll", - "whole_line", - "files", - "tags", - "path_patterns", - "path_defines", - "unknown", // CTRL_X_FINISHED - "dictionary", - "thesaurus", - "cmdline", - "function", - "omni", - "spell", - NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" - "eval", - "cmdline", -}; - -static char e_hitend[] = N_("Hit end of paragraph"); -static char e_compldel[] = N_("E840: Completion function deleted text"); - -/* - * Structure used to store one match for insert completion. - */ -typedef struct compl_S compl_T; -struct compl_S { - compl_T *cp_next; - compl_T *cp_prev; - char_u *cp_str; // matched text - char_u *(cp_text[CPT_COUNT]); // text for the menu - typval_T cp_user_data; - char_u *cp_fname; // file containing the match, allocated when - // cp_flags has CP_FREE_FNAME - int cp_flags; // CP_ values - int cp_number; // sequence number -}; - -/* - * All the current matches are stored in a list. - * "compl_first_match" points to the start of the list. - * "compl_curr_match" points to the currently selected entry. - * "compl_shown_match" is different from compl_curr_match during - * ins_compl_get_exp(). - */ -static compl_T *compl_first_match = NULL; -static compl_T *compl_curr_match = NULL; -static compl_T *compl_shown_match = NULL; -static compl_T *compl_old_match = NULL; - -/* After using a cursor key <Enter> selects a match in the popup menu, - * otherwise it inserts a line break. */ -static int compl_enter_selects = FALSE; - -/* When "compl_leader" is not NULL only matches that start with this string - * are used. */ -static char_u *compl_leader = NULL; - -static bool compl_get_longest = false; // put longest common string in compl_leader - -static int compl_no_insert = FALSE; /* FALSE: select & insert - TRUE: noinsert */ -static int compl_no_select = FALSE; /* FALSE: select & insert - TRUE: noselect */ - -static bool compl_used_match; // Selected one of the matches. - // When false the match was edited or using - // the longest common string. - -static int compl_was_interrupted = FALSE; /* didn't finish finding - completions. */ - -static bool compl_restarting = false; // don't insert match - -// When the first completion is done "compl_started" is set. When it's -// false the word to be completed must be located. -static bool compl_started = false; - -// Which Ctrl-X mode are we in? -static int ctrl_x_mode = CTRL_X_NORMAL; - -static int compl_matches = 0; -static char_u *compl_pattern = NULL; -static Direction compl_direction = FORWARD; -static Direction compl_shows_dir = FORWARD; -static int compl_pending = 0; // > 1 for postponed CTRL-N -static pos_T compl_startpos; -static colnr_T compl_col = 0; /* column where the text starts - * that is being completed */ -static char_u *compl_orig_text = NULL; /* text as it was before - * completion started */ -static int compl_cont_mode = 0; -static expand_T compl_xp; - -static bool compl_opt_refresh_always = false; - -static int pum_selected_item = -1; - -/// state for pum_ext_select_item. -struct { - bool active; - int item; - bool insert; - bool finish; -} pum_want; - typedef struct insert_state { VimState state; cmdarg_T *ca; @@ -253,8 +91,6 @@ typedef struct insert_state { #define BACKSPACE_WORD_NOT_SPACE 3 #define BACKSPACE_LINE 4 -static size_t spell_bad_len = 0; // length of located bad word - static colnr_T Insstart_textlen; // length of line when insert started static colnr_T Insstart_blank_vcol; // vcol for first inserted blank static bool update_Insstart_orig = true; // set Insstart_orig to Insstart @@ -521,7 +357,7 @@ static int insert_check(VimState *state) // If typed something may trigger CursorHoldI again. if (s->c != K_EVENT // but not in CTRL-X mode, a script can't restore the state - && ctrl_x_mode == CTRL_X_NORMAL) { + && ctrl_x_mode_normal()) { did_cursorhold = false; } @@ -532,8 +368,8 @@ static int insert_check(VimState *state) if (can_cindent && cindent_on() - && ctrl_x_mode == CTRL_X_NORMAL - && !compl_started) { + && ctrl_x_mode_normal() + && !ins_compl_active()) { insert_do_cindent(s); } @@ -551,7 +387,7 @@ static int insert_check(VimState *state) Insstart_orig = Insstart; } - if (stop_insert_mode && !compl_started) { + if (stop_insert_mode && !ins_compl_active()) { // ":stopinsert" used s->count = 0; return 0; // exit insert mode @@ -688,29 +524,25 @@ static int insert_execute(VimState *state, int key) // Special handling of keys while the popup menu is visible or wanted // and the cursor is still in the completed word. Only when there is // a match, skip this when no matches were found. - if (compl_started + if (ins_compl_active() && pum_wanted() - && curwin->w_cursor.col >= compl_col - && (compl_shown_match == NULL - || compl_shown_match != compl_shown_match->cp_next)) { + && curwin->w_cursor.col >= ins_compl_col() + && ins_compl_has_shown_match()) { // BS: Delete one character from "compl_leader". if ((s->c == K_BS || s->c == Ctrl_H) - && curwin->w_cursor.col > compl_col + && curwin->w_cursor.col > ins_compl_col() && (s->c = ins_compl_bs()) == NUL) { return 1; // continue } // When no match was selected or it was edited. - if (!compl_used_match) { + if (!ins_compl_used_match()) { // CTRL-L: Add one character from the current match to // "compl_leader". Except when at the original match and // there is nothing to add, CTRL-L works like CTRL-P then. if (s->c == Ctrl_L - && (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode) - || (compl_shown_match != NULL - && compl_shown_match->cp_str != NULL - && (int)STRLEN(compl_shown_match->cp_str) - > curwin->w_cursor.col - compl_col))) { + && (!ctrl_x_mode_line_or_eval() + || ins_compl_long_shown_match())) { ins_compl_addfrommatch(); return 1; // continue } @@ -736,7 +568,7 @@ static int insert_execute(VimState *state, int key) // Pressing CTRL-Y selects the current match. When // compl_enter_selects is set the Enter key does the same. if ((s->c == Ctrl_Y - || (compl_enter_selects + || (ins_compl_enter_selects() && (s->c == CAR || s->c == K_KENTER || s->c == NL))) && stop_arrow() == OK) { ins_compl_delete(); @@ -747,7 +579,7 @@ static int insert_execute(VimState *state, int key) // Prepare for or stop CTRL-X mode. This doesn't do completion, but it does // fix up the text when finishing completion. - compl_get_longest = false; + ins_compl_init_get_longest(); if (ins_compl_prep(s->c)) { return 1; // continue } @@ -779,8 +611,7 @@ static int insert_execute(VimState *state, int key) s->c = do_digraph(s->c); - if ((s->c == Ctrl_V || s->c == Ctrl_Q) - && (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X)) { + if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode_cmdline()) { insert_do_complete(s); return 1; } @@ -791,8 +622,7 @@ static int insert_execute(VimState *state, int key) return 1; // continue } - if (cindent_on() - && ctrl_x_mode == 0) { + if (cindent_on() && ctrl_x_mode_none()) { // A key name preceded by a bang means this key is not to be // inserted. Skip ahead to the re-indenting below. // A key name preceded by a star means that indenting has to be @@ -873,7 +703,7 @@ static int insert_handle_key(InsertState *s) goto normalchar; // insert CTRL-Z as normal char case Ctrl_O: // execute one command - if (ctrl_x_mode == CTRL_X_OMNI) { + if (ctrl_x_mode_omni()) { insert_do_complete(s); break; } @@ -946,14 +776,14 @@ static int insert_handle_key(InsertState *s) break; case Ctrl_D: // Make indent one shiftwidth smaller. - if (ctrl_x_mode == CTRL_X_PATH_DEFINES) { + if (ctrl_x_mode_path_defines()) { insert_do_complete(s); break; } FALLTHROUGH; case Ctrl_T: // Make indent one shiftwidth greater. - if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { + if (s->c == Ctrl_T && ctrl_x_mode_thesaurus()) { if (check_compl_option(false)) { insert_do_complete(s); } @@ -992,7 +822,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_U: // delete all inserted text in current line // CTRL-X CTRL-U completes with 'completefunc'. - if (ctrl_x_mode == CTRL_X_FUNCTION) { + if (ctrl_x_mode_function()) { insert_do_complete(s); } else { s->did_backspace = ins_bs(s->c, BACKSPACE_LINE, &s->inserted_space); @@ -1157,7 +987,7 @@ check_pum: FALLTHROUGH; case TAB: // TAB or Complete patterns along path - if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) { + if (ctrl_x_mode_path_patterns()) { insert_do_complete(s); break; } @@ -1205,7 +1035,7 @@ check_pum: break; case Ctrl_K: // digraph or keyword completion - if (ctrl_x_mode == CTRL_X_DICTIONARY) { + if (ctrl_x_mode_dictionary()) { if (check_compl_option(true)) { insert_do_complete(s); } @@ -1223,7 +1053,7 @@ check_pum: break; case Ctrl_RSB: // Tag name completion after ^X - if (ctrl_x_mode != CTRL_X_TAGS) { + if (!ctrl_x_mode_tags()) { goto normalchar; } else { insert_do_complete(s); @@ -1231,7 +1061,7 @@ check_pum: break; case Ctrl_F: // File name completion after ^X - if (ctrl_x_mode != CTRL_X_FILES) { + if (!ctrl_x_mode_files()) { goto normalchar; } else { insert_do_complete(s); @@ -1240,7 +1070,7 @@ check_pum: case 's': // Spelling completion after ^X case Ctrl_S: - if (ctrl_x_mode != CTRL_X_SPELL) { + if (!ctrl_x_mode_spell()) { goto normalchar; } else { insert_do_complete(s); @@ -1248,7 +1078,7 @@ check_pum: break; case Ctrl_L: // Whole line completion after ^X - if (ctrl_x_mode != CTRL_X_WHOLE_LINE) { + if (!ctrl_x_mode_whole_line()) { goto normalchar; } FALLTHROUGH; @@ -1258,8 +1088,7 @@ check_pum: // if 'complete' is empty then plain ^P is no longer special, // but it is under other ^X modes if (*curbuf->b_p_cpt == NUL - && (ctrl_x_mode == CTRL_X_NORMAL - || ctrl_x_mode == CTRL_X_WHOLE_LINE) + && (ctrl_x_mode_normal() || ctrl_x_mode_whole_line()) && !(compl_cont_status & CONT_LOCAL)) { goto normalchar; } @@ -1394,10 +1223,10 @@ bool edit(int cmdchar, bool startln, long count) // the value of `restart_edit` before `ex_normal` returns. restart_edit = 'i'; force_restart_edit = true; + return false; } else { - terminal_enter(); + return terminal_enter(); } - return false; } // Don't allow inserting in the sandbox. @@ -1409,7 +1238,7 @@ bool edit(int cmdchar, bool startln, long count) // Don't allow changes in the buffer while editing the cmdline. The // caller of getcmdline() may get confused. // Don't allow recursive insert mode when busy with completion. - if (textlock != 0 || compl_started || compl_busy || pum_visible()) { + if (textlock != 0 || ins_compl_active() || compl_busy || pum_visible()) { emsg(_(e_textlock)); return false; } @@ -1425,6 +1254,11 @@ bool edit(int cmdchar, bool startln, long count) return s->c == Ctrl_O; } +bool ins_need_undo_get(void) +{ + return ins_need_undo; +} + /// Redraw for Insert mode. /// This is postponed until getting the next character to make '$' in the 'cpo' /// option work correctly. @@ -1432,7 +1266,7 @@ bool edit(int cmdchar, bool startln, long count) /// inserting sequences of characters (e.g., for CTRL-R). /// /// @param ready not busy with something -static void ins_redraw(bool ready) +void ins_redraw(bool ready) { if (char_avail()) { return; @@ -2019,3523 +1853,6 @@ static bool del_char_after_col(int limit_col) return true; } -/* - * CTRL-X pressed in Insert mode. - */ -static void ins_ctrl_x(void) -{ - if (ctrl_x_mode != CTRL_X_CMDLINE && ctrl_x_mode != CTRL_X_CMDLINE_CTRL_X) { - // if the next ^X<> won't ADD nothing, then reset compl_cont_status - if (compl_cont_status & CONT_N_ADDS) { - compl_cont_status |= CONT_INTRPT; - } else { - compl_cont_status = 0; - } - // We're not sure which CTRL-X mode it will be yet - ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - edit_submode_pre = NULL; - showmode(); - } else { - // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X - // CTRL-V look like CTRL-N - ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; - } - - may_trigger_modechanged(); -} - -// Whether other than default completion has been selected. -bool ctrl_x_mode_not_default(void) - FUNC_ATTR_PURE -{ - return ctrl_x_mode != CTRL_X_NORMAL; -} - -// Whether CTRL-X was typed without a following character, -// not including when in CTRL-X CTRL-V mode. -bool ctrl_x_mode_not_defined_yet(void) - FUNC_ATTR_PURE -{ - return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; -} - -/// 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) - : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL - && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL)) { - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - msg_attr((dict_opt - ? _("'dictionary' option is empty") - : _("'thesaurus' option is empty")), HL_ATTR(HLF_E)); - if (emsg_silent == 0) { - vim_beep(BO_COMPL); - setcursor(); - ui_flush(); - os_delay(2004L, false); - } - return false; - } - return true; -} - -/// 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 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; - } - - switch (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: - case CTRL_X_CMDLINE_CTRL_X: - return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E - || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB - || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P - || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V - || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O - || c == Ctrl_S || c == Ctrl_K || c == 's' - || c == Ctrl_Z; - case CTRL_X_SCROLL: - return c == Ctrl_Y || c == Ctrl_E; - case CTRL_X_WHOLE_LINE: - return c == Ctrl_L || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_FILES: - return c == Ctrl_F || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_DICTIONARY: - return c == Ctrl_K || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_THESAURUS: - return c == Ctrl_T || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_TAGS: - return c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_PATH_PATTERNS: - return c == Ctrl_P || c == Ctrl_N; - case CTRL_X_PATH_DEFINES: - return c == Ctrl_D || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_CMDLINE: - return c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N - || c == Ctrl_X; - case CTRL_X_FUNCTION: - return c == Ctrl_U || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_OMNI: - return c == Ctrl_O || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_SPELL: - return c == Ctrl_S || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_EVAL: - return (c == Ctrl_P || c == Ctrl_N); - } - internal_error("vim_is_ctrl_x_key()"); - return false; -} - -/// 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. - 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 - return vim_isfilec(c) && !vim_ispathsep(c); - - case CTRL_X_CMDLINE: - case CTRL_X_CMDLINE_CTRL_X: - case CTRL_X_OMNI: - // 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. - return vim_isprintc(c); - } - return vim_iswordc(c); -} - -/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the -/// case of the originally typed text is used, and the case of the completed -/// text is inferred, ie this tries to work out what case you probably wanted -/// the rest of the word to be in -- webb -/// -/// @param[in] cont_s_ipos next ^X<> will set initial_pos -int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, Direction dir, - bool cont_s_ipos) - FUNC_ATTR_NONNULL_ARG(1) -{ - char_u *str = str_arg; - int i, c; - int actual_len; // Take multi-byte characters - int actual_compl_length; // into account. - int min_len; - bool has_lower = false; - bool was_letter = false; - int flags = 0; - - if (p_ic && curbuf->b_p_inf && len > 0) { - // Infer case of completed part. - - // Find actual length of completion. - { - const char_u *p = str; - actual_len = 0; - while (*p != NUL) { - MB_PTR_ADV(p); - actual_len++; - } - } - - // Find actual length of original text. - { - const char_u *p = compl_orig_text; - actual_compl_length = 0; - while (*p != NUL) { - MB_PTR_ADV(p); - actual_compl_length++; - } - } - - /* "actual_len" may be smaller than "actual_compl_length" when using - * thesaurus, only use the minimum when comparing. */ - min_len = actual_len < actual_compl_length - ? actual_len : actual_compl_length; - - // Allocate wide character array for the completion and fill it. - int *const wca = xmalloc((size_t)actual_len * sizeof(*wca)); - { - const char_u *p = str; - for (i = 0; i < actual_len; i++) { - wca[i] = mb_ptr2char_adv(&p); - } - } - - // Rule 1: Were any chars converted to lower? - { - const char_u *p = compl_orig_text; - for (i = 0; i < min_len; i++) { - c = mb_ptr2char_adv(&p); - if (mb_islower(c)) { - has_lower = true; - if (mb_isupper(wca[i])) { - // Rule 1 is satisfied. - for (i = actual_compl_length; i < actual_len; i++) { - wca[i] = mb_tolower(wca[i]); - } - break; - } - } - } - } - - /* - * Rule 2: No lower case, 2nd consecutive letter converted to - * upper case. - */ - if (!has_lower) { - const char_u *p = compl_orig_text; - for (i = 0; i < min_len; i++) { - c = mb_ptr2char_adv(&p); - if (was_letter && mb_isupper(c) && mb_islower(wca[i])) { - // Rule 2 is satisfied. - for (i = actual_compl_length; i < actual_len; i++) { - wca[i] = mb_toupper(wca[i]); - } - break; - } - was_letter = mb_islower(c) || mb_isupper(c); - } - } - - // Copy the original case of the part we typed. - { - const char_u *p = compl_orig_text; - for (i = 0; i < min_len; i++) { - c = mb_ptr2char_adv(&p); - if (mb_islower(c)) { - wca[i] = mb_tolower(wca[i]); - } else if (mb_isupper(c)) { - wca[i] = mb_toupper(wca[i]); - } - } - } - - // Generate encoding specific output from wide character array. - // Multi-byte characters can occupy up to five bytes more than - // ASCII characters, and we also need one byte for NUL, so stay - // six bytes away from the edge of IObuff. - { - char_u *p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) { - p += utf_char2bytes(wca[i++], (char *)p); - } - *p = NUL; - } - - xfree(wca); - - str = IObuff; - } - if (cont_s_ipos) { - flags |= CP_CONT_S_IPOS; - } - if (icase) { - flags |= CP_ICASE; - } - - return ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false); -} - -/// Add a match to the list of matches -/// -/// @param[in] str Match to add. -/// @param[in] len Match length, -1 to use #STRLEN. -/// @param[in] fname File name match comes from. May be NULL. -/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, -/// must have exactly #CPT_COUNT items. -/// @param[in] cptext_allocated If true, will not copy cptext strings. -/// -/// @note Will free strings in case of error. -/// cptext itself will not be freed. -/// @param[in] cdir Completion direction. -/// @param[in] adup True if duplicate matches are to be accepted. -/// -/// @return NOTDONE if the given string is already in the list of completions, -/// otherwise it is added to the list and OK is returned. FAIL will be -/// returned in case of error. -static int ins_compl_add(char_u *const str, int len, char_u *const fname, - char_u *const *const cptext, const bool cptext_allocated, - typval_T *user_data, const Direction cdir, int flags_arg, const bool adup) - FUNC_ATTR_NONNULL_ARG(1) -{ - compl_T *match; - const Direction dir = (cdir == kDirectionNotSet ? compl_direction : cdir); - int flags = flags_arg; - - if (flags & CP_FAST) { - fast_breakcheck(); - } else { - os_breakcheck(); - } -#define FREE_CPTEXT(cptext, cptext_allocated) \ - do { \ - if ((cptext) != NULL && (cptext_allocated)) { \ - for (size_t i = 0; i < CPT_COUNT; i++) { \ - xfree((cptext)[i]); \ - } \ - } \ - } while (0) - if (got_int) { - FREE_CPTEXT(cptext, cptext_allocated); - return FAIL; - } - if (len < 0) { - len = (int)STRLEN(str); - } - - /* - * If the same match is already present, don't add it. - */ - if (compl_first_match != NULL && !adup) { - match = compl_first_match; - do { - if (!(match->cp_flags & CP_ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) { - FREE_CPTEXT(cptext, cptext_allocated); - return NOTDONE; - } - match = match->cp_next; - } while (match != NULL && match != compl_first_match); - } - - // Remove any popup menu before changing the list of matches. - ins_compl_del_pum(); - - /* - * Allocate a new match structure. - * Copy the values to the new match structure. - */ - match = xcalloc(1, sizeof(compl_T)); - match->cp_number = -1; - if (flags & CP_ORIGINAL_TEXT) { - match->cp_number = 0; - } - match->cp_str = vim_strnsave(str, (size_t)len); - - // match-fname is: - // - compl_curr_match->cp_fname if it is a string equal to fname. - // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem. - // - NULL otherwise. --Acevedo - if (fname != NULL - && compl_curr_match != NULL - && compl_curr_match->cp_fname != NULL - && STRCMP(fname, compl_curr_match->cp_fname) == 0) { - match->cp_fname = compl_curr_match->cp_fname; - } else if (fname != NULL) { - match->cp_fname = vim_strsave(fname); - flags |= CP_FREE_FNAME; - } else { - match->cp_fname = NULL; - } - match->cp_flags = flags; - - if (cptext != NULL) { - int i; - - for (i = 0; i < CPT_COUNT; i++) { - if (cptext[i] == NULL) { - continue; - } - if (*cptext[i] != NUL) { - match->cp_text[i] = (cptext_allocated - ? cptext[i] - : (char_u *)xstrdup((char *)cptext[i])); - } else if (cptext_allocated) { - xfree(cptext[i]); - } - } - } - - if (user_data != NULL) { - match->cp_user_data = *user_data; - } - - /* - * Link the new match structure in the list of matches. - */ - if (compl_first_match == NULL) { - match->cp_next = match->cp_prev = NULL; - } else if (dir == FORWARD) { - match->cp_next = compl_curr_match->cp_next; - match->cp_prev = compl_curr_match; - } else { // BACKWARD - match->cp_next = compl_curr_match; - match->cp_prev = compl_curr_match->cp_prev; - } - if (match->cp_next) { - match->cp_next->cp_prev = match; - } - if (match->cp_prev) { - match->cp_prev->cp_next = match; - } else { // if there's nothing before, it is the first match - compl_first_match = match; - } - compl_curr_match = match; - - /* - * Find the longest common string if still doing that. - */ - if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0) { - ins_compl_longest_match(match); - } - - return OK; -} - -/// Check that "str[len]" matches with "match->cp_str", considering -/// "match->cp_flags". -/// -/// @param match completion match -/// @param str character string to check -/// @param len length 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_flags & CP_EQUAL) { - return true; - } - if (match->cp_flags & CP_ICASE) { - return STRNICMP(match->cp_str, str, len) == 0; - } - return STRNCMP(match->cp_str, str, len) == 0; -} - -/* - * Reduce the longest common string for match "match". - */ -static void ins_compl_longest_match(compl_T *match) -{ - char_u *p, *s; - int c1, c2; - int had_match; - - if (compl_leader == NULL) { - // First match, use it as a whole. - compl_leader = vim_strsave(match->cp_str); - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) { - ins_compl_delete(); - } - compl_used_match = false; - } else { - // Reduce the text if this match differs from compl_leader. - p = compl_leader; - s = match->cp_str; - while (*p != NUL) { - c1 = utf_ptr2char((char *)p); - c2 = utf_ptr2char((char *)s); - - if ((match->cp_flags & CP_ICASE) - ? (mb_tolower(c1) != mb_tolower(c2)) - : (c1 != c2)) { - break; - } - MB_PTR_ADV(p); - MB_PTR_ADV(s); - } - - if (*p != NUL) { - // Leader was shortened, need to change the inserted text. - *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) { - ins_compl_delete(); - } - } - - compl_used_match = false; - } -} - -/* - * Add an array of matches to the list of matches. - * Frees matches[]. - */ -static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) - FUNC_ATTR_NONNULL_ALL -{ - int add_r = OK; - Direction dir = compl_direction; - - for (int i = 0; i < num_matches && add_r != FAIL; i++) { - if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir, - CP_FAST | (icase ? CP_ICASE : 0), - false)) == OK) { - // If dir was BACKWARD then honor it just once. - dir = FORWARD; - } - } - FreeWild(num_matches, matches); -} - -/* Make the completion list cyclic. - * Return the number of matches (excluding the original). - */ -static int ins_compl_make_cyclic(void) -{ - compl_T *match; - int count = 0; - - if (compl_first_match != NULL) { - /* - * Find the end of the list. - */ - match = compl_first_match; - // there's always an entry for the compl_orig_text, it doesn't count. - while (match->cp_next != NULL && match->cp_next != compl_first_match) { - match = match->cp_next; - ++count; - } - match->cp_next = compl_first_match; - compl_first_match->cp_prev = match; - } - 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). - * "list" is the list of matches. - */ -void set_completion(colnr_T startcol, list_T *list) -{ - int flags = CP_ORIGINAL_TEXT; - - // If already doing completions stop it. - if (ctrl_x_mode != CTRL_X_NORMAL) { - ins_compl_prep(' '); - } - ins_compl_clear(); - ins_compl_free(); - - compl_direction = FORWARD; - if (startcol > curwin->w_cursor.col) { - startcol = curwin->w_cursor.col; - } - compl_col = startcol; - compl_length = (int)curwin->w_cursor.col - (int)startcol; - // compl_pattern doesn't need to be set - compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, - (size_t)compl_length); - if (p_ic) { - flags |= CP_ICASE; - } - if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, - flags | CP_FAST, false) != OK) { - return; - } - - ctrl_x_mode = CTRL_X_EVAL; - - ins_compl_add_list(list); - compl_matches = ins_compl_make_cyclic(); - compl_started = true; - compl_used_match = true; - compl_cont_status = 0; - int save_w_wrow = curwin->w_wrow; - int save_w_leftcol = curwin->w_leftcol; - - compl_curr_match = compl_first_match; - if (compl_no_insert || compl_no_select) { - ins_complete(K_DOWN, false); - if (compl_no_select) { - ins_complete(K_UP, false); - } - } else { - ins_complete(Ctrl_N, false); - } - compl_enter_selects = compl_no_insert; - - // Lazily show the popup menu, unless we got interrupted. - if (!compl_interrupted) { - show_pum(save_w_wrow, save_w_leftcol); - } - - may_trigger_modechanged(); - ui_flush(); -} - -/* "compl_match_array" points the currently displayed list of entries in the - * popup menu. It is NULL when there is no popup menu. */ -static pumitem_T *compl_match_array = NULL; -static int compl_match_arraysize; - -/* - * Remove any popup menu. - */ -static void ins_compl_del_pum(void) -{ - if (compl_match_array != NULL) { - pum_undisplay(false); - XFREE_CLEAR(compl_match_array); - } -} - -/// 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" - return vim_strchr((char *)p_cot, 'm') != NULL; -} - -/// 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 -{ - // 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 (comp == NULL - || ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) { - break; - } - comp = comp->cp_next; - } while (comp != compl_first_match); - - if (strstr((char *)p_cot, "menuone") != NULL) { - return i >= 1; - } - return i >= 2; -} - -static void trigger_complete_changed_event(int cur) -{ - static bool recursive = false; - save_v_event_T save_v_event; - - if (recursive) { - return; - } - - dict_T *v_event = get_v_event(&save_v_event); - if (cur < 0) { - tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc()); - } else { - dict_T *item = ins_compl_dict_alloc(compl_curr_match); - tv_dict_add_dict(v_event, S_LEN("completed_item"), item); - } - pum_set_event_info(v_event); - tv_dict_set_keys_readonly(v_event); - - recursive = true; - textlock++; - apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, false, curbuf); - textlock--; - recursive = false; - - restore_v_event(v_event, &save_v_event); -} - -/// Show the popup menu for the list of matches. -/// Also adjusts "compl_shown_match" to an entry that is actually displayed. -void ins_compl_show_pum(void) -{ - compl_T *compl; - compl_T *shown_compl = NULL; - bool did_find_shown_match = false; - bool shown_match_ok = false; - int i; - int cur = -1; - colnr_T col; - int lead_len = 0; - bool array_changed = false; - - if (!pum_wanted() || !pum_enough_matches()) { - return; - } - - // Dirty hard-coded hack: remove any matchparen highlighting. - do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif"); - - // Update the screen before drawing the popup menu over it. - update_screen(0); - - if (compl_match_array == NULL) { - array_changed = true; - // Need to build the popup menu list. - compl_match_arraysize = 0; - compl = compl_first_match; - // - // If it's user complete function and refresh_always, - // do not use "compl_leader" as prefix filter. - // - if (ins_compl_need_restart()) { - XFREE_CLEAR(compl_leader); - } - if (compl_leader != NULL) { - lead_len = (int)STRLEN(compl_leader); - } - do { - if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { - compl_match_arraysize++; - } - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - if (compl_match_arraysize == 0) { - return; - } - - assert(compl_match_arraysize >= 0); - compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); - // If the current match is the original text don't find the first - // match after it, don't highlight anything. - if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { - shown_match_ok = true; - } - - i = 0; - compl = compl_first_match; - do { - if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { - if (!shown_match_ok) { - if (compl == compl_shown_match || did_find_shown_match) { - /* This item is the shown match or this is the - * first displayed item after the shown match. */ - compl_shown_match = compl; - did_find_shown_match = true; - shown_match_ok = true; - } else { - // Remember this displayed match for when the - // shown match is just below it. - shown_compl = compl; - } - cur = i; - } - - if (compl->cp_text[CPT_ABBR] != NULL) { - compl_match_array[i].pum_text = - compl->cp_text[CPT_ABBR]; - } else { - compl_match_array[i].pum_text = compl->cp_str; - } - compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; - if (compl->cp_text[CPT_MENU] != NULL) { - compl_match_array[i++].pum_extra = - compl->cp_text[CPT_MENU]; - } else { - compl_match_array[i++].pum_extra = compl->cp_fname; - } - } - - if (compl == compl_shown_match) { - did_find_shown_match = true; - - /* When the original text is the shown match don't set - * compl_shown_match. */ - if (compl->cp_flags & CP_ORIGINAL_TEXT) { - shown_match_ok = true; - } - - if (!shown_match_ok && shown_compl != NULL) { - /* The shown match isn't displayed, set it to the - * previously displayed match. */ - compl_shown_match = shown_compl; - shown_match_ok = true; - } - } - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - - if (!shown_match_ok) { // no displayed match at all - cur = -1; - } - } else { - // popup menu already exists, only need to find the current item. - for (i = 0; i < compl_match_arraysize; i++) { - if (compl_match_array[i].pum_text == compl_shown_match->cp_str - || compl_match_array[i].pum_text - == compl_shown_match->cp_text[CPT_ABBR]) { - cur = i; - break; - } - } - } - - // 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_selected_item = cur; - pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); - curwin->w_cursor.col = col; - - if (has_event(EVENT_COMPLETECHANGED)) { - trigger_complete_changed_event(cur); - } -} - -#define DICT_FIRST (1) // use just first element in "dict" -#define DICT_EXACT (2) // "dict" is the exact name of a file - -/// Add any identifiers that match the given pattern in the list of dictionary -/// files "dict_start" to the list of completions. -/// -/// @param flags DICT_FIRST and/or DICT_EXACT -/// @param thesaurus Thesaurus completion -static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, int thesaurus) -{ - char_u *dict = dict_start; - char_u *ptr; - char_u *buf; - regmatch_T regmatch; - char_u **files; - int count; - int save_p_scs; - Direction dir = compl_direction; - - if (*dict == NUL) { - /* When 'dictionary' is empty and spell checking is enabled use - * "spell". */ - if (!thesaurus && curwin->w_p_spell) { - dict = (char_u *)"spell"; - } else { - return; - } - } - - buf = xmalloc(LSIZE); - regmatch.regprog = NULL; // so that we can goto theend - - // If 'infercase' is set, don't use 'smartcase' here - save_p_scs = p_scs; - if (curbuf->b_p_inf) { - p_scs = FALSE; - } - - /* When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern - * to only match at the start of a line. Otherwise just match the - * pattern. Also need to double backslashes. */ - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); - - size_t len = STRLEN(pat_esc) + 10; - ptr = xmalloc(len); - vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); - regmatch.regprog = vim_regcomp((char *)ptr, RE_MAGIC); - xfree(pat_esc); - xfree(ptr); - } else { - regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) { - goto theend; - } - } - - // ignore case depends on 'ignorecase', 'smartcase' and "pat" - regmatch.rm_ic = ignorecase(pat); - while (*dict != NUL && !got_int && !compl_interrupted) { - // copy one dictionary file name into buf - if (flags == DICT_EXACT) { - count = 1; - files = &dict; - } else { - /* Expand wildcards in the dictionary name, but do not allow - * backticks (for security, the 'dict' option may have been set in - * a modeline). */ - copy_option_part((char **)&dict, (char *)buf, LSIZE, ","); - if (!thesaurus && STRCMP(buf, "spell") == 0) { - count = -1; - } else if (vim_strchr((char *)buf, '`') != NULL - || expand_wildcards(1, &buf, &count, &files, - EW_FILE|EW_SILENT) != OK) { - count = 0; - } - } - - if (count == -1) { - /* Complete from active spelling. Skip "\<" in the pattern, we - * don't use it as a RE. */ - if (pat[0] == '\\' && pat[1] == '<') { - ptr = pat + 2; - } else { - ptr = pat; - } - spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); - } else if (count > 0) { // avoid warning for using "files" uninit - ins_compl_files(count, files, thesaurus, flags, - ®match, buf, &dir); - if (flags != DICT_EXACT) { - FreeWild(count, files); - } - } - if (flags != 0) { - break; - } - } - -theend: - p_scs = save_p_scs; - vim_regfree(regmatch.regprog); - xfree(buf); -} - -static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, - regmatch_T *regmatch, char_u *buf, Direction *dir) - FUNC_ATTR_NONNULL_ARG(2, 7) -{ - char_u *ptr; - int i; - FILE *fp; - int add_r; - - for (i = 0; i < count && !got_int && !compl_interrupted; i++) { - fp = os_fopen((char *)files[i], "r"); // open dictionary file - if (flags != DICT_EXACT) { - msg_hist_off = true; // reset in msg_trunc_attr() - vim_snprintf((char *)IObuff, IOSIZE, - _("Scanning dictionary: %s"), (char *)files[i]); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); - } - - if (fp == NULL) { - continue; - } - /* - * Read dictionary file line by line. - * Check each line for a match. - */ - while (!got_int && !compl_interrupted - && !vim_fgets(buf, LSIZE, fp)) { - ptr = buf; - while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) { - ptr = regmatch->startp[0]; - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - ptr = find_line_end(ptr); - } else { - ptr = find_word_end(ptr); - } - add_r = ins_compl_add_infercase(regmatch->startp[0], - (int)(ptr - regmatch->startp[0]), - p_ic, files[i], *dir, false); - if (thesaurus) { - char_u *wstart; - - /* - * Add the other matches on the line - */ - ptr = buf; - while (!got_int) { - /* Find start of the next word. Skip white - * space and punctuation. */ - ptr = find_word_start(ptr); - if (*ptr == NUL || *ptr == NL) { - break; - } - wstart = ptr; - - // Find end of the word. - // Japanese words may have characters in - // different classes, only separate words - // with single-byte non-word characters. - while (*ptr != NUL) { - const int l = utfc_ptr2len((char *)ptr); - - if (l < 2 && !vim_iswordc(*ptr)) { - break; - } - ptr += l; - } - - // Add the word. Skip the regexp match. - if (wstart != regmatch->startp[0]) { - add_r = ins_compl_add_infercase(wstart, (int)(ptr - wstart), - p_ic, files[i], *dir, false); - } - } - } - if (add_r == OK) { - // if dir was BACKWARD then honor it just once - *dir = FORWARD; - } else if (add_r == FAIL) { - break; - } - // avoid expensive call to vim_regexec() when at end - // of line - if (*ptr == '\n' || got_int) { - break; - } - } - line_breakcheck(); - ins_compl_check_keys(50, false); - } - fclose(fp); - } -} - -/* - * Find the start of the next word. - * Returns a pointer to the first char of the word. Also stops at a NUL. - */ -char_u *find_word_start(char_u *ptr) - FUNC_ATTR_PURE -{ - while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) { - ptr += utfc_ptr2len((char *)ptr); - } - return ptr; -} - -/* - * Find the end of the word. Assumes it starts inside a word. - * Returns a pointer to just after the word. - */ -char_u *find_word_end(char_u *ptr) - FUNC_ATTR_PURE -{ - const int start_class = mb_get_class(ptr); - if (start_class > 1) { - while (*ptr != NUL) { - ptr += utfc_ptr2len((char *)ptr); - if (mb_get_class(ptr) != start_class) { - break; - } - } - } - return ptr; -} - -/* - * Find the end of the line, omitting CR and NL at the end. - * Returns a pointer to just after the line. - */ -static char_u *find_line_end(char_u *ptr) -{ - char_u *s; - - s = ptr + STRLEN(ptr); - while (s > ptr && (s[-1] == CAR || s[-1] == NL)) { - --s; - } - return s; -} - -/* - * Free the list of completions - */ -static void ins_compl_free(void) -{ - compl_T *match; - - XFREE_CLEAR(compl_pattern); - XFREE_CLEAR(compl_leader); - - if (compl_first_match == NULL) { - return; - } - - ins_compl_del_pum(); - pum_clear(); - - compl_curr_match = compl_first_match; - do { - match = compl_curr_match; - compl_curr_match = compl_curr_match->cp_next; - xfree(match->cp_str); - // several entries may use the same fname, free it just once. - if (match->cp_flags & CP_FREE_FNAME) { - xfree(match->cp_fname); - } - for (int i = 0; i < CPT_COUNT; i++) { - xfree(match->cp_text[i]); - } - tv_clear(&match->cp_user_data); - xfree(match); - } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); - compl_first_match = compl_curr_match = NULL; - compl_shown_match = NULL; - compl_old_match = NULL; -} - -static void ins_compl_clear(void) -{ - compl_cont_status = 0; - compl_started = false; - compl_matches = 0; - XFREE_CLEAR(compl_pattern); - XFREE_CLEAR(compl_leader); - edit_submode_extra = NULL; - XFREE_CLEAR(compl_orig_text); - compl_enter_selects = false; - // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); -} - -/// Check that Insert completion is active. -bool ins_compl_active(void) - FUNC_ATTR_PURE -{ - return compl_started; -} - -static void ins_compl_update_sequence_numbers(void) -{ - int number = 0; - compl_T *match; - - if (compl_direction == FORWARD) { - // search backwards for the first valid (!= -1) number. - // This should normally succeed already at the first loop - // cycle, so it's fast! - for (match = compl_curr_match->cp_prev; - match != NULL && match != compl_first_match; - match = match->cp_prev) { - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - } - if (match != NULL) { - // go up and assign all numbers which are not assigned yet - for (match = match->cp_next; - match != NULL && match->cp_number == -1; - match = match->cp_next) { - match->cp_number = ++number; - } - } - } else { // BACKWARD - assert(compl_direction == BACKWARD); - // search forwards (upwards) for the first valid (!= -1) - // number. This should normally succeed already at the - // first loop cycle, so it's fast! - for (match = compl_curr_match->cp_next; - match != NULL && match != compl_first_match; - match = match->cp_next) { - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - } - if (match != NULL) { - // go down and assign all numbers which are not - // assigned yet - for (match = match->cp_prev; - match && match->cp_number == -1; - match = match->cp_prev) { - match->cp_number = ++number; - } - } - } -} - -// Get complete information -void get_complete_info(list_T *what_list, dict_T *retdict) -{ -#define CI_WHAT_MODE 0x01 -#define CI_WHAT_PUM_VISIBLE 0x02 -#define CI_WHAT_ITEMS 0x04 -#define CI_WHAT_SELECTED 0x08 -#define CI_WHAT_INSERTED 0x10 -#define CI_WHAT_ALL 0xff - int what_flag; - - if (what_list == NULL) { - what_flag = CI_WHAT_ALL; - } else { - what_flag = 0; - for (listitem_T *item = tv_list_first(what_list) - ; item != NULL - ; item = TV_LIST_ITEM_NEXT(what_list, item)) { - const char *what = tv_get_string(TV_LIST_ITEM_TV(item)); - - if (STRCMP(what, "mode") == 0) { - what_flag |= CI_WHAT_MODE; - } else if (STRCMP(what, "pum_visible") == 0) { - what_flag |= CI_WHAT_PUM_VISIBLE; - } else if (STRCMP(what, "items") == 0) { - what_flag |= CI_WHAT_ITEMS; - } else if (STRCMP(what, "selected") == 0) { - what_flag |= CI_WHAT_SELECTED; - } else if (STRCMP(what, "inserted") == 0) { - what_flag |= CI_WHAT_INSERTED; - } - } - } - - int ret = OK; - if (what_flag & CI_WHAT_MODE) { - ret = tv_dict_add_str(retdict, S_LEN("mode"), - (char *)ins_compl_mode()); - } - - if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) { - ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible()); - } - - if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { - list_T *li = tv_list_alloc(ins_compl_len()); - - ret = tv_dict_add_list(retdict, S_LEN("items"), li); - if (ret == OK && compl_first_match != NULL) { - compl_T *match = compl_first_match; - do { - if (!(match->cp_flags & CP_ORIGINAL_TEXT)) { - dict_T *di = tv_dict_alloc(); - - tv_list_append_dict(li, di); - tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); - if (match->cp_user_data.v_type == VAR_UNKNOWN) { - tv_dict_add_str(di, S_LEN("user_data"), ""); - } else { - tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); - } - } - match = match->cp_next; - } while (match != NULL && match != compl_first_match); - } - } - - if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { - if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) { - ins_compl_update_sequence_numbers(); - } - ret = tv_dict_add_nr(retdict, S_LEN("selected"), - (compl_curr_match != NULL) - ? compl_curr_match->cp_number - 1 : -1); - } - - (void)ret; - // TODO(vim): - // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) -} - -// Return Insert completion mode name string -static char_u *ins_compl_mode(void) -{ - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || ctrl_x_mode == CTRL_X_SCROLL || compl_started) { - return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; - } - return (char_u *)""; -} - -/* - * Delete one character before the cursor and show the subset of the matches - * that match the word that is now before the cursor. - * Returns the character to be used, NUL if the work is done and another char - * to be got from the user. - */ -static int ins_compl_bs(void) -{ - char_u *line = get_cursor_line_ptr(); - char_u *p = line + curwin->w_cursor.col; - MB_PTR_BACK(line, p); - ptrdiff_t p_off = p - line; - - // Stop completion when the whole word was deleted. For Omni completion - // allow the word to be deleted, we won't match everything. - // Respect the 'backspace' option. - if ((int)(p - line) - (int)compl_col < 0 - || ((int)(p - line) - (int)compl_col == 0 && ctrl_x_mode != CTRL_X_OMNI) - || ctrl_x_mode == CTRL_X_EVAL - || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col - - compl_length < 0)) { - return K_BS; - } - - /* Deleted more than what was used to find matches or didn't finish - * finding all matches: need to look for matches all over again. */ - if (curwin->w_cursor.col <= compl_col + compl_length - || ins_compl_need_restart()) { - ins_compl_restart(); - } - - // ins_compl_restart() calls update_screen(0) which may invalidate the pointer - // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic - line = get_cursor_line_ptr(); - - xfree(compl_leader); - compl_leader = vim_strnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col)); - ins_compl_new_leader(); - if (compl_shown_match != NULL) { - // Make sure current match is not a hidden item. - compl_curr_match = compl_shown_match; - } - - return NUL; -} - -/// 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 compl_was_interrupted - || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) - && compl_opt_refresh_always); -} - -/* - * Called after changing "compl_leader". - * Show the popup menu with a different set of matches. - * May also search for matches again if the previous search was interrupted. - */ -static void ins_compl_new_leader(void) -{ - ins_compl_del_pum(); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - compl_used_match = false; - - 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. - // 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_enter_selects = !compl_used_match; - - // 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. - * 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; - } -} - -/* - * Return the length of the completion, from the completion start column to - * the cursor column. Making sure it never goes below zero. - */ -static int ins_compl_len(void) -{ - int off = (int)curwin->w_cursor.col - (int)compl_col; - - if (off < 0) { - return 0; - } - return off; -} - -/* - * Append one character to the match leader. May reduce the number of - * matches. - */ -static void ins_compl_addleader(int c) -{ - int cc; - - if (stop_arrow() == FAIL) { - return; - } - if ((cc = utf_char2len(c)) > 1) { - char buf[MB_MAXBYTES + 1]; - - utf_char2bytes(c, (char *)buf); - buf[cc] = NUL; - ins_char_bytes((char_u *)buf, (size_t)cc); - } else { - ins_char(c); - } - - // If we didn't complete finding matches we must search again. - if (ins_compl_need_restart()) { - ins_compl_restart(); - } - - xfree(compl_leader); - compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, - (size_t)(curwin->w_cursor.col - compl_col)); - ins_compl_new_leader(); -} - -/* - * Setup for finding completions again without leaving CTRL-X mode. Used when - * BS or a key was typed while still searching for matches. - */ -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; - compl_cont_status = 0; - compl_cont_mode = 0; -} - -/* - * Set the first match, the original text. - */ -static void ins_compl_set_original_text(char_u *str) - FUNC_ATTR_NONNULL_ALL -{ - // Replace the original text entry. - // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be - // at the last item for backward completion - if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check - xfree(compl_first_match->cp_str); - compl_first_match->cp_str = vim_strsave(str); - } else if (compl_first_match->cp_prev != NULL - && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) { - xfree(compl_first_match->cp_prev->cp_str); - compl_first_match->cp_prev->cp_str = vim_strsave(str); - } -} - -/* - * Append one character to the match leader. May reduce the number of - * matches. - */ -static void ins_compl_addfrommatch(void) -{ - char_u *p; - int len = (int)curwin->w_cursor.col - (int)compl_col; - int c; - compl_T *cp; - assert(compl_shown_match != NULL); - p = compl_shown_match->cp_str; - if ((int)STRLEN(p) <= len) { // the match is too short - // When still at the original match use the first entry that matches - // the leader. - if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { - p = NULL; - for (cp = compl_shown_match->cp_next; cp != NULL - && cp != compl_first_match; cp = cp->cp_next) { - if (compl_leader == NULL - || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) { - p = cp->cp_str; - break; - } - } - if (p == NULL || (int)STRLEN(p) <= len) { - return; - } - } else { - return; - } - } - p += len; - c = utf_ptr2char((char *)p); - ins_compl_addleader(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; - bool retval = false; - const int prev_mode = ctrl_x_mode; - - /* 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. - */ - if (c != Ctrl_R && vim_is_ctrl_x_key(c)) { - edit_submode_extra = NULL; - } - - // 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_EVENT - || c == K_COMMAND || c == K_LUA) { - return retval; - } - - if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X) { - if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c) - || !vim_is_ctrl_x_key(c)) { - // Not starting another completion mode. - ctrl_x_mode = CTRL_X_CMDLINE; - - // CTRL-X CTRL-Z should stop completion without inserting anything - if (c == Ctrl_Z) { - retval = true; - } - } else { - ctrl_x_mode = CTRL_X_CMDLINE; - - // Other CTRL-X keys first stop completion, then start another - // completion mode. - ins_compl_prep(' '); - ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; - } - } - - // Set "compl_get_longest" when finding the first matches. - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET - || (ctrl_x_mode == CTRL_X_NORMAL && !compl_started)) { - compl_get_longest = (strstr((char *)p_cot, "longest") != NULL); - compl_used_match = true; - } - - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) { - /* - * We have just typed CTRL-X and aren't quite sure which CTRL-X mode - * it will be yet. Now we decide. - */ - switch (c) { - case Ctrl_E: - case Ctrl_Y: - ctrl_x_mode = CTRL_X_SCROLL; - if (!(State & REPLACE_FLAG)) { - edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); - } else { - edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); - } - edit_submode_pre = NULL; - showmode(); - break; - case Ctrl_L: - ctrl_x_mode = CTRL_X_WHOLE_LINE; - break; - case Ctrl_F: - ctrl_x_mode = CTRL_X_FILES; - break; - case Ctrl_K: - ctrl_x_mode = CTRL_X_DICTIONARY; - break; - case Ctrl_R: - // Simply allow ^R to happen without affecting ^X mode - break; - case Ctrl_T: - ctrl_x_mode = CTRL_X_THESAURUS; - break; - case Ctrl_U: - ctrl_x_mode = CTRL_X_FUNCTION; - break; - case Ctrl_O: - ctrl_x_mode = CTRL_X_OMNI; - break; - case 's': - case Ctrl_S: - ctrl_x_mode = CTRL_X_SPELL; - emsg_off++; // Avoid getting the E756 error twice. - spell_back_to_badword(); - emsg_off--; - break; - case Ctrl_RSB: - ctrl_x_mode = CTRL_X_TAGS; - break; - case Ctrl_I: - case K_S_TAB: - ctrl_x_mode = CTRL_X_PATH_PATTERNS; - break; - case Ctrl_D: - ctrl_x_mode = CTRL_X_PATH_DEFINES; - break; - case Ctrl_V: - case Ctrl_Q: - ctrl_x_mode = CTRL_X_CMDLINE; - break; - case Ctrl_Z: - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - showmode(); - retval = true; - break; - case Ctrl_P: - case Ctrl_N: - /* ^X^P means LOCAL expansion if nothing interrupted (eg we - * just started ^X mode, or there were enough ^X's to cancel - * the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) - * do normal expansion when interrupting a different mode (say - * ^X^F^X^P or ^P^X^X^P, see below) - * nothing changes if interrupting mode 0, (eg, the flag - * doesn't change when going to ADDING mode -- Acevedo */ - if (!(compl_cont_status & CONT_INTRPT)) { - compl_cont_status |= CONT_LOCAL; - } else if (compl_cont_mode != 0) { - compl_cont_status &= ~CONT_LOCAL; - } - FALLTHROUGH; - default: - /* If we have typed at least 2 ^X's... for modes != 0, we set - * compl_cont_status = 0 (eg, as if we had just started ^X - * mode). - * For mode 0, we set "compl_cont_mode" to an impossible - * value, in both cases ^X^X can be used to restart the same - * mode (avoiding ADDING mode). - * Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start - * 'complete' and local ^P expansions respectively. - * In mode 0 an extra ^X is needed since ^X^P goes to ADDING - * mode -- Acevedo */ - if (c == Ctrl_X) { - if (compl_cont_mode != 0) { - compl_cont_status = 0; - } else { - compl_cont_mode = CTRL_X_NOT_DEFINED_YET; - } - } - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - showmode(); - break; - } - } else if (ctrl_x_mode != CTRL_X_NORMAL) { - // We're already in CTRL-X mode, do we stay in it? - if (!vim_is_ctrl_x_key(c)) { - if (ctrl_x_mode == CTRL_X_SCROLL) { - ctrl_x_mode = CTRL_X_NORMAL; - } else { - ctrl_x_mode = CTRL_X_FINISHED; - } - edit_submode = NULL; - } - showmode(); - } - - if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) { - /* Show error message from attempted keyword completion (probably - * 'Pattern not found') until another key is hit, then go back to - * showing what mode we are in. */ - showmode(); - if ((ctrl_x_mode == CTRL_X_NORMAL - && c != Ctrl_N - && c != Ctrl_P - && c != Ctrl_R - && !ins_compl_pum_key(c)) - || ctrl_x_mode == CTRL_X_FINISHED) { - /* Get here when we have finished typing a sequence of ^N and - * ^P or other completion characters in CTRL-X mode. Free up - * memory that was used, and make sure we can redo the insert. */ - if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) { - /* - * If any of the original typed text has been changed, eg when - * ignorecase is set, we must add back-spaces to the redo - * buffer. We add as few as necessary to delete just the part - * of the original text that has changed. - * When using the longest match, edited the match or used - * CTRL-E then don't use the current match. - */ - if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { - ptr = compl_curr_match->cp_str; - } else { - ptr = NULL; - } - ins_compl_fixRedoBufForLeader(ptr); - } - - 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) { - do_c_expr_indent(); - want_cindent = false; // don't do it again - } - } else { - int prev_col = curwin->w_cursor.col; - - // put the cursor on the last char, for 'tw' formatting - if (prev_col > 0) { - dec_cursor(); - } - - if (!arrow_used && !ins_need_undo && c != Ctrl_E) { - insertchar(NUL, 0, -1); - } - - if (prev_col > 0 - && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) { - 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 ((c == Ctrl_Y || (compl_enter_selects - && (c == CAR || c == K_KENTER || c == NL))) - && pum_visible()) { - retval = true; - } - - // CTRL-E means completion is Ended, go back to the typed text. - // but only do this, if the Popup is still visible - if (c == Ctrl_E) { - ins_compl_delete(); - if (compl_leader != NULL) { - ins_bytes(compl_leader + ins_compl_len()); - } else if (compl_first_match != NULL) { - ins_bytes(compl_orig_text + ins_compl_len()); - } - retval = true; - } - - auto_format(false, true); - - // Trigger the CompleteDonePre event to give scripts a chance to - // act upon the completion before clearing the info, and restore - // ctrl_x_mode, so that complete_info() can be used. - ctrl_x_mode = prev_mode; - ins_apply_autocmds(EVENT_COMPLETEDONEPRE); - - ins_compl_free(); - compl_started = false; - compl_matches = 0; - if (!shortmess(SHM_COMPLETIONMENU)) { - msg_clr_cmdline(); // necessary for "noshowmode" - } - ctrl_x_mode = CTRL_X_NORMAL; - compl_enter_selects = false; - if (edit_submode != NULL) { - edit_submode = NULL; - 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'. - */ - if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) { - do_c_expr_indent(); - } - // Trigger the CompleteDone event to give scripts a chance to act - // upon the end of completion. - ins_apply_autocmds(EVENT_COMPLETEDONE); - } - } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { - /* Trigger the CompleteDone event to give scripts a chance to act - * upon the (possibly failed) completion. */ - ins_apply_autocmds(EVENT_COMPLETEDONE); - } - - may_trigger_modechanged(); - - /* reset continue_* if we left expansion-mode, if we stay they'll be - * (re)set properly in ins_complete() */ - if (!vim_is_ctrl_x_key(c)) { - compl_cont_status = 0; - compl_cont_mode = 0; - } - - return retval; -} - -/* - * Fix the redo buffer for the completion leader replacing some of the typed - * text. This inserts backspaces and appends the changed text. - * "ptr" is the known leader text or NUL. - */ -static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) -{ - int len; - char_u *p; - char_u *ptr = ptr_arg; - - if (ptr == NULL) { - if (compl_leader != NULL) { - ptr = compl_leader; - } else { - return; // nothing to do - } - } - if (compl_orig_text != NULL) { - p = compl_orig_text; - for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {} - if (len > 0) { - len -= utf_head_off(p, p + len); - } - for (p += len; *p != NUL; MB_PTR_ADV(p)) { - AppendCharToRedobuff(K_BS); - } - } else { - len = 0; - } - AppendToRedobuffLit((char *)ptr + len, -1); -} - -/* - * Loops through the list of windows, loaded-buffers or non-loaded-buffers - * (depending on flag) starting from buf and looking for a non-scanned - * buffer (other than curbuf). curbuf is special, if it is called with - * buf=curbuf then it has to be the first call for a given flag/expansion. - * - * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo - */ -static buf_T *ins_compl_next_buf(buf_T *buf, int flag) -{ - static win_T *wp = NULL; - - if (flag == 'w') { // just windows - if (buf == curbuf || wp == NULL) { // first call for this flag/expansion - wp = curwin; - } - assert(wp); - while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin - && wp->w_buffer->b_scanned) {} - buf = wp->w_buffer; - } else { - /* 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' - * (unlisted buffers) - * When completing whole lines skip unloaded buffers. */ - while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf - && ((flag == 'U' - ? buf->b_p_bl - : (!buf->b_p_bl - || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) - || buf->b_scanned)) {} - } - return buf; -} - -/// Get the user-defined completion function name for completion 'type' -static char_u *get_complete_funcname(int type) -{ - switch (type) { - case CTRL_X_FUNCTION: - return curbuf->b_p_cfu; - case CTRL_X_OMNI: - return curbuf->b_p_ofu; - case CTRL_X_THESAURUS: - return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu; - default: - return (char_u *)""; - } -} - -/// Execute user defined complete function 'completefunc' or 'omnifunc', and -/// get matches in "matches". -/// -/// @param type CTRL_X_OMNI or CTRL_X_FUNCTION -static void expand_by_function(int type, char_u *base) -{ - list_T *matchlist = NULL; - dict_T *matchdict = NULL; - char_u *funcname; - pos_T pos; - typval_T rettv; - const int save_State = State; - - assert(curbuf != NULL); - funcname = get_complete_funcname(type); - if (*funcname == NUL) { - return; - } - - // Call 'completefunc' to obtain the list of matches. - typval_T args[3]; - args[0].v_type = VAR_NUMBER; - args[1].v_type = VAR_STRING; - args[2].v_type = VAR_UNKNOWN; - args[0].vval.v_number = 0; - args[1].vval.v_string = base != NULL ? (char *)base : ""; - - pos = curwin->w_cursor; - // Lock the text to avoid weird things from happening. Also disallow - // switching to another window, it should not be needed and may end up in - // Insert mode in another buffer. - textlock++; - - // Call a function, which returns a list or dict. - if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) { - switch (rettv.v_type) { - case VAR_LIST: - matchlist = rettv.vval.v_list; - break; - case VAR_DICT: - matchdict = rettv.vval.v_dict; - break; - case VAR_SPECIAL: - FALLTHROUGH; - default: - // TODO(brammool): Give error message? - tv_clear(&rettv); - break; - } - } - textlock--; - - curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); - if (!equalpos(curwin->w_cursor, pos)) { - emsg(_(e_compldel)); - goto theend; - } - - if (matchlist != NULL) { - ins_compl_add_list(matchlist); - } else if (matchdict != NULL) { - ins_compl_add_dict(matchdict); - } - -theend: - // Restore State, it might have been changed. - State = save_State; - - if (matchdict != NULL) { - tv_dict_unref(matchdict); - } - if (matchlist != NULL) { - tv_list_unref(matchlist); - } -} - -/* - * Add completions from a list. - */ -static void ins_compl_add_list(list_T *const list) -{ - Direction dir = compl_direction; - - // Go through the List with matches and add each of them. - TV_LIST_ITER(list, li, { - if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir, true) == OK) { - // If dir was BACKWARD then honor it just once. - dir = FORWARD; - } else if (did_emsg) { - break; - } - }); -} - -/* - * Add completions from a dict. - */ -static void ins_compl_add_dict(dict_T *dict) -{ - dictitem_T *di_refresh; - dictitem_T *di_words; - - // Check for optional "refresh" item. - compl_opt_refresh_always = false; - di_refresh = tv_dict_find(dict, S_LEN("refresh")); - if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - const char *v = (const char *)di_refresh->di_tv.vval.v_string; - - if (v != NULL && strcmp(v, "always") == 0) { - compl_opt_refresh_always = true; - } - } - - // Add completions from a "words" list. - di_words = tv_dict_find(dict, S_LEN("words")); - if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { - ins_compl_add_list(di_words->di_tv.vval.v_list); - } -} - -/// Add a match to the list of matches from VimL object -/// -/// @param[in] tv Object to get matches from. -/// @param[in] dir Completion direction. -/// @param[in] fast use fast_breakcheck() instead of os_breakcheck(). -/// -/// @return NOTDONE if the given string is already in the list of completions, -/// otherwise it is added to the list and OK is returned. FAIL will be -/// returned in case of error. -int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) - FUNC_ATTR_NONNULL_ALL -{ - const char *word; - bool dup = false; - bool empty = false; - int flags = fast ? CP_FAST : 0; - char *(cptext[CPT_COUNT]); - typval_T user_data; - - user_data.v_type = VAR_UNKNOWN; - if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - word = tv_dict_get_string(tv->vval.v_dict, "word", false); - cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); - cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); - cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); - cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); - tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data); - - if (tv_dict_get_number(tv->vval.v_dict, "icase")) { - flags |= CP_ICASE; - } - dup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); - empty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); - if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL - && tv_dict_get_number(tv->vval.v_dict, "equal")) { - flags |= CP_EQUAL; - } - } else { - word = tv_get_string_chk(tv); - memset(cptext, 0, sizeof(cptext)); - } - if (word == NULL || (!empty && *word == NUL)) { - for (size_t i = 0; i < CPT_COUNT; i++) { - xfree(cptext[i]); - } - return FAIL; - } - return ins_compl_add((char_u *)word, -1, NULL, - (char_u **)cptext, true, &user_data, dir, flags, dup); -} - -/// Returns true when using a user-defined function for thesaurus completion. -static bool thesaurus_func_complete(int type) -{ - return type == CTRL_X_THESAURUS - && (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL); -} - -// Get the next expansion(s), using "compl_pattern". -// The search starts at position "ini" in curbuf and in the direction -// compl_direction. -// When "compl_started" is false start at that position, otherwise continue -// where we stopped searching before. -// This may return before finding all the matches. -// Return the total number of matches or -1 if still unknown -- Acevedo -static int ins_compl_get_exp(pos_T *ini) -{ - static pos_T first_match_pos; - static pos_T last_match_pos; - static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' - static bool found_all = false; // Found all matches of a - // certain type. - static buf_T *ins_buf = NULL; // buffer being scanned - - pos_T *pos; - char_u **matches; - int save_p_scs; - bool save_p_ws; - int save_p_ic; - int i; - int num_matches; - int len; - int found_new_match; - int type = ctrl_x_mode; - char_u *ptr; - char_u *dict = NULL; - int dict_f = 0; - bool set_match_pos; - pos_T prev_pos = { 0, 0, 0 }; - int l_ctrl_x_mode = ctrl_x_mode; - - assert(curbuf != NULL); - - if (!compl_started) { - FOR_ALL_BUFFERS(buf) { - buf->b_scanned = false; - } - found_all = false; - ins_buf = curbuf; - e_cpt = (compl_cont_status & CONT_LOCAL) - ? (char_u *)"." : curbuf->b_p_cpt; - last_match_pos = first_match_pos = *ini; - } else if (ins_buf != curbuf && !buf_valid(ins_buf)) { - ins_buf = curbuf; // In case the buffer was wiped out. - } - - compl_old_match = compl_curr_match; // remember the last current match - pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; - - // For ^N/^P loop over all the flags/windows/buffers in 'complete' - for (;;) { - found_new_match = FAIL; - set_match_pos = false; - - assert(l_ctrl_x_mode == ctrl_x_mode); - - // For ^N/^P pick a new entry from e_cpt if compl_started is off, - // or if found_all says this entry is done. For ^X^L only use the - // entries from 'complete' that look in loaded buffers. - if ((l_ctrl_x_mode == CTRL_X_NORMAL - || CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - && (!compl_started || found_all)) { - found_all = false; - while (*e_cpt == ',' || *e_cpt == ' ') { - e_cpt++; - } - if (*e_cpt == '.' && !curbuf->b_scanned) { - ins_buf = curbuf; - first_match_pos = *ini; - // Move the cursor back one character so that ^N can match the - // word immediately after the cursor. - if (ctrl_x_mode == CTRL_X_NORMAL && dec(&first_match_pos) < 0) { - // Move the cursor to after the last character in the - // buffer, so that word at start of buffer is found - // correctly. - first_match_pos.lnum = ins_buf->b_ml.ml_line_count; - first_match_pos.col = (colnr_T)STRLEN(ml_get(first_match_pos.lnum)); - } - last_match_pos = first_match_pos; - type = 0; - - // Remember the first match so that the loop stops when we - // wrap and come back there a second time. - set_match_pos = true; - } else if (vim_strchr("buwU", *e_cpt) != NULL - && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { - // Scan a buffer, but not the current one. - if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer - compl_started = true; - first_match_pos.col = last_match_pos.col = 0; - first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; - last_match_pos.lnum = 0; - type = 0; - } else { // unloaded buffer, scan like dictionary - found_all = true; - if (ins_buf->b_fname == NULL) { - continue; - } - type = CTRL_X_DICTIONARY; - dict = (char_u *)ins_buf->b_fname; - dict_f = DICT_EXACT; - } - msg_hist_off = true; // reset in msg_trunc_attr() - vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), - ins_buf->b_fname == NULL - ? buf_spname(ins_buf) - : ins_buf->b_sfname == NULL - ? ins_buf->b_fname - : ins_buf->b_sfname); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); - } else if (*e_cpt == NUL) { - break; - } else { - if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) { - type = -1; - } else if (*e_cpt == 'k' || *e_cpt == 's') { - if (*e_cpt == 'k') { - type = CTRL_X_DICTIONARY; - } else { - type = CTRL_X_THESAURUS; - } - if (*++e_cpt != ',' && *e_cpt != NUL) { - dict = e_cpt; - dict_f = DICT_FIRST; - } - } else if (*e_cpt == 'i') { - type = CTRL_X_PATH_PATTERNS; - } else if (*e_cpt == 'd') { - type = CTRL_X_PATH_DEFINES; - } else if (*e_cpt == ']' || *e_cpt == 't') { - msg_hist_off = true; // reset in msg_trunc_attr() - type = CTRL_X_TAGS; - vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); - (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); - } else { - type = -1; - } - - // in any case e_cpt is advanced to the next entry - (void)copy_option_part((char **)&e_cpt, (char *)IObuff, IOSIZE, ","); - - found_all = true; - if (type == -1) { - continue; - } - } - } - - // If complete() was called then compl_pattern has been reset. - // The following won't work then, bail out. - if (compl_pattern == NULL) { - break; - } - - switch (type) { - case -1: - break; - case CTRL_X_PATH_PATTERNS: - case CTRL_X_PATH_DEFINES: - find_pattern_in_path(compl_pattern, compl_direction, - STRLEN(compl_pattern), FALSE, FALSE, - ((type == CTRL_X_PATH_DEFINES - && !(compl_cont_status & CONT_SOL)) - ? FIND_DEFINE - : FIND_ANY), - 1L, ACTION_EXPAND, 1, MAXLNUM); - break; - - case CTRL_X_DICTIONARY: - case CTRL_X_THESAURUS: - if (thesaurus_func_complete(type)) { - expand_by_function(type, compl_pattern); - } else { - ins_compl_dictionaries(dict != NULL ? dict - : (type == CTRL_X_THESAURUS - ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) - : (*curbuf->b_p_dict == - NUL ? p_dict : curbuf->b_p_dict)), - compl_pattern, - dict != NULL ? dict_f : 0, type == CTRL_X_THESAURUS); - } - dict = NULL; - break; - - case CTRL_X_TAGS: - // set p_ic according to p_ic, p_scs and pat for find_tags(). - save_p_ic = p_ic; - p_ic = ignorecase(compl_pattern); - - // Find up to TAG_MANY matches. Avoids that an enormous number - // of matches is found when compl_pattern is empty - g_tag_at_cursor = true; - if (find_tags(compl_pattern, &num_matches, &matches, - TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP - | (l_ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0), - TAG_MANY, (char_u *)curbuf->b_ffname) == OK && num_matches > 0) { - ins_compl_add_matches(num_matches, matches, p_ic); - } - g_tag_at_cursor = false; - p_ic = save_p_ic; - break; - - case CTRL_X_FILES: - if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, - EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { - // May change home directory back to "~". - tilde_replace(compl_pattern, num_matches, matches); -#ifdef BACKSLASH_IN_FILENAME - if (curbuf->b_p_csl[0] != NUL) { - for (int i = 0; i < num_matches; i++) { - char_u *ptr = matches[i]; - while (*ptr != NUL) { - if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') { - *ptr = '/'; - } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') { - *ptr = '\\'; - } - ptr += utfc_ptr2len(ptr); - } - } - } -#endif - ins_compl_add_matches(num_matches, matches, p_fic || p_wic); - } - break; - - case CTRL_X_CMDLINE: - case CTRL_X_CMDLINE_CTRL_X: - if (expand_cmdline(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), - &num_matches, &matches) == EXPAND_OK) { - ins_compl_add_matches(num_matches, matches, false); - } - break; - - case CTRL_X_FUNCTION: - case CTRL_X_OMNI: - expand_by_function(type, compl_pattern); - break; - - case CTRL_X_SPELL: - num_matches = expand_spelling(first_match_pos.lnum, - compl_pattern, &matches); - if (num_matches > 0) { - ins_compl_add_matches(num_matches, matches, p_ic); - } - break; - - default: // normal ^P/^N and ^X^L - // If 'infercase' is set, don't use 'smartcase' here - save_p_scs = p_scs; - assert(ins_buf); - if (ins_buf->b_p_inf) { - p_scs = FALSE; - } - - // Buffers other than curbuf are scanned from the beginning or the - // end but never from the middle, thus setting nowrapscan in this - // buffers is a good idea, on the other hand, we always set - // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb - save_p_ws = p_ws; - if (ins_buf != curbuf) { - p_ws = false; - } else if (*e_cpt == '.') { - p_ws = true; - } - bool looped_around = false; - for (;;) { - bool cont_s_ipos = false; - - msg_silent++; // Don't want messages for wrapscan. - // CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode) || word-wise search that - // has added a word that was at the beginning of the line. - if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode) - || (compl_cont_status & CONT_SOL)) { - found_new_match = search_for_exact_line(ins_buf, pos, - compl_direction, - compl_pattern); - } else { - found_new_match = searchit(NULL, ins_buf, pos, NULL, - compl_direction, - compl_pattern, 1L, - SEARCH_KEEP + SEARCH_NFMSG, - RE_LAST, NULL); - } - msg_silent--; - if (!compl_started || set_match_pos) { - // set "compl_started" even on fail - compl_started = true; - first_match_pos = *pos; - last_match_pos = *pos; - set_match_pos = false; - } else if (first_match_pos.lnum == last_match_pos.lnum - && first_match_pos.col == last_match_pos.col) { - found_new_match = FAIL; - } else if ((compl_direction == FORWARD) - && (prev_pos.lnum > pos->lnum - || (prev_pos.lnum == pos->lnum - && prev_pos.col >= pos->col))) { - if (looped_around) { - found_new_match = FAIL; - } else { - looped_around = true; - } - } else if ((compl_direction != FORWARD) - && (prev_pos.lnum < pos->lnum - || (prev_pos.lnum == pos->lnum - && prev_pos.col <= pos->col))) { - if (looped_around) { - found_new_match = FAIL; - } else { - looped_around = true; - } - } - prev_pos = *pos; - if (found_new_match == FAIL) { - if (ins_buf == curbuf) { - found_all = true; - } - break; - } - - // when ADDING, the text before the cursor matches, skip it - if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf - && ini->lnum == pos->lnum - && ini->col == pos->col) { - continue; - } - ptr = ml_get_buf(ins_buf, pos->lnum, false) + pos->col; - if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) { - if (compl_cont_status & CONT_ADDING) { - if (pos->lnum >= ins_buf->b_ml.ml_line_count) { - continue; - } - ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); - if (!p_paste) { - ptr = (char_u *)skipwhite((char *)ptr); - } - } - len = (int)STRLEN(ptr); - } else { - char_u *tmp_ptr = ptr; - - if (compl_cont_status & CONT_ADDING) { - tmp_ptr += compl_length; - // Skip if already inside a word. - if (vim_iswordp(tmp_ptr)) { - continue; - } - // Find start of next word. - tmp_ptr = find_word_start(tmp_ptr); - } - // Find end of this word. - tmp_ptr = find_word_end(tmp_ptr); - len = (int)(tmp_ptr - ptr); - - if ((compl_cont_status & CONT_ADDING) - && len == compl_length) { - if (pos->lnum < ins_buf->b_ml.ml_line_count) { - // Try next line, if any. the new word will be "join" as if the - // normal command "J" was used. IOSIZE is always greater than - // compl_length, so the next STRNCPY always works -- Acevedo - STRNCPY(IObuff, ptr, len); - ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); - tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr); - // Find start of next word. - tmp_ptr = find_word_start(tmp_ptr); - // Find end of next word. - tmp_ptr = find_word_end(tmp_ptr); - if (tmp_ptr > ptr) { - if (*ptr != ')' && IObuff[len - 1] != TAB) { - if (IObuff[len - 1] != ' ') { - IObuff[len++] = ' '; - } - // IObuf =~ "\k.* ", thus len >= 2 - if (p_js - && (IObuff[len - 2] == '.' - || IObuff[len - 2] == '?' - || IObuff[len - 2] == '!')) { - IObuff[len++] = ' '; - } - } - // copy as much as possible of the new word - if (tmp_ptr - ptr >= IOSIZE - len) { - tmp_ptr = ptr + IOSIZE - len - 1; - } - STRLCPY(IObuff + len, ptr, IOSIZE - len); - len += (int)(tmp_ptr - ptr); - cont_s_ipos = true; - } - IObuff[len] = NUL; - ptr = IObuff; - } - if (len == compl_length) { - continue; - } - } - } - if (ins_compl_add_infercase(ptr, len, p_ic, - ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname, - 0, cont_s_ipos) != NOTDONE) { - found_new_match = OK; - break; - } - } - p_scs = save_p_scs; - p_ws = save_p_ws; - } - - // check if compl_curr_match has changed, (e.g. other type of - // expansion added something) - if (type != 0 && compl_curr_match != compl_old_match) { - found_new_match = OK; - } - - // break the loop for specialized modes (use 'complete' just for the - // generic l_ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match - if ((l_ctrl_x_mode != CTRL_X_NORMAL - && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - || found_new_match != FAIL) { - if (got_int) { - break; - } - // Fill the popup menu as soon as possible. - if (type != -1) { - ins_compl_check_keys(0, false); - } - - if ((l_ctrl_x_mode != CTRL_X_NORMAL - && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - || compl_interrupted) { - break; - } - compl_started = true; - } else { - // Mark a buffer scanned when it has been scanned completely - if (type == 0 || type == CTRL_X_PATH_PATTERNS) { - assert(ins_buf); - ins_buf->b_scanned = true; - } - - compl_started = false; - } - } - compl_started = true; - - if ((l_ctrl_x_mode == CTRL_X_NORMAL - || CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) - && *e_cpt == NUL) { // Got to end of 'complete' - found_new_match = FAIL; - } - - i = -1; // total of matches, unknown - if (found_new_match == FAIL - || (l_ctrl_x_mode != CTRL_X_NORMAL - && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode))) { - i = ins_compl_make_cyclic(); - } - - if (compl_old_match != NULL) { - // If several matches were added (FORWARD) or the search failed and has - // just been made cyclic then we have to move compl_curr_match to the - // next or previous entry (if any) -- Acevedo - compl_curr_match = compl_direction == FORWARD - ? compl_old_match->cp_next - : compl_old_match->cp_prev; - if (compl_curr_match == NULL) { - compl_curr_match = compl_old_match; - } - } - may_trigger_modechanged(); - - return i; -} - -// Delete the old text being completed. -static void ins_compl_delete(void) -{ - int col; - - // In insert mode: Delete the typed part. - // In replace mode: Put the old characters back, if any. - col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); - if ((int)curwin->w_cursor.col > col) { - if (stop_arrow() == FAIL) { - return; - } - backspace_until_column(col); - } - - // TODO(vim): is this sufficient for redrawing? Redrawing everything - // causes flicker, thus we can't do that. - changed_cline_bef_curs(); - // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); -} - -// Insert the new text being completed. -// "in_compl_func" is TRUE when called from complete_check(). -static void ins_compl_insert(int in_compl_func) -{ - ins_bytes(compl_shown_match->cp_str + ins_compl_len()); - compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT); - - dict_T *dict = ins_compl_dict_alloc(compl_shown_match); - set_vim_var_dict(VV_COMPLETED_ITEM, dict); - if (!in_compl_func) { - compl_curr_match = compl_shown_match; - } -} - -// Convert to complete item dict -static dict_T *ins_compl_dict_alloc(compl_T *match) -{ - // { word, abbr, menu, kind, info } - dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); - tv_dict_add_str(dict, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(dict, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(dict, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(dict, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(dict, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); - if (match->cp_user_data.v_type == VAR_UNKNOWN) { - tv_dict_add_str(dict, S_LEN("user_data"), ""); - } else { - tv_dict_add_tv(dict, S_LEN("user_data"), &match->cp_user_data); - } - return dict; -} - -/// Fill in the next completion in the current direction. -/// If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to -/// get more completions. If it is FALSE, then we just do nothing when there -/// are no more completions in a given direction. The latter case is used when -/// we are still in the middle of finding completions, to allow browsing -/// through the ones found so far. -/// @return the total number of matches, or -1 if still unknown -- webb. -/// -/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use -/// compl_shown_match here. -/// -/// Note that this function may be called recursively once only. First with -/// "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn -/// calls this function with "allow_get_expansion" FALSE. -/// -/// @param count Repeat completion this many times; should be at least 1 -/// @param insert_match Insert the newly selected match -/// @param in_compl_func Called from complete_check() -static int ins_compl_next(int allow_get_expansion, int count, int insert_match, int in_compl_func) -{ - int num_matches = -1; - int todo = count; - compl_T *found_compl = NULL; - bool found_end = false; - const bool started = compl_started; - - /* When user complete function return -1 for findstart which is next - * time of 'always', compl_shown_match become NULL. */ - if (compl_shown_match == NULL) { - return -1; - } - - if (compl_leader != NULL - && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) { - // Set "compl_shown_match" to the actually shown match, it may differ - // when "compl_leader" is used to omit some of the matches. - while (!ins_compl_equal(compl_shown_match, - compl_leader, STRLEN(compl_leader)) - && compl_shown_match->cp_next != NULL - && compl_shown_match->cp_next != compl_first_match) { - compl_shown_match = compl_shown_match->cp_next; - } - - /* If we didn't find it searching forward, and compl_shows_dir is - * backward, find the last match. */ - if (compl_shows_dir == BACKWARD - && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) - && (compl_shown_match->cp_next == NULL - || compl_shown_match->cp_next == compl_first_match)) { - while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) - && compl_shown_match->cp_prev != NULL - && compl_shown_match->cp_prev != compl_first_match) { - compl_shown_match = compl_shown_match->cp_prev; - } - } - } - - if (allow_get_expansion && insert_match - && (!(compl_get_longest || compl_restarting) || compl_used_match)) { - // Delete old text to be replaced - ins_compl_delete(); - } - - // When finding the longest common text we stick at the original text, - // don't let CTRL-N or CTRL-P move to the first match. - bool advance = count != 1 || !allow_get_expansion || !compl_get_longest; - - // When restarting the search don't insert the first match either. - if (compl_restarting) { - advance = false; - compl_restarting = false; - } - - /* Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap - * around. */ - while (--todo >= 0) { - if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; - found_end = (compl_first_match != NULL - && (compl_shown_match->cp_next == compl_first_match - || compl_shown_match == compl_first_match)); - } else if (compl_shows_dir == BACKWARD - && compl_shown_match->cp_prev != NULL) { - found_end = (compl_shown_match == compl_first_match); - compl_shown_match = compl_shown_match->cp_prev; - found_end |= (compl_shown_match == compl_first_match); - } else { - if (!allow_get_expansion) { - if (advance) { - if (compl_shows_dir == BACKWARD) { - compl_pending -= todo + 1; - } else { - compl_pending += todo + 1; - } - } - return -1; - } - - if (!compl_no_select && advance) { - if (compl_shows_dir == BACKWARD) { - --compl_pending; - } else { - ++compl_pending; - } - } - - // Find matches. - num_matches = ins_compl_get_exp(&compl_startpos); - - // handle any pending completions - while (compl_pending != 0 && compl_direction == compl_shows_dir - && advance) { - if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; - --compl_pending; - } - if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) { - compl_shown_match = compl_shown_match->cp_prev; - ++compl_pending; - } else { - break; - } - } - found_end = false; - } - if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0 - && compl_leader != NULL - && !ins_compl_equal(compl_shown_match, - compl_leader, STRLEN(compl_leader))) { - todo++; - } else { - // Remember a matching item. - found_compl = compl_shown_match; - } - - // Stop at the end of the list when we found a usable match. - if (found_end) { - if (found_compl != NULL) { - compl_shown_match = found_compl; - break; - } - todo = 1; // use first usable match after wrapping around - } - } - - // Insert the text of the new completion, or the compl_leader. - if (compl_no_insert && !started) { - ins_bytes(compl_orig_text + ins_compl_len()); - compl_used_match = false; - } else if (insert_match) { - if (!compl_get_longest || compl_used_match) { - ins_compl_insert(in_compl_func); - } else { - ins_bytes(compl_leader + ins_compl_len()); - } - } else { - compl_used_match = false; - } - - if (!allow_get_expansion) { - // redraw to show the user what was inserted - update_screen(0); - - // display the updated popup menu - ins_compl_show_pum(); - - // Delete old text to be replaced, since we're still searching and - // don't want to match ourselves! - ins_compl_delete(); - } - - /* Enter will select a match when the match wasn't inserted and the popup - * menu is visible. */ - if (compl_no_insert && !started) { - compl_enter_selects = TRUE; - } else { - compl_enter_selects = !insert_match && compl_match_array != NULL; - } - - /* - * Show the file name for the match (if any) - * Truncate the file name to avoid a wait for return. - */ - if (compl_shown_match->cp_fname != NULL) { - char *lead = _("match in file"); - int space = sc_col - vim_strsize(lead) - 2; - char *s; - char *e; - - if (space > 0) { - // We need the tail that fits. With double-byte encoding going - // back from the end is very slow, thus go from the start and keep - // the text that fits in "space" between "s" and "e". - for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) { - space -= ptr2cells(e); - while (space < 0) { - space += ptr2cells(s); - MB_PTR_ADV(s); - } - } - msg_hist_off = true; - vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, - (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s); - msg((char *)IObuff); - msg_hist_off = false; - redraw_cmdline = false; // don't overwrite! - } - } - - return num_matches; -} - -void pum_ext_select_item(int item, bool insert, bool finish) -{ - if (!pum_visible() || item < -1 || item >= compl_match_arraysize) { - return; - } - pum_want.active = true; - pum_want.item = item; - pum_want.insert = insert; - pum_want.finish = finish; -} - -// Call this while finding completions, to check whether the user has hit a key -// that should change the currently displayed completion, or exit completion -// mode. Also, when compl_pending is not zero, show a completion as soon as -// possible. -- webb -// "frequency" specifies out of how many calls we actually check. -// "in_compl_func" is TRUE when called from complete_check(), don't set -// compl_curr_match. -void ins_compl_check_keys(int frequency, int in_compl_func) -{ - static int count = 0; - - // Don't check when reading keys from a script, :normal or feedkeys(). - // That would break the test scripts. But do check for keys when called - // from complete_check(). - if (!in_compl_func && (using_script() || ex_normal_busy)) { - return; - } - - // Only do this at regular intervals - if (++count < frequency) { - return; - } - count = 0; - - /* Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key() - * can't do its work correctly. */ - int c = vpeekc_any(); - if (c != NUL) { - if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) { - c = safe_vgetc(); // Eat the character - compl_shows_dir = ins_compl_key2dir(c); - (void)ins_compl_next(false, ins_compl_key2count(c), - c != K_UP && c != K_DOWN, in_compl_func); - } else { - /* Need to get the character to have KeyTyped set. We'll put it - * back with vungetc() below. But skip K_IGNORE. */ - c = safe_vgetc(); - if (c != K_IGNORE) { - /* Don't interrupt completion when the character wasn't typed, - * e.g., when doing @q to replay keys. */ - if (c != Ctrl_R && KeyTyped) { - compl_interrupted = TRUE; - } - - vungetc(c); - } - } - } - if (compl_pending != 0 && !got_int && !compl_no_insert) { - int todo = compl_pending > 0 ? compl_pending : -compl_pending; - - compl_pending = 0; - (void)ins_compl_next(false, todo, true, in_compl_func); - } -} - -/* - * Decide the direction of Insert mode complete from the key typed. - * Returns BACKWARD or FORWARD. - */ -static int ins_compl_key2dir(int c) -{ - if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { - return pum_want.item < pum_selected_item ? BACKWARD : FORWARD; - } - if (c == Ctrl_P || c == Ctrl_L - || c == K_PAGEUP || c == K_KPAGEUP - || c == K_S_UP || c == K_UP) { - return BACKWARD; - } - return FORWARD; -} - -/// 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); -} - -/* - * Decide the number of completions to move forward. - * Returns 1 for most keys, height of the popup menu for page-up/down keys. - */ -static int ins_compl_key2count(int c) -{ - int h; - - if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { - int offset = pum_want.item - pum_selected_item; - return abs(offset); - } - - if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) { - h = pum_get_height(); - if (h > 3) { - h -= 2; // keep some context - } - return h; - } - return 1; -} - -/// 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: - case K_DOWN: - case K_PAGEDOWN: - case K_KPAGEDOWN: - case K_S_DOWN: - case K_PAGEUP: - case K_KPAGEUP: - case K_S_UP: - return false; - case K_EVENT: - case K_COMMAND: - case K_LUA: - return pum_want.active && pum_want.insert; - } - return true; -} - -/* - * Do Insert mode completion. - * 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, bool enable_pum) -{ - char_u *line; - int startcol = 0; // column where searched text starts - colnr_T curs_col; // cursor column - int n; - int save_w_wrow; - int save_w_leftcol; - int insert_match; - const bool save_did_ai = did_ai; - int flags = CP_ORIGINAL_TEXT; - - compl_direction = ins_compl_key2dir(c); - insert_match = ins_compl_use_match(c); - - if (!compl_started) { - // First time we hit ^N or ^P (in a row, I mean) - - did_ai = false; - did_si = false; - can_si = false; - can_si_back = false; - if (stop_arrow() == FAIL) { - return FAIL; - } - - line = ml_get(curwin->w_cursor.lnum); - curs_col = curwin->w_cursor.col; - compl_pending = 0; - - /* If this same ctrl_x_mode has been interrupted use the text from - * "compl_startpos" to the cursor as a pattern to add a new word - * instead of expand the one before the cursor, in word-wise if - * "compl_startpos" is not in the same line as the cursor then fix it - * (the line has been split because it was longer than 'tw'). if SOL - * is set then skip the previous pattern, a word at the beginning of - * the line has been inserted, we'll look for that -- Acevedo. */ - if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT - && compl_cont_mode == ctrl_x_mode) { - /* - * it is a continued search - */ - compl_cont_status &= ~CONT_INTRPT; // remove INTRPT - if (ctrl_x_mode == CTRL_X_NORMAL - || ctrl_x_mode == CTRL_X_PATH_PATTERNS - || ctrl_x_mode == CTRL_X_PATH_DEFINES) { - if (compl_startpos.lnum != curwin->w_cursor.lnum) { - // line (probably) wrapped, set compl_startpos to the - // first non_blank in the line, if it is not a wordchar - // include it to get a better pattern, but then we don't - // want the "\\<" prefix, check it below. - compl_col = (colnr_T)getwhitecols(line); - compl_startpos.col = compl_col; - compl_startpos.lnum = curwin->w_cursor.lnum; - compl_cont_status &= ~CONT_SOL; // clear SOL if present - } else { - /* S_IPOS was set when we inserted a word that was at the - * beginning of the line, which means that we'll go to SOL - * mode but first we need to redefine compl_startpos */ - if (compl_cont_status & CONT_S_IPOS) { - compl_cont_status |= CONT_SOL; - compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line - + compl_length - + compl_startpos.col) - line); - } - compl_col = compl_startpos.col; - } - compl_length = curwin->w_cursor.col - (int)compl_col; - /* IObuff is used to add a "word from the next line" would we - * have enough space? just being paranoid */ -#define MIN_SPACE 75 - if (compl_length > (IOSIZE - MIN_SPACE)) { - compl_cont_status &= ~CONT_SOL; - compl_length = (IOSIZE - MIN_SPACE); - compl_col = curwin->w_cursor.col - compl_length; - } - compl_cont_status |= CONT_ADDING | CONT_N_ADDS; - if (compl_length < 1) { - compl_cont_status &= CONT_LOCAL; - } - } else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - compl_cont_status = CONT_ADDING | CONT_N_ADDS; - } else { - compl_cont_status = 0; - } - } else { - compl_cont_status &= CONT_LOCAL; - } - - if (!(compl_cont_status & CONT_ADDING)) { // normal expansion - compl_cont_mode = ctrl_x_mode; - if (ctrl_x_mode != CTRL_X_NORMAL) { - // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL - compl_cont_status = 0; - } - compl_cont_status |= CONT_N_ADDS; - compl_startpos = curwin->w_cursor; - startcol = (int)curs_col; - compl_col = 0; - } - - // Work out completion pattern and original text -- webb - if (ctrl_x_mode == CTRL_X_NORMAL - || (ctrl_x_mode & CTRL_X_WANT_IDENT - && !thesaurus_func_complete(ctrl_x_mode))) { - if ((compl_cont_status & CONT_SOL) - || ctrl_x_mode == CTRL_X_PATH_DEFINES) { - if (!(compl_cont_status & CONT_ADDING)) { - while (--startcol >= 0 && vim_isIDc(line[startcol])) {} - compl_col += ++startcol; - compl_length = curs_col - startcol; - } - if (p_ic) { - compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0); - } else { - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } - } else if (compl_cont_status & CONT_ADDING) { - char_u *prefix = (char_u *)"\\<"; - - // we need up to 2 extra chars for the prefix - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); - if (!vim_iswordp(line + compl_col) - || (compl_col > 0 - && ( - vim_iswordp(mb_prevptr(line, line + compl_col)) - ))) { - prefix = (char_u *)""; - } - STRCPY(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 - compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); - compl_col += curs_col; - compl_length = 0; - } else { - // Search the point of change class of multibyte character - // or not a word single byte character backward. - startcol -= utf_head_off(line, line + startcol); - int base_class = mb_get_class(line + startcol); - while (--startcol >= 0) { - int head_off = utf_head_off(line, line + startcol); - if (base_class != mb_get_class(line + startcol - head_off)) { - break; - } - startcol -= head_off; - } - compl_col += ++startcol; - compl_length = (int)curs_col - startcol; - if (compl_length == 1) { - /* Only match word with at least two chars -- webb - * there's no need to call quote_meta, - * xmalloc(7) is enough -- Acevedo - */ - compl_pattern = xmalloc(7); - STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, 1); - STRCAT(compl_pattern, "\\k"); - } else { - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); - STRCPY(compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, - compl_length); - } - } - } else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - compl_col = (colnr_T)getwhitecols(line); - compl_length = (int)curs_col - (int)compl_col; - if (compl_length < 0) { // cursor in indent: empty pattern - compl_length = 0; - } - if (p_ic) { - compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0); - } else { - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } - } else if (ctrl_x_mode == CTRL_X_FILES) { - // Go back to just before the first filename character. - if (startcol > 0) { - char_u *p = line + startcol; - - MB_PTR_BACK(line, p); - while (p > line && vim_isfilec(utf_ptr2char((char *)p))) { - MB_PTR_BACK(line, p); - } - if (p == line && vim_isfilec(utf_ptr2char((char *)p))) { - startcol = 0; - } else { - startcol = (int)(p - line) + 1; - } - } - - compl_col += startcol; - compl_length = (int)curs_col - startcol; - compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES); - } else if (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X) { - compl_pattern = vim_strnsave(line, (size_t)curs_col); - set_cmd_context(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), curs_col, false); - if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL - || compl_xp.xp_context == EXPAND_NOTHING) { - // No completion possible, use an empty pattern to get a - // "pattern not found" message. - compl_col = curs_col; - } else { - compl_col = (int)((char_u *)compl_xp.xp_pattern - compl_pattern); - } - compl_length = curs_col - compl_col; - } else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI - || thesaurus_func_complete(ctrl_x_mode)) { - // Call user defined function 'completefunc' with "a:findstart" - // set to 1 to obtain the length of text to use for completion. - char_u *funcname; - pos_T pos; - const int save_State = State; - - // Call 'completefunc' or 'omnifunc' and get pattern length as a string - funcname = get_complete_funcname(ctrl_x_mode); - if (*funcname == NUL) { - semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION - ? "completefunc" : "omnifunc"); - // restore did_ai, so that adding comment leader works - did_ai = save_did_ai; - return FAIL; - } - - typval_T args[3]; - args[0].v_type = VAR_NUMBER; - args[1].v_type = VAR_STRING; - args[2].v_type = VAR_UNKNOWN; - args[0].vval.v_number = 1; - args[1].vval.v_string = ""; - - pos = curwin->w_cursor; - textlock++; - colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args); - textlock--; - - State = save_State; - curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); - if (!equalpos(curwin->w_cursor, pos)) { - emsg(_(e_compldel)); - return FAIL; - } - - // Return value -2 means the user complete function wants to cancel the - // complete without an error, do the same if the function did not execute - // successfully. - if (col == -2 || aborting()) { - return FAIL; - } - // Return value -3 does the same as -2 and leaves CTRL-X mode. - if (col == -3) { - ctrl_x_mode = CTRL_X_NORMAL; - edit_submode = NULL; - if (!shortmess(SHM_COMPLETIONMENU)) { - msg_clr_cmdline(); - } - return FAIL; - } - - // Reset extended parameters of completion, when start new - // completion. - compl_opt_refresh_always = false; - - if (col < 0) { - col = curs_col; - } - compl_col = col; - if (compl_col > curs_col) { - compl_col = curs_col; - } - - /* Setup variables for completion. Need to obtain "line" again, - * it may have become invalid. */ - line = ml_get(curwin->w_cursor.lnum); - compl_length = curs_col - compl_col; - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } else if (ctrl_x_mode == CTRL_X_SPELL) { - if (spell_bad_len > 0) { - assert(spell_bad_len <= INT_MAX); - compl_col = curs_col - (int)spell_bad_len; - } else { - compl_col = spell_word_start(startcol); - } - if (compl_col >= (colnr_T)startcol) { - compl_length = 0; - compl_col = curs_col; - } else { - spell_expand_check_cap(compl_col); - compl_length = (int)curs_col - compl_col; - } - // Need to obtain "line" again, it may have become invalid. - line = ml_get(curwin->w_cursor.lnum); - compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length); - } else { - internal_error("ins_complete()"); - return FAIL; - } - - if (compl_cont_status & CONT_ADDING) { - edit_submode_pre = (char_u *)_(" Adding"); - if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { - // Insert a new line, keep indentation but ignore 'comments' - char_u *old = curbuf->b_p_com; - - curbuf->b_p_com = (char_u *)""; - compl_startpos.lnum = curwin->w_cursor.lnum; - compl_startpos.col = compl_col; - ins_eol('\r'); - curbuf->b_p_com = old; - compl_length = 0; - compl_col = curwin->w_cursor.col; - } - } else { - edit_submode_pre = NULL; - compl_startpos.col = compl_col; - } - - if (compl_cont_status & CONT_LOCAL) { - edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); - } else { - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - } - - /* If any of the original typed text has been changed we need to fix - * the redo buffer. */ - ins_compl_fixRedoBufForLeader(NULL); - - // Always add completion for the original text. - xfree(compl_orig_text); - compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length); - if (p_ic) { - flags |= CP_ICASE; - } - if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, - flags, false) != OK) { - XFREE_CLEAR(compl_pattern); - XFREE_CLEAR(compl_orig_text); - return FAIL; - } - - /* showmode might reset the internal line pointers, so it must - * be called before line = ml_get(), or when this address is no - * longer needed. -- Acevedo. - */ - edit_submode_extra = (char_u *)_("-- Searching..."); - edit_submode_highl = HLF_COUNT; - showmode(); - edit_submode_extra = NULL; - ui_flush(); - } else if (insert_match && stop_arrow() == FAIL) { - return FAIL; - } - - compl_shown_match = compl_curr_match; - compl_shows_dir = compl_direction; - - /* - * Find next match (and following matches). - */ - save_w_wrow = curwin->w_wrow; - save_w_leftcol = curwin->w_leftcol; - n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false); - - if (n > 1) { // all matches have been found - compl_matches = n; - } - compl_curr_match = compl_shown_match; - compl_direction = compl_shows_dir; - - /* Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert - * mode. */ - if (got_int && !global_busy) { - (void)vgetc(); - got_int = FALSE; - } - - // we found no match if the list has only the "compl_orig_text"-entry - if (compl_first_match == compl_first_match->cp_next) { - edit_submode_extra = (compl_cont_status & CONT_ADDING) - && compl_length > 1 - ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); - edit_submode_highl = HLF_E; - /* remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, - * because we couldn't expand anything at first place, but if we used - * ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word - * (such as M in M'exico) if not tried already. -- Acevedo */ - if (compl_length > 1 - || (compl_cont_status & CONT_ADDING) - || (ctrl_x_mode != CTRL_X_NORMAL - && ctrl_x_mode != CTRL_X_PATH_PATTERNS - && ctrl_x_mode != CTRL_X_PATH_DEFINES)) { - compl_cont_status &= ~CONT_N_ADDS; - } - } - - if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) { - compl_cont_status |= CONT_S_IPOS; - } else { - compl_cont_status &= ~CONT_S_IPOS; - } - - if (edit_submode_extra == NULL) { - if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) { - edit_submode_extra = (char_u *)_("Back at original"); - edit_submode_highl = HLF_W; - } else if (compl_cont_status & CONT_S_IPOS) { - edit_submode_extra = (char_u *)_("Word from other line"); - edit_submode_highl = HLF_COUNT; - } else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) { - edit_submode_extra = (char_u *)_("The only match"); - edit_submode_highl = HLF_COUNT; - compl_curr_match->cp_number = 1; - } else { - // Update completion sequence number when needed. - if (compl_curr_match->cp_number == -1) { - ins_compl_update_sequence_numbers(); - } - - /* The match should always have a sequence number now, this is - * just a safety check. */ - if (compl_curr_match->cp_number != -1) { - /* Space for 10 text chars. + 2x10-digit no.s = 31. - * Translations may need more than twice that. */ - static char_u match_ref[81]; - - if (compl_matches > 0) { - vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d of %d"), - compl_curr_match->cp_number, compl_matches); - } else { - vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d"), - compl_curr_match->cp_number); - } - edit_submode_extra = match_ref; - edit_submode_highl = HLF_R; - if (dollar_vcol >= 0) { - curs_columns(curwin, false); - } - } - } - } - - // Show a message about what (completion) mode we're in. - showmode(); - if (!shortmess(SHM_COMPLETIONMENU)) { - if (edit_submode_extra != NULL) { - if (!p_smd) { - msg_hist_off = true; - msg_attr((const char *)edit_submode_extra, - (edit_submode_highl < HLF_COUNT - ? HL_ATTR(edit_submode_highl) : 0)); - msg_hist_off = false; - } - } else { - msg_clr_cmdline(); // necessary for "noshowmode" - } - } - - // Show the popup menu, unless we got interrupted. - if (enable_pum && !compl_interrupted) { - show_pum(save_w_wrow, save_w_leftcol); - } - compl_was_interrupted = compl_interrupted; - compl_interrupted = FALSE; - - return OK; -} - -/* - * Looks in the first "len" chars. of "src" for search-metachars. - * If dest is not NULL the chars. are copied there quoting (with - * a backslash) the metachars, and dest would be NUL terminated. - * Returns the length (needed) of dest - */ -static unsigned quote_meta(char_u *dest, char_u *src, int len) -{ - unsigned m = (unsigned)len + 1; // one extra for the NUL - - for (; --len >= 0; src++) { - switch (*src) { - case '.': - case '*': - case '[': - if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) { - break; - } - FALLTHROUGH; - case '~': - if (!p_magic) { // quote these only if magic is set - break; - } - FALLTHROUGH; - case '\\': - if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) { - break; - } - FALLTHROUGH; - case '^': // currently it's not needed. - case '$': - m++; - if (dest != NULL) { - *dest++ = '\\'; - } - break; - } - if (dest != NULL) { - *dest++ = *src; - } - // Copy remaining bytes of a multibyte character. - const int mb_len = utfc_ptr2len((char *)src) - 1; - if (mb_len > 0 && len >= mb_len) { - for (int i = 0; i < mb_len; i++) { - len--; - src++; - if (dest != NULL) { - *dest++ = *src; - } - } - } - } - if (dest != NULL) { - *dest = NUL; - } - - return m; -} - /// Next character is interpreted literally. /// A one, two or three digit decimal number is interpreted as its byte value. /// If one or two digits are entered, the next character is given to vungetc(). @@ -6500,7 +2817,7 @@ static void redo_literal(int c) /// For undo/redo it resembles hitting the <ESC> key. /// /// @param end_insert_pos can be NULL -static void start_arrow(pos_T *end_insert_pos) +void start_arrow(pos_T *end_insert_pos) { start_arrow_common(end_insert_pos, true); } @@ -6546,19 +2863,6 @@ static void check_spell_redraw(void) } /* - * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly - * spelled word, if there is one. - */ -static void spell_back_to_badword(void) -{ - pos_T tpos = curwin->w_cursor; - spell_bad_len = spell_move_to(curwin, BACKWARD, true, true, NULL); - if (curwin->w_cursor.col != tpos.col) { - start_arrow(&tpos); - } -} - -/* * stop_arrow() is called before a change is made in insert mode. * If an arrow key has been used, start a new insertion. * Returns FAIL if undo is impossible, shouldn't insert then. @@ -6748,9 +3052,7 @@ void set_last_insert(int c) void free_last_insert(void) { XFREE_CLEAR(last_insert); - XFREE_CLEAR(compl_orig_text); } - #endif /* @@ -8009,8 +4311,8 @@ static bool ins_start_select(int c) 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. @@ -9016,7 +5318,7 @@ static bool ins_tab(void) /// Handle CR or NL in insert mode. /// /// @return false when it can't undo. -static bool ins_eol(int c) +bool ins_eol(int c) { if (echeck_abbr(c + ABBR_OFF)) { return true; @@ -9180,7 +5482,7 @@ static int ins_ctrl_ey(int tc) { int c = tc; - if (ctrl_x_mode == CTRL_X_SCROLL) { + if (ctrl_x_mode_scroll()) { if (c == Ctrl_Y) { scrolldown_clamp(); } else { @@ -9353,8 +5655,13 @@ static char_u *do_insert_char_pre(int c) return res; } +bool can_cindent_get(void) +{ + return can_cindent; +} + /// Trigger "event" and take care of fixing undo. -static int ins_apply_autocmds(event_T event) +int ins_apply_autocmds(event_T event) { varnumber_T tick = buf_get_changedtick(curbuf); int r; @@ -9370,21 +5677,3 @@ static int ins_apply_autocmds(event_T event) return r; } - -static void show_pum(int prev_w_wrow, int prev_w_leftcol) -{ - // RedrawingDisabled may be set when invoked through complete(). - int n = RedrawingDisabled; - RedrawingDisabled = 0; - - // If the cursor moved or the display scrolled we need to remove the pum - // first. - setcursor(); - if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol) { - ins_compl_del_pum(); - } - - ins_compl_show_pum(); - setcursor(); - RedrawingDisabled = n; -} diff --git a/src/nvim/edit.h b/src/nvim/edit.h index 894e23ee9f..eda6d8c9db 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -1,27 +1,9 @@ #ifndef NVIM_EDIT_H #define NVIM_EDIT_H +#include "nvim/autocmd.h" #include "nvim/vim.h" -/* - * Array indexes used for cptext argument of ins_compl_add(). - */ -#define CPT_ABBR 0 // "abbr" -#define CPT_MENU 1 // "menu" -#define CPT_KIND 2 // "kind" -#define CPT_INFO 3 // "info" -#define CPT_COUNT 4 // Number of entries - -// values for cp_flags -typedef enum { - CP_ORIGINAL_TEXT = 1, // the original text when the expansion begun - CP_FREE_FNAME = 2, // cp_fname is allocated - CP_CONT_S_IPOS = 4, // use CONT_S_IPOS for compl_cont_status - CP_EQUAL = 8, // ins_compl_equal() always returns true - CP_ICASE = 16, // ins_compl_equal ignores case - CP_FAST = 32, // use fast_breakcheck instead of os_breakcheck -} cp_flags_T; - typedef int (*IndentGetter)(void); // Values for in_cinkeys() diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 16c3e72c5b..9cc92cb62e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -27,6 +27,7 @@ #include "nvim/eval/gc.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" #include "nvim/ex_session.h" @@ -58,20 +59,13 @@ #define DICT_MAXNEST 100 // maximum nesting of lists and dicts -static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_illvar = N_("E461: Illegal variable name: %s"); -static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); -static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); static char *e_write2 = N_("E80: Error while writing: %s"); static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); -// TODO(ZyX-I): move to eval/executor -static char *e_letwrong = N_("E734: Wrong variable type for %s="); - static char * const namespace_char = "abglstvw"; /// Variable used for g: @@ -212,7 +206,7 @@ static struct vimvar { 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_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, 0), VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), @@ -967,6 +961,30 @@ typval_T *eval_expr(char *arg) return tv; } +/// List Vim variables. +void list_vim_vars(int *first) +{ + list_hashtable_vars(&vimvarht, "v:", false, first); +} + +/// List script-local variables, if there is a script. +void list_script_vars(int *first) +{ + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { + list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); + } +} + +bool is_vimvarht(const hashtab_T *ht) +{ + return ht == &vimvarht; +} + +bool is_compatht(const hashtab_T *ht) +{ + return ht == &compat_hashtab; +} + /// Prepare v: variable "idx" to be used. /// Save the current typeval in "save_tv". /// When not used yet add the variable to the v: hashtable. @@ -1251,723 +1269,6 @@ int eval_foldexpr(char *arg, int *cp) return (int)retval; } -/// ":cons[t] var = expr1" define constant -/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list -/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list -void ex_const(exarg_T *eap) -{ - ex_let_const(eap, true); -} - -/// Get a list of lines from a HERE document. The here document is a list of -/// lines surrounded by a marker. -/// cmd << {marker} -/// {line1} -/// {line2} -/// .... -/// {marker} -/// -/// The {marker} is a string. If the optional 'trim' word is supplied before the -/// marker, then the leading indentation before the lines (matching the -/// indentation in the 'cmd' line) is stripped. -/// -/// @return a List with {lines} or NULL. -static list_T *heredoc_get(exarg_T *eap, char *cmd) -{ - char *marker; - char *p; - int marker_indent_len = 0; - int text_indent_len = 0; - char *text_indent = NULL; - - if (eap->getline == NULL) { - emsg(_("E991: cannot use =<< here")); - return NULL; - } - - // Check for the optional 'trim' word before the marker - cmd = skipwhite(cmd); - if (STRNCMP(cmd, "trim", 4) == 0 - && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { - cmd = skipwhite(cmd + 4); - - // Trim the indentation from all the lines in the here document. - // The amount of indentation trimmed is the same as the indentation of - // the first line after the :let command line. To find the end marker - // the indent of the :let command line is trimmed. - p = *eap->cmdlinep; - while (ascii_iswhite(*p)) { - p++; - marker_indent_len++; - } - text_indent_len = -1; - } - - // The marker is the next word. - if (*cmd != NUL && *cmd != '"') { - marker = skipwhite(cmd); - p = (char *)skiptowhite((char_u *)marker); - if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { - emsg(_(e_trailing)); - return NULL; - } - *p = NUL; - if (islower(*marker)) { - emsg(_("E221: Marker cannot start with lower case letter")); - return NULL; - } - } else { - emsg(_("E172: Missing marker")); - return NULL; - } - - list_T *l = tv_list_alloc(0); - for (;;) { - int mi = 0; - int ti = 0; - - char *theline = eap->getline(NUL, eap->cookie, 0, false); - if (theline == NULL) { - semsg(_("E990: Missing end marker '%s'"), marker); - break; - } - - // with "trim": skip the indent matching the :let line to find the - // marker - if (marker_indent_len > 0 - && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { - mi = marker_indent_len; - } - if (STRCMP(marker, theline + mi) == 0) { - xfree(theline); - break; - } - if (text_indent_len == -1 && *theline != NUL) { - // set the text indent from the first line. - p = theline; - text_indent_len = 0; - while (ascii_iswhite(*p)) { - p++; - text_indent_len++; - } - text_indent = xstrnsave(theline, (size_t)text_indent_len); - } - // with "trim": skip the indent matching the first line - if (text_indent != NULL) { - for (ti = 0; ti < text_indent_len; ti++) { - if (theline[ti] != text_indent[ti]) { - break; - } - } - } - - tv_list_append_string(l, theline + ti, -1); - xfree(theline); - } - xfree(text_indent); - - return l; -} - -/// ":let" list all variable values -/// ":let var1 var2" list variable values -/// ":let var = expr" assignment command. -/// ":let var += expr" assignment command. -/// ":let var -= expr" assignment command. -/// ":let var *= expr" assignment command. -/// ":let var /= expr" assignment command. -/// ":let var %= expr" assignment command. -/// ":let var .= expr" assignment command. -/// ":let var ..= expr" assignment command. -/// ":let [var1, var2] = expr" unpack list. -/// ":let [name, ..., ; lastname] = expr" unpack list. -void ex_let(exarg_T *eap) -{ - ex_let_const(eap, false); -} - -static void ex_let_const(exarg_T *eap, const bool is_const) -{ - char *arg = eap->arg; - char *expr = NULL; - typval_T rettv; - int i; - int var_count = 0; - int semicolon = 0; - char op[2]; - char *argend; - int first = true; - - argend = (char *)skip_var_list(arg, &var_count, &semicolon); - if (argend == NULL) { - return; - } - if (argend > arg && argend[-1] == '.') { // For var.='str'. - argend--; - } - expr = skipwhite(argend); - if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL - && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { - // ":let" without "=": list variables - if (*arg == '[') { - emsg(_(e_invarg)); - } else if (!ends_excmd(*arg)) { - // ":let var1 var2" - arg = (char *)list_arg_vars(eap, (const char *)arg, &first); - } else if (!eap->skip) { - // ":let" - list_glob_vars(&first); - list_buf_vars(&first); - list_win_vars(&first); - list_tab_vars(&first); - list_script_vars(&first); - list_func_vars(&first); - list_vim_vars(&first); - } - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); - } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { - // HERE document - list_T *l = heredoc_get(eap, expr + 3); - if (l != NULL) { - tv_list_set_ret(&rettv, l); - if (!eap->skip) { - op[0] = '='; - op[1] = NUL; - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, - is_const, (char *)op); - } - tv_clear(&rettv); - } - } else { - op[0] = '='; - op[1] = NUL; - if (*expr != '=') { - if (vim_strchr("+-*/%.", *expr) != NULL) { - op[0] = *expr; // +=, -=, *=, /=, %= or .= - if (expr[0] == '.' && expr[1] == '.') { // ..= - expr++; - } - } - expr = skipwhite(expr + 2); - } else { - expr = skipwhite(expr + 1); - } - - if (eap->skip) { - ++emsg_skip; - } - i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); - if (eap->skip) { - if (i != FAIL) { - tv_clear(&rettv); - } - emsg_skip--; - } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, - is_const, (char *)op); - tv_clear(&rettv); - } - } -} - -/// Assign the typevalue "tv" to the variable or variables at "arg_start". -/// Handles both "var" with any type and "[var, var; var]" with a list type. -/// When "op" is not NULL it points to a string with characters that -/// must appear after the variable(s). Use "+", "-" or "." for add, subtract -/// or concatenate. -/// -/// @param copy copy values from "tv", don't move -/// @param semicolon from skip_var_list() -/// @param var_count from skip_var_list() -/// @param is_const lock variables for :const -/// -/// @return OK or FAIL; -static int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count, - int is_const, char *op) -{ - char *arg = arg_start; - typval_T ltv; - - if (*arg != '[') { - /* - * ":let var = expr" or ":for var in list" - */ - if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { - return FAIL; - } - return OK; - } - - // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" - if (tv->v_type != VAR_LIST) { - emsg(_(e_listreq)); - return FAIL; - } - list_T *const l = tv->vval.v_list; - - const int len = tv_list_len(l); - if (semicolon == 0 && var_count < len) { - emsg(_("E687: Less targets than List items")); - return FAIL; - } - if (var_count - semicolon > len) { - emsg(_("E688: More targets than List items")); - return FAIL; - } - // List l may actually be NULL, but it should fail with E688 or even earlier - // if you try to do ":let [] = v:_null_list". - assert(l != NULL); - - listitem_T *item = tv_list_first(l); - size_t rest_len = (size_t)tv_list_len(l); - while (*arg != ']') { - arg = skipwhite(arg + 1); - arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op); - if (arg == NULL) { - return FAIL; - } - rest_len--; - - item = TV_LIST_ITEM_NEXT(l, item); - arg = skipwhite(arg); - if (*arg == ';') { - /* Put the rest of the list (may be empty) in the var after ';'. - * Create a new list for this. */ - list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len); - while (item != NULL) { - tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); - item = TV_LIST_ITEM_NEXT(l, item); - } - - ltv.v_type = VAR_LIST; - ltv.v_lock = VAR_UNLOCKED; - ltv.vval.v_list = rest_list; - tv_list_ref(rest_list); - - arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, "]", op); - tv_clear(<v); - if (arg == NULL) { - return FAIL; - } - break; - } else if (*arg != ',' && *arg != ']') { - internal_error("ex_let_vars()"); - return FAIL; - } - } - - return OK; -} - -/// Skip over assignable variable "var" or list of variables "[var, var]". -/// Used for ":let varvar = expr" and ":for varvar in expr". -/// For "[var, var]" increment "*var_count" for each variable. -/// for "[var, var; var]" set "semicolon". -/// -/// @return NULL for an error. -static const char *skip_var_list(const char *arg, int *var_count, int *semicolon) -{ - const char *p; - const char *s; - - if (*arg == '[') { - // "[var, var]": find the matching ']'. - p = arg; - for (;;) { - p = skipwhite(p + 1); // skip whites after '[', ';' or ',' - s = skip_var_one((char *)p); - if (s == p) { - semsg(_(e_invarg2), p); - return NULL; - } - ++*var_count; - - p = skipwhite(s); - if (*p == ']') { - break; - } else if (*p == ';') { - if (*semicolon == 1) { - emsg(_("E452: Double ; in list of variables")); - return NULL; - } - *semicolon = 1; - } else if (*p != ',') { - semsg(_(e_invarg2), p); - return NULL; - } - } - return p + 1; - } else { - return skip_var_one((char *)arg); - } -} - -/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, -/// l[idx]. -static const char *skip_var_one(const char *arg) -{ - if (*arg == '@' && arg[1] != NUL) { - return arg + 1 + utfc_ptr2len(arg + 1); - } - return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, - NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); -} - -/// List variables for hashtab "ht" with prefix "prefix". -/// -/// @param empty if TRUE also list NULL strings as empty strings. -void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) -{ - hashitem_T *hi; - dictitem_T *di; - int todo; - - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - di = TV_DICT_HI2DI(hi); - char buf[IOSIZE]; - - // apply :filter /pat/ to variable name - xstrlcpy(buf, prefix, IOSIZE); - xstrlcat(buf, (char *)di->di_key, IOSIZE); - if (message_filtered((char_u *)buf)) { - continue; - } - - if (empty || di->di_tv.v_type != VAR_STRING - || di->di_tv.vval.v_string != NULL) { - list_one_var(di, prefix, first); - } - } - } -} - -/// List global variables. -static void list_glob_vars(int *first) -{ - list_hashtable_vars(&globvarht, "", true, first); -} - -/// List buffer variables. -static void list_buf_vars(int *first) -{ - list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); -} - -/// List window variables. -static void list_win_vars(int *first) -{ - list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); -} - -/// List tab page variables. -static void list_tab_vars(int *first) -{ - list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); -} - -/// List Vim variables. -static void list_vim_vars(int *first) -{ - list_hashtable_vars(&vimvarht, "v:", false, first); -} - -/// List script-local variables, if there is a script. -static void list_script_vars(int *first) -{ - if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { - list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); - } -} - -/// List variables in "arg". -static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) -{ - int error = FALSE; - int len; - const char *name; - const char *name_start; - typval_T tv; - - while (!ends_excmd(*arg) && !got_int) { - if (error || eap->skip) { - arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); - if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) { - emsg_severe = true; - emsg(_(e_trailing)); - break; - } - } else { - // get_name_len() takes care of expanding curly braces - name_start = name = arg; - char *tofree; - len = get_name_len(&arg, &tofree, true, true); - if (len <= 0) { - /* This is mainly to keep test 49 working: when expanding - * curly braces fails overrule the exception error message. */ - if (len < 0 && !aborting()) { - emsg_severe = true; - semsg(_(e_invarg2), arg); - break; - } - error = TRUE; - } else { - if (tofree != NULL) { - name = tofree; - } - if (get_var_tv(name, len, &tv, NULL, true, false) - == FAIL) { - error = true; - } else { - // handle d.key, l[idx], f(expr) - const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { - error = true; - } else { - if (arg == arg_subsc && len == 2 && name[1] == ':') { - switch (*name) { - case 'g': - list_glob_vars(first); break; - case 'b': - list_buf_vars(first); break; - case 'w': - list_win_vars(first); break; - case 't': - list_tab_vars(first); break; - case 'v': - list_vim_vars(first); break; - case 's': - list_script_vars(first); break; - case 'l': - list_func_vars(first); break; - default: - semsg(_("E738: Can't list variables for %s"), name); - } - } else { - char *const s = encode_tv2echo(&tv, NULL); - const char *const used_name = (arg == arg_subsc - ? name - : name_start); - const ptrdiff_t name_size = (used_name == tofree - ? (ptrdiff_t)strlen(used_name) - : (arg - used_name)); - list_one_var_a("", used_name, name_size, - tv.v_type, s == NULL ? "" : s, first); - xfree(s); - } - tv_clear(&tv); - } - } - } - - xfree(tofree); - } - - arg = (const char *)skipwhite(arg); - } - - return arg; -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value -/// -/// @param[in] arg Start of the variable name. -/// @param[in] tv Value to assign to the variable. -/// @param[in] copy If true, copy value from `tv`. -/// @param[in] endchars Valid characters after variable name or NULL. -/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. -/// NULL for `=`. -/// -/// @return a pointer to the char just after the var name or NULL in case of -/// error. -static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const, - const char *const endchars, const char *const op) - FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT -{ - char *arg_end = NULL; - int len; - int opt_flags; - char *tofree = NULL; - - /* - * ":let $VAR = expr": Set environment variable. - */ - if (*arg == '$') { - if (is_const) { - emsg(_("E996: Cannot lock an environment variable")); - return NULL; - } - // Find the end of the name. - arg++; - char *name = arg; - len = get_env_len((const char **)&arg); - if (len == 0) { - semsg(_(e_invarg2), name - 1); - } else { - if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg)) == NULL) { - emsg(_(e_letunexp)); - } else if (!check_secure()) { - const char c1 = name[len]; - name[len] = NUL; - const char *p = tv_get_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - char *s = vim_getenv(name); - - if (s != NULL) { - tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); - p = (const char *)tofree; - xfree(s); - } - } - if (p != NULL) { - os_setenv(name, p, 1); - if (STRICMP(name, "HOME") == 0) { - init_homedir(); - } else if (didset_vim && STRICMP(name, "VIM") == 0) { - didset_vim = false; - } else if (didset_vimruntime - && STRICMP(name, "VIMRUNTIME") == 0) { - didset_vimruntime = false; - } - arg_end = arg; - } - name[len] = c1; - xfree(tofree); - } - } - // ":let &option = expr": Set option value. - // ":let &l:option = expr": Set local option value. - // ":let &g:option = expr": Set global option value. - } else if (*arg == '&') { - if (is_const) { - emsg(_("E996: Cannot lock an option")); - return NULL; - } - // Find the end of the name. - char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); - if (p == NULL - || (endchars != NULL - && vim_strchr(endchars, *skipwhite(p)) == NULL)) { - emsg(_(e_letunexp)); - } else { - int opt_type; - long numval; - char *stringval = NULL; - const char *s = NULL; - - const char c1 = *p; - *p = NUL; - - varnumber_T n = tv_get_number(tv); - if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { - s = tv_get_string_chk(tv); // != NULL if number or string. - } - if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); - if ((opt_type == 1 && *op == '.') - || (opt_type == 0 && *op != '.')) { - semsg(_(e_letwrong), op); - s = NULL; // don't set the value - } else { - if (opt_type == 1) { // number - switch (*op) { - case '+': - n = numval + n; break; - case '-': - n = numval - n; break; - case '*': - n = numval * n; break; - case '/': - n = num_divide(numval, n); break; - case '%': - n = num_modulus(numval, n); break; - } - } else if (opt_type == 0 && stringval != NULL) { // string - char *const oldstringval = stringval; - stringval = (char *)concat_str((const char_u *)stringval, - (const char_u *)s); - xfree(oldstringval); - s = stringval; - } - } - } - if (s != NULL || tv->v_type == VAR_BOOL - || tv->v_type == VAR_SPECIAL) { - set_option_value((const char *)arg, n, s, opt_flags); - arg_end = p; - } - *p = c1; - xfree(stringval); - } - // ":let @r = expr": Set register contents. - } else if (*arg == '@') { - if (is_const) { - emsg(_("E996: Cannot lock a register")); - return NULL; - } - arg++; - - int regname = utf_ptr2char(arg); - int mblen = utf_ptr2len(arg); - - if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) { - emsg(_(e_letunexp)); - } else { - char *s; - - char *ptofree = NULL; - const char *p = tv_get_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - s = get_reg_contents(regname == '@' ? '"' : regname, kGRegExprSrc); - if (s != NULL) { - ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); - p = (const char *)ptofree; - xfree(s); - } - } - if (p != NULL) { - - write_reg_contents(regname == '@' ? '"' : regname, - (const char_u *)p, (ssize_t)STRLEN(p), false); - arg_end = arg + mblen; - } - xfree(ptofree); - } - } - /* - * ":let var = expr": Set internal variable. - * ":let {expr} = expr": Idem, name made with curly braces - */ - else if (eval_isnamec1(*arg) || *arg == '{') { - lval_T lv; - - char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); - if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { - emsg(_(e_letunexp)); - } else { - set_var_lval(&lv, p, tv, copy, is_const, op); - arg_end = p; - } - } - clear_lval(&lv); - } else { - semsg(_(e_invarg2), arg); - } - - return arg_end; -} - // TODO(ZyX-I): move to eval/executor /// Get an lvalue @@ -2357,8 +1658,8 @@ void clear_lval(lval_T *lp) /// @param endp points to just after the parsed name. /// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", /// "%" for "%=", "." for ".=" or "=" for "=". -static void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const, - const char *op) +void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const, + const char *op) { int cc; listitem_T *ri; @@ -2810,327 +2111,6 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) xp->xp_pattern = arg; } -/// ":unlet[!] var1 ... " command. -void ex_unlet(exarg_T *eap) -{ - ex_unletlock(eap, eap->arg, 0, do_unlet_var); -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// ":lockvar" and ":unlockvar" commands -void ex_lockvar(exarg_T *eap) -{ - char *arg = eap->arg; - int deep = 2; - - if (eap->forceit) { - deep = -1; - } else if (ascii_isdigit(*arg)) { - deep = getdigits_int(&arg, false, -1); - arg = skipwhite(arg); - } - - ex_unletlock(eap, arg, deep, do_lock_var); -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Common parsing logic for :unlet, :lockvar and :unlockvar. -/// -/// Invokes `callback` afterwards if successful and `eap->skip == false`. -/// -/// @param[in] eap Ex command arguments for the command. -/// @param[in] argstart Start of the string argument for the command. -/// @param[in] deep Levels to (un)lock for :(un)lockvar, -1 to (un)lock -/// everything. -/// @param[in] callback Appropriate handler for the command. -static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_callback callback) - FUNC_ATTR_NONNULL_ALL -{ - char *arg = argstart; - char *name_end; - bool error = false; - lval_T lv; - - do { - if (*arg == '$') { - lv.ll_name = (const char *)arg; - lv.ll_tv = NULL; - arg++; - if (get_env_len((const char **)&arg) == 0) { - semsg(_(e_invarg2), arg - 1); - return; - } - if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) { - error = true; - } - name_end = arg; - } else { - // Parse the name and find the end. - name_end = get_lval(arg, NULL, &lv, true, eap->skip || error, - 0, FNE_CHECK_START); - if (lv.ll_name == NULL) { - error = true; // error, but continue parsing. - } - if (name_end == NULL - || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { - if (name_end != NULL) { - emsg_severe = true; - emsg(_(e_trailing)); - } - if (!(eap->skip || error)) { - clear_lval(&lv); - } - break; - } - - if (!error && !eap->skip && callback(&lv, name_end, eap, deep) == FAIL) { - error = true; - } - - if (!eap->skip) { - clear_lval(&lv); - } - } - arg = skipwhite(name_end); - } while (!ends_excmd(*arg)); - - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Unlet a variable indicated by `lp`. -/// -/// @param[in] lp The lvalue. -/// @param[in] name_end End of the string argument for the command. -/// @param[in] eap Ex command arguments for :unlet. -/// @param[in] deep Unused. -/// -/// @return OK on success, or FAIL on failure. -static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ATTR_UNUSED) - FUNC_ATTR_NONNULL_ALL -{ - int forceit = eap->forceit; - int ret = OK; - int cc; - - if (lp->ll_tv == NULL) { - cc = (char_u)(*name_end); - *name_end = NUL; - - // Environment variable, normal name or expanded name. - if (*lp->ll_name == '$') { - os_unsetenv(lp->ll_name + 1); - } else if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { - ret = FAIL; - } - *name_end = (char)cc; - } else if ((lp->ll_list != NULL - // ll_list is not NULL when lvalue is not in a list, NULL lists - // yield E689. - && var_check_lock(tv_list_locked(lp->ll_list), - lp->ll_name, - lp->ll_name_len)) - || (lp->ll_dict != NULL - && var_check_lock(lp->ll_dict->dv_lock, - lp->ll_name, - lp->ll_name_len))) { - return FAIL; - } else if (lp->ll_range) { - assert(lp->ll_list != NULL); - // Delete a range of List items. - listitem_T *const first_li = lp->ll_li; - listitem_T *last_li = first_li; - for (;;) { - listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, - lp->ll_name, - lp->ll_name_len)) { - return false; - } - lp->ll_li = li; - lp->ll_n1++; - if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { - break; - } else { - last_li = lp->ll_li; - } - } - tv_list_remove_items(lp->ll_list, first_li, last_li); - } else { - if (lp->ll_list != NULL) { - // unlet a List item. - tv_list_item_remove(lp->ll_list, lp->ll_li); - } else { - // unlet a Dictionary item. - dict_T *d = lp->ll_dict; - assert(d != NULL); - dictitem_T *di = lp->ll_di; - bool watched = tv_dict_is_watched(d); - char *key = NULL; - typval_T oldtv; - - if (watched) { - tv_copy(&di->di_tv, &oldtv); - // need to save key because dictitem_remove will free it - key = xstrdup((char *)di->di_key); - } - - tv_dict_item_remove(d, di); - - if (watched) { - tv_dict_watcher_notify(d, key, NULL, &oldtv); - tv_clear(&oldtv); - xfree(key); - } - } - } - - return ret; -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// unlet a variable -/// -/// @param[in] name Variable name to unlet. -/// @param[in] name_len Variable name length. -/// @param[in] forceit If true, do not complain if variable doesn’t exist. -/// -/// @return OK if it existed, FAIL otherwise. -int do_unlet(const char *const name, const size_t name_len, const bool forceit) - FUNC_ATTR_NONNULL_ALL -{ - const char *varname; - dict_T *dict; - hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); - - if (ht != NULL && *varname != NUL) { - dict_T *d = get_current_funccal_dict(ht); - if (d == NULL) { - if (ht == &globvarht) { - d = &globvardict; - } else if (ht == &compat_hashtab) { - d = &vimvardict; - } else { - dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); - d = di->di_tv.vval.v_dict; - } - if (d == NULL) { - internal_error("do_unlet()"); - return FAIL; - } - } - - hashitem_T *hi = hash_find(ht, varname); - if (HASHITEM_EMPTY(hi)) { - hi = find_hi_in_scoped_ht(name, &ht); - } - if (hi != NULL && !HASHITEM_EMPTY(hi)) { - dictitem_T *const di = TV_DICT_HI2DI(hi); - if (var_check_fixed(di->di_flags, name, TV_CSTRING) - || var_check_ro(di->di_flags, name, TV_CSTRING) - || var_check_lock(d->dv_lock, name, TV_CSTRING)) { - return FAIL; - } - - if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { - return FAIL; - } - - typval_T oldtv; - bool watched = tv_dict_is_watched(dict); - - if (watched) { - tv_copy(&di->di_tv, &oldtv); - } - - delete_var(ht, hi); - - if (watched) { - tv_dict_watcher_notify(dict, varname, NULL, &oldtv); - tv_clear(&oldtv); - } - return OK; - } - } - if (forceit) { - return OK; - } - semsg(_("E108: No such variable: \"%s\""), name); - return FAIL; -} - -// TODO(ZyX-I): move to eval/ex_cmds - -/// Lock or unlock variable indicated by `lp`. -/// -/// Locks if `eap->cmdidx == CMD_lockvar`, unlocks otherwise. -/// -/// @param[in] lp The lvalue. -/// @param[in] name_end Unused. -/// @param[in] eap Ex command arguments for :(un)lockvar. -/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. -/// -/// @return OK on success, or FAIL on failure. -static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap, int deep) - FUNC_ATTR_NONNULL_ARG(1, 3) -{ - bool lock = eap->cmdidx == CMD_lockvar; - int ret = OK; - - if (deep == 0) { // Nothing to do. - return OK; - } - - if (lp->ll_tv == NULL) { - if (*lp->ll_name == '$') { - semsg(_(e_lock_unlock), lp->ll_name); - ret = FAIL; - } else { - // Normal name or expanded name. - dictitem_T *const di = find_var(lp->ll_name, lp->ll_name_len, NULL, - true); - if (di == NULL) { - ret = FAIL; - } else if ((di->di_flags & DI_FLAGS_FIX) - && di->di_tv.v_type != VAR_DICT - && di->di_tv.v_type != VAR_LIST) { - // For historical reasons this error is not given for Lists and - // Dictionaries. E.g. b: dictionary may be locked/unlocked. - semsg(_(e_lock_unlock), lp->ll_name); - ret = FAIL; - } else { - if (lock) { - di->di_flags |= DI_FLAGS_LOCK; - } else { - di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK); - } - tv_item_lock(&di->di_tv, deep, lock, false); - } - } - } else if (lp->ll_range) { - listitem_T *li = lp->ll_li; - - // (un)lock a range of List items. - while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock, false); - li = TV_LIST_ITEM_NEXT(lp->ll_list, li); - lp->ll_n1++; - } - } else if (lp->ll_list != NULL) { - // (un)lock a List item. - tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock, false); - } else { - // (un)lock a Dictionary item. - tv_item_lock(&lp->ll_di->di_tv, deep, lock, false); - } - - return ret; -} - /// Delete all "menutrans_" variables. void del_menutrans_vars(void) { @@ -4795,7 +3775,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval { long numval; char *stringval; - int opt_type; + getoption_T opt_type; bool working = (**arg == '+'); // has("+option") int ret = OK; int opt_flags; @@ -4819,26 +3799,28 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval opt_type = get_option_value(*arg, &numval, rettv == NULL ? NULL : &stringval, opt_flags); - if (opt_type == -3) { // invalid name + if (opt_type == gov_unknown) { if (rettv != NULL) { semsg(_("E113: Unknown option: %s"), *arg); } ret = FAIL; } else if (rettv != NULL) { - if (opt_type == -2) { // hidden string option + if (opt_type == gov_hidden_string) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - } else if (opt_type == -1) { // hidden number option + } else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; - } else if (opt_type == 1 || opt_type == 2) { // number or boolean option + } else if (opt_type == gov_bool || opt_type == gov_number) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = numval; } else { // string option rettv->v_type = VAR_STRING; rettv->vval.v_string = stringval; } - } else if (working && (opt_type == -2 || opt_type == -1)) { + } else if (working && (opt_type == gov_hidden_bool + || opt_type == gov_hidden_number + || opt_type == gov_hidden_string)) { ret = FAIL; } @@ -5685,7 +4667,7 @@ static inline bool set_ref_dict(dict_T *dict, int copyID) return false; } -/// Get the key for *{key: val} into "tv" and advance "arg". +/// Get the key for #{key: val} into "tv" and advance "arg". /// /// @return FAIL when there is no valid key. static int get_literal_key(char **arg, typval_T *tv) @@ -5705,7 +4687,7 @@ static int get_literal_key(char **arg, typval_T *tv) } /// Allocate a variable for a Dictionary and fill it from "*arg". -/// "literal" is true for *{key: val} +/// "literal" is true for #{key: val} /// /// @return OK or FAIL. Returns NOTDONE for {expr}. static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal) @@ -6480,73 +5462,6 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp) return wp; } -/// getwinvar() and gettabwinvar() -/// -/// @param off 1 for gettabwinvar() -void getwinvar(typval_T *argvars, typval_T *rettv, int off) -{ - win_T *win; - dictitem_T *v; - tabpage_T *tp = NULL; - bool done = false; - - if (off == 1) { - tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - } else { - tp = curtab; - } - win = find_win_by_nr(&argvars[off], tp); - const char *varname = tv_get_string_chk(&argvars[off + 1]); - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - emsg_off++; - if (win != NULL && varname != NULL) { - // 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; - switchwin_T switchwin; - if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { - if (*varname == '&') { - if (varname[1] == NUL) { - // get all window-local options in a dict - dict_T *opts = get_winbuf_options(false); - - if (opts != NULL) { - tv_dict_set_ret(rettv, opts); - done = true; - } - } else if (get_option_tv(&varname, rettv, 1) == OK) { - // window-local-option - done = true; - } - } else { - // Look up the variable. - // Let getwinvar({nr}, "") return the "w:" dictionary. - v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, - strlen(varname), false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - } - - if (need_switch_win) { - // restore previous notion of curwin - restore_win(&switchwin, true); - } - } - emsg_off--; - - if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { - // use the default return value - tv_copy(&argvars[off + 2], rettv); - } -} - /// This function is used by f_input() and f_inputdialog() functions. The third /// argument to f_input() specifies the type of completion to use at the /// prompt. The third argument to f_inputdialog() specifies the value to return @@ -6677,58 +5592,6 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const cmd_silent = cmd_silent_save; } -/// Turn a dictionary into a list -/// -/// @param[in] tv Dictionary to convert. Is checked for actually being -/// a dictionary, will give an error if not. -/// @param[out] rettv Location where result will be saved. -/// @param[in] what What to save in rettv. -void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) -{ - if (tv->v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return; - } - if (tv->vval.v_dict == NULL) { - return; - } - - tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); - - TV_DICT_ITER(tv->vval.v_dict, di, { - typval_T tv_item = { .v_lock = VAR_UNLOCKED }; - - switch (what) { - case kDictListKeys: - tv_item.v_type = VAR_STRING; - tv_item.vval.v_string = (char *)vim_strsave(di->di_key); - break; - case kDictListValues: - tv_copy(&di->di_tv, &tv_item); - break; - case kDictListItems: { - // items() - list_T *const sub_l = tv_list_alloc(2); - tv_item.v_type = VAR_LIST; - tv_item.vval.v_list = sub_l; - tv_list_ref(sub_l); - - tv_list_append_owned_tv(sub_l, (typval_T) { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrdup((const char *)di->di_key), - }); - - tv_list_append_tv(sub_l, &di->di_tv); - - break; - } - } - - tv_list_append_owned_tv(rettv->vval.v_list, tv_item); - }); -} - /// Builds a process argument vector from a VimL object (typval_T). /// /// @param[in] cmd_tv VimL object @@ -6940,56 +5803,6 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T } } -/* - * "setwinvar()" and "settabwinvar()" functions - */ - -void setwinvar(typval_T *argvars, typval_T *rettv, int off) -{ - if (check_secure()) { - return; - } - - tabpage_T *tp = NULL; - if (off == 1) { - tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - } else { - tp = curtab; - } - win_T *const win = find_win_by_nr(&argvars[off], tp); - const char *varname = tv_get_string_chk(&argvars[off + 1]); - typval_T *varp = &argvars[off + 2]; - - if (win != NULL && varname != NULL && varp != NULL) { - bool need_switch_win = tp != curtab || win != curwin; - switchwin_T switchwin; - if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { - if (*varname == '&') { - long numval; - bool error = false; - - varname++; - numval = tv_get_number_chk(varp, &error); - char nbuf[NUMBUFLEN]; - const char *const strval = tv_get_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); - } - } else { - const size_t varname_len = strlen(varname); - char *const winvarname = xmalloc(varname_len + 3); - memcpy(winvarname, "w:", 2); - memcpy(winvarname + 2, varname, varname_len + 1); - set_var(winvarname, varname_len + 2, varp, true); - xfree(winvarname); - } - } - if (need_switch_win) { - restore_win(&switchwin, true); - } - } -} - /// "stdpath()" helper for list results void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -7877,7 +6690,7 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c /// Advance "arg" to the first character after the name. /// /// @return 0 for error. -static int get_env_len(const char **arg) +int get_env_len(const char **arg) { int len; @@ -8410,41 +7223,6 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) return oldval; } -/// Get the value of internal variable "name". -/// Return OK or FAIL. If OK is returned "rettv" must be cleared. -/// -/// @param len length of "name" -/// @param rettv NULL when only checking existence -/// @param dip non-NULL when typval's dict item is needed -/// @param verbose may give error message -/// @param no_autoload do not use script autoloading -int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, - int no_autoload) -{ - int ret = OK; - typval_T *tv = NULL; - dictitem_T *v; - - v = find_var(name, (size_t)len, NULL, no_autoload); - if (v != NULL) { - tv = &v->di_tv; - if (dip != NULL) { - *dip = v; - } - } - - if (tv == NULL) { - if (rettv != NULL && verbose) { - semsg(_("E121: Undefined variable: %.*s"), len, name); - } - ret = FAIL; - } else if (rettv != NULL) { - tv_copy(tv, rettv); - } - - return ret; -} - /// Check if variable "name[len]" is a local variable or an argument. /// If so, "*eval_lavars_used" is set to true. static void check_vars(const char *name, size_t len) @@ -8710,8 +7488,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va /// @param[out] d Scope dictionary. /// /// @return Scope hashtab, NULL if name is not valid. -static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname, - dict_T **d) +hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname, + dict_T **d) { hashitem_T *hi; funccall_T *funccal = get_funccal(); @@ -8814,21 +7592,6 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var return find_var_ht_dict(name, name_len, varname, &d); } -/// @return the string value of a (global/local) variable or -/// NULL when it doesn't exist. -/// -/// @see tv_get_string() for how long the pointer remains valid. -char_u *get_var_value(const char *const name) -{ - dictitem_T *v; - - v = find_var(name, strlen(name), NULL, false); - if (v == NULL) { - return NULL; - } - return (char_u *)tv_get_string(&v->di_tv); -} - /// Allocate a new hashtab for a sourced script. It will be used while /// sourcing this script and when executing functions defined in the script. void new_script_vars(scid_T id) @@ -8884,391 +7647,6 @@ void unref_var_dict(dict_T *dict) tv_dict_unref(dict); } -/// Clean up a list of internal variables. -/// Frees all allocated variables and the value they contain. -/// Clears hashtab "ht", does not free it. -void vars_clear(hashtab_T *ht) -{ - vars_clear_ext(ht, TRUE); -} - -/// Like vars_clear(), but only free the value if "free_val" is TRUE. -void vars_clear_ext(hashtab_T *ht, int free_val) -{ - int todo; - hashitem_T *hi; - dictitem_T *v; - - hash_lock(ht); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) { - 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. - v = TV_DICT_HI2DI(hi); - if (free_val) { - tv_clear(&v->di_tv); - } - if (v->di_flags & DI_FLAGS_ALLOC) { - xfree(v); - } - } - } - hash_clear(ht); - ht->ht_used = 0; -} - -/// Delete a variable from hashtab "ht" at item "hi". -/// Clear the variable value and free the dictitem. -static void delete_var(hashtab_T *ht, hashitem_T *hi) -{ - dictitem_T *di = TV_DICT_HI2DI(hi); - - hash_remove(ht, hi); - tv_clear(&di->di_tv); - xfree(di); -} - -/// List the value of one internal variable. -static void list_one_var(dictitem_T *v, const char *prefix, int *first) -{ - char *const s = encode_tv2echo(&v->di_tv, NULL); - list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)STRLEN(v->di_key), - v->di_tv.v_type, (s == NULL ? "" : s), first); - xfree(s); -} - -/// @param[in] name_len Length of the name. May be -1, in this case strlen() -/// will be used. -/// @param[in,out] first When true clear rest of screen and set to false. -static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len, - const VarType type, const char *string, int *first) -{ - // don't use msg() or msg_attr() to avoid overwriting "v:statusmsg" - msg_start(); - msg_puts(prefix); - if (name != NULL) { // "a:" vars don't have a name stored - msg_puts_attr_len(name, name_len, 0); - } - msg_putchar(' '); - msg_advance(22); - if (type == VAR_NUMBER) { - msg_putchar('#'); - } else if (type == VAR_FUNC || type == VAR_PARTIAL) { - msg_putchar('*'); - } else if (type == VAR_LIST) { - msg_putchar('['); - if (*string == '[') { - ++string; - } - } else if (type == VAR_DICT) { - msg_putchar('{'); - if (*string == '{') { - ++string; - } - } else { - msg_putchar(' '); - } - - msg_outtrans((char *)string); - - if (type == VAR_FUNC || type == VAR_PARTIAL) { - msg_puts("()"); - } - if (*first) { - msg_clr_eos(); - *first = FALSE; - } -} - -/// Set variable to the given value -/// -/// If the variable already exists, the value is updated. Otherwise the variable -/// is created. -/// -/// @param[in] name Variable name to set. -/// @param[in] name_len Length of the variable name. -/// @param tv Variable value. -/// @param[in] copy True if value in tv is to be copied. -void set_var(const char *name, const size_t name_len, typval_T *const tv, const bool copy) - FUNC_ATTR_NONNULL_ALL -{ - set_var_const(name, name_len, tv, copy, false); -} - -/// Set variable to the given value -/// -/// If the variable already exists, the value is updated. Otherwise the variable -/// is created. -/// -/// @param[in] name Variable name to set. -/// @param[in] name_len Length of the variable name. -/// @param tv Variable value. -/// @param[in] copy True if value in tv is to be copied. -/// @param[in] is_const True if value in tv is to be locked. -static void set_var_const(const char *name, const size_t name_len, typval_T *const tv, - const bool copy, const bool is_const) - FUNC_ATTR_NONNULL_ALL -{ - dictitem_T *v; - hashtab_T *ht; - dict_T *dict; - - const char *varname; - ht = find_var_ht_dict(name, name_len, &varname, &dict); - const bool watched = tv_dict_is_watched(dict); - - if (ht == NULL || *varname == NUL) { - semsg(_(e_illvar), name); - return; - } - v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); - - // Search in parent scope which is possible to reference from lambda - if (v == NULL) { - v = find_var_in_scoped_ht(name, name_len, true); - } - - if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { - return; - } - - typval_T oldtv = TV_INITIAL_VALUE; - if (v != NULL) { - if (is_const) { - emsg(_(e_cannot_mod)); - return; - } - - // existing variable, need to clear the value - if (var_check_ro(v->di_flags, name, name_len) - || var_check_lock(v->di_tv.v_lock, name, name_len)) { - return; - } - - // 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_CLEAR(v->di_tv.vval.v_string); - if (copy || tv->v_type != VAR_STRING) { - const char *const val = tv_get_string(tv); - - // Careful: when assigning to v:errmsg and tv_get_string() - // causes an error message the variable will already be set. - if (v->di_tv.vval.v_string == NULL) { - v->di_tv.vval.v_string = xstrdup(val); - } - } else { - // Take over the string to avoid an extra alloc/free. - v->di_tv.vval.v_string = tv->vval.v_string; - tv->vval.v_string = NULL; - } - return; - } else if (v->di_tv.v_type == VAR_NUMBER) { - v->di_tv.vval.v_number = tv_get_number(tv); - if (strcmp(varname, "searchforward") == 0) { - set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); - } else if (strcmp(varname, "hlsearch") == 0) { - no_hlsearch = !v->di_tv.vval.v_number; - redraw_all_later(SOME_VALID); - } - return; - } else if (v->di_tv.v_type != tv->v_type) { - semsg(_("E963: setting %s to value with wrong type"), name); - return; - } - } - - if (watched) { - tv_copy(&v->di_tv, &oldtv); - } - tv_clear(&v->di_tv); - } else { // Add a new variable. - // Can't add "v:" or "a:" variable. - if (ht == &vimvarht || ht == get_funccal_args_ht()) { - semsg(_(e_illvar), name); - return; - } - - // Make sure the variable name is valid. - if (!valid_varname(varname)) { - return; - } - - // Make sure dict is valid - assert(dict != NULL); - - v = xmalloc(sizeof(dictitem_T) + strlen(varname)); - STRCPY(v->di_key, varname); - if (tv_dict_add(dict, v) == FAIL) { - xfree(v); - return; - } - v->di_flags = DI_FLAGS_ALLOC; - if (is_const) { - v->di_flags |= DI_FLAGS_LOCK; - } - } - - if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { - tv_copy(tv, &v->di_tv); - } else { - v->di_tv = *tv; - v->di_tv.v_lock = VAR_UNLOCKED; - tv_init(tv); - } - - if (watched) { - if (oldtv.v_type == VAR_UNKNOWN) { - tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); - } else { - tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); - tv_clear(&oldtv); - } - } - - if (is_const) { - // Like :lockvar! name: lock the value and what it contains, but only - // if the reference count is up to one. That locks only literal - // values. - tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true); - } -} - -/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX) -/// -/// Also gives an error message. -/// -/// @param[in] flags di_flags attribute value. -/// @param[in] name Variable name, for use in error message. -/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate -/// variable name and compute the length. Use #TV_CSTRING -/// to compute the length with strlen() without -/// translating. -/// -/// Both #TV_… values are used for optimization purposes: -/// variable name with its length is needed only in case -/// of error, when no error occurs computing them is -/// a waste of CPU resources. This especially applies to -/// gettext. -/// -/// @return True if variable is read-only: either always or in sandbox when -/// sandbox is enabled, false otherwise. -bool var_check_ro(const int flags, const char *name, size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - const char *error_message = NULL; - if (flags & DI_FLAGS_RO) { - error_message = _(e_readonlyvar); - } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) { - error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\""); - } - - if (error_message == NULL) { - return false; - } - if (name_len == TV_TRANSLATE) { - name = _(name); - name_len = strlen(name); - } else if (name_len == TV_CSTRING) { - name_len = strlen(name); - } - - semsg(_(error_message), (int)name_len, name); - - return true; -} - -/// Check whether variable is fixed (DI_FLAGS_FIX) -/// -/// Also gives an error message. -/// -/// @param[in] flags di_flags attribute value. -/// @param[in] name Variable name, for use in error message. -/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate -/// variable name and compute the length. Use #TV_CSTRING -/// to compute the length with strlen() without -/// translating. -/// -/// Both #TV_… values are used for optimization purposes: -/// variable name with its length is needed only in case -/// of error, when no error occurs computing them is -/// a waste of CPU resources. This especially applies to -/// gettext. -/// -/// @return True if variable is fixed, false otherwise. -bool var_check_fixed(const int flags, const char *name, size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - if (flags & DI_FLAGS_FIX) { - if (name_len == TV_TRANSLATE) { - name = _(name); - name_len = strlen(name); - } else if (name_len == TV_CSTRING) { - name_len = strlen(name); - } - semsg(_("E795: Cannot delete variable %.*s"), (int)name_len, name); - return true; - } - return false; -} - -// TODO(ZyX-I): move to eval/expressions - -/// Check if name is a valid name to assign funcref to -/// -/// @param[in] name Possible function/funcref name. -/// @param[in] new_var True if it is a name for a variable. -/// -/// @return false in case of error, true in case of success. Also gives an -/// error message if appropriate. -bool var_check_func_name(const char *const name, const bool new_var) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - // Allow for w: b: s: and t:. - if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') - ? name[2] : name[0])) { - semsg(_("E704: Funcref variable name must start with a capital: %s"), name); - return false; - } - // Don't allow hiding a function. When "v" is not NULL we might be - // assigning another function to the same var, the type is checked - // below. - if (new_var && function_exists(name, false)) { - semsg(_("E705: Variable name conflicts with existing function: %s"), - name); - return false; - } - return true; -} - -// TODO(ZyX-I): move to eval/expressions - -/// Check if a variable name is valid -/// -/// @param[in] varname Variable name to check. -/// -/// @return false when variable name is not valid, true when it is. Also gives -/// an error message if appropriate. -bool valid_varname(const char *varname) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - for (const char *p = varname; *p != NUL; p++) { - if (!eval_isnamec1((int)(uint8_t)(*p)) - && (p == varname || !ascii_isdigit(*p)) - && *p != AUTOLOAD_CHAR) { - semsg(_(e_illvar), varname); - return false; - } - } - return true; -} - /// Make a copy of an item /// /// Lists and Dictionaries are also copied. @@ -9533,7 +7911,7 @@ void ex_execute(exarg_T *eap) /// /// @return NULL when no option name found. Otherwise pointer to the char /// after the option name. -static const char *find_option_end(const char **const arg, int *const opt_flags) +const char *find_option_end(const char **const arg, int *const opt_flags) { const char *p = *arg; @@ -10895,35 +9273,3 @@ char *typval_tostring(typval_T *arg) } return encode_tv2string(arg, NULL); } - -bool var_exists(const char *var) - FUNC_ATTR_NONNULL_ALL -{ - char *tofree; - bool n = false; - - // get_name_len() takes care of expanding curly braces - const char *name = var; - const int len = get_name_len(&var, &tofree, true, false); - if (len > 0) { - typval_T tv; - - if (tofree != NULL) { - name = tofree; - } - n = get_var_tv(name, len, &tv, NULL, false, true) == OK; - if (n) { - // Handle d.key, l[idx], f(expr). - n = handle_subscript(&var, &tv, true, false, name, &name) == OK; - if (n) { - tv_clear(&tv); - } - } - } - if (*var != NUL) { - n = false; - } - - xfree(tofree); - return n; -} diff --git a/src/nvim/eval.c.orig b/src/nvim/eval.c.orig new file mode 100644 index 0000000000..9bfb2cd7d6 --- /dev/null +++ b/src/nvim/eval.c.orig @@ -0,0 +1,9995 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +/* + * eval.c: Expression evaluation. + */ + +#include <math.h> +#include <stdlib.h> + +#include "auto/config.h" + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/change.h" +#include "nvim/channel.h" +#include "nvim/charset.h" +#include "nvim/cursor.h" +#include "nvim/edit.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/gc.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" +#include "nvim/ex_cmds2.h" +#include "nvim/ex_getln.h" +#include "nvim/ex_session.h" +#include "nvim/fileio.h" +#include "nvim/getchar.h" +#include "nvim/highlight_group.h" +#include "nvim/lua/executor.h" +#include "nvim/mark.h" +#include "nvim/memline.h" +#include "nvim/move.h" +#include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/shell.h" +#include "nvim/path.h" +#include "nvim/quickfix.h" +#include "nvim/regexp.h" +#include "nvim/screen.h" +#include "nvim/search.h" +#include "nvim/sign.h" +#include "nvim/syntax.h" +#include "nvim/ui.h" +#include "nvim/ui_compositor.h" +#include "nvim/undo.h" +#include "nvim/version.h" +#include "nvim/window.h" + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 // maximum nesting of lists and dicts + +static char *e_missbrac = N_("E111: Missing ']'"); +static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); +static char *e_nowhitespace + = N_("E274: No white space allowed before parenthesis"); +static char *e_write2 = N_("E80: Error while writing: %s"); +static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); + +static char * const namespace_char = "abglstvw"; + +/// Variable used for g: +static ScopeDictDictItem globvars_var; + +/* + * Old Vim variables such as "v:version" are also available without the "v:". + * Also in functions. We need a special hashtable for them. + */ +static hashtab_T compat_hashtab; + +/// Used for checking if local variables or arguments used in a lambda. +bool *eval_lavars_used = NULL; + +/* + * Array to hold the hashtab with variables local to each sourced script. + * Each item holds a variable (nameless) that points to the dict_T. + */ +typedef struct { + ScopeDictDictItem sv_var; + dict_T sv_dict; +} scriptvar_T; + +static garray_T ga_scripts = { 0, 0, sizeof(scriptvar_T *), 4, NULL }; +#define SCRIPT_SV(id) (((scriptvar_T **)ga_scripts.ga_data)[(id) - 1]) +#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab) + +static int echo_attr = 0; // attributes used for ":echo" + +// The names of packages that once were loaded are remembered. +static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL }; + +/* + * Info used by a ":for" loop. + */ +typedef struct { + int fi_semicolon; // TRUE if ending in '; var]' + int fi_varcount; // nr of variables in the list + listwatch_T fi_lw; // keep an eye on the item used. + list_T *fi_list; // list being used + int fi_bi; // index of blob + blob_T *fi_blob; // blob being used + char *fi_string; // copy of string being used + int fi_byte_idx; // byte index in fi_string +} forinfo_T; + +// values for vv_flags: +#define VV_COMPAT 1 // compatible, also used without "v:" +#define VV_RO 2 // read-only +#define VV_RO_SBX 4 // read-only in the sandbox + +#define VV(idx, name, type, flags) \ + [idx] = { \ + .vv_name = (name), \ + .vv_di = { \ + .di_tv = { .v_type = (type) }, \ + .di_flags = 0, \ + .di_key = { 0 }, \ + }, \ + .vv_flags = (flags), \ + } + +#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables + +// 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 the variable, without v:. + TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. +} vimvars[] = +{ + // VV_ tails differing from upcased string literals: + // 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_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), + 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_WINID, "beval_winid", 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_WINID, "mouse_winid", 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_COMPLETED_ITEM, "completed_item", VAR_DICT, 0), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO), + VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_FALSE, "false", VAR_BOOL, VV_RO), + VV(VV_TRUE, "true", VAR_BOOL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), + VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), + VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), + VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), + VV(VV_TESTING, "testing", VAR_NUMBER, 0), + VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), + VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), + VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), + VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), + VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), + VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + // Neovim + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), + VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, 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_bool vv_di.di_tv.vval.v_bool +#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 +#define vv_dict vv_di.di_tv.vval.v_dict +#define vv_blob vv_di.di_tv.vval.v_blob +#define vv_partial vv_di.di_tv.vval.v_partial +#define vv_tv vv_di.di_tv + +/// Variable used for v: +static ScopeDictDictItem vimvars_var; + +static partial_T *vvlua_partial; + +/// v: hashtab +#define vimvarht vimvardict.dv_hashtab + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval.c.generated.h" +#endif + +static uint64_t last_timer_id = 1; +static PMap(uint64_t) timers = MAP_INIT; + +static const char *const msgpack_type_names[] = { + [kMPNil] = "nil", + [kMPBoolean] = "boolean", + [kMPInteger] = "integer", + [kMPFloat] = "float", + [kMPString] = "string", + [kMPBinary] = "binary", + [kMPArray] = "array", + [kMPMap] = "map", + [kMPExt] = "ext", +}; +const list_T *eval_msgpack_type_lists[] = { + [kMPNil] = NULL, + [kMPBoolean] = NULL, + [kMPInteger] = NULL, + [kMPFloat] = NULL, + [kMPString] = NULL, + [kMPBinary] = NULL, + [kMPArray] = NULL, + [kMPMap] = NULL, + [kMPExt] = NULL, +}; + +dict_T *get_v_event(save_v_event_T *sve) +{ + dict_T *v_event = get_vim_var_dict(VV_EVENT); + + if (v_event->dv_hashtab.ht_used > 0) { + // recursive use of v:event, save, make empty and restore later + sve->sve_did_save = true; + sve->sve_hashtab = v_event->dv_hashtab; + hash_init(&v_event->dv_hashtab); + } else { + sve->sve_did_save = false; + } + return v_event; +} + +void restore_v_event(dict_T *v_event, save_v_event_T *sve) +{ + tv_dict_free_contents(v_event); + if (sve->sve_did_save) { + v_event->dv_hashtab = sve->sve_hashtab; + } else { + hash_init(&v_event->dv_hashtab); + } +} + +/// @return "n1" divided by "n2", taking care of dividing by zero. +varnumber_T num_divide(varnumber_T n1, varnumber_T n2) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + varnumber_T result; + + if (n2 == 0) { // give an error message? + if (n1 == 0) { + result = VARNUMBER_MIN; // similar to NaN + } else if (n1 < 0) { + result = -VARNUMBER_MAX; + } else { + result = VARNUMBER_MAX; + } + } else { + result = n1 / n2; + } + + return result; +} + +/// @return "n1" modulus "n2", taking care of dividing by zero. +varnumber_T num_modulus(varnumber_T n1, varnumber_T n2) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Give an error when n2 is 0? + return (n2 == 0) ? 0 : (n1 % n2); +} + +/// Initialize the global and v: variables. +void eval_init(void) +{ + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + + struct vimvar *p; + + init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); + init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE); + vimvardict.dv_lock = VAR_FIXED; + hash_init(&compat_hashtab); + func_init(); + + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { + p = &vimvars[i]; + assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); + STRCPY(p->vv_di.di_key, p->vv_name); + if (p->vv_flags & VV_RO) { + p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + } else if (p->vv_flags & VV_RO_SBX) { + p->vv_di.di_flags = DI_FLAGS_RO_SBX | DI_FLAGS_FIX; + } else { + p->vv_di.di_flags = DI_FLAGS_FIX; + } + + // add to v: scope dict, unless the value is not always available + if (p->vv_type != VAR_UNKNOWN) { + hash_add(&vimvarht, p->vv_di.di_key); + } + if (p->vv_flags & VV_COMPAT) { + // add to compat scope dict + hash_add(&compat_hashtab, p->vv_di.di_key); + } + } + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; + + dict_T *const msgpack_types_dict = tv_dict_alloc(); + for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { + list_T *const type_list = tv_list_alloc(0); + tv_list_set_lock(type_list, VAR_FIXED); + tv_list_ref(type_list); + dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); + di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; + di->di_tv = (typval_T) { + .v_type = VAR_LIST, + .vval = { .v_list = type_list, }, + }; + eval_msgpack_type_lists[i] = type_list; + if (tv_dict_add(msgpack_types_dict, di) == FAIL) { + // There must not be duplicate items in this dictionary by definition. + abort(); + } + } + msgpack_types_dict->dv_lock = VAR_FIXED; + + set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); + + set_vim_var_dict(VV_EVENT, tv_dict_alloc_lock(VAR_FIXED)); + set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown)); + set_vim_var_nr(VV_STDERR, CHAN_STDERR); + set_vim_var_nr(VV_SEARCHFORWARD, 1L); + set_vim_var_nr(VV_HLSEARCH, 1L); + set_vim_var_nr(VV_COUNT1, 1); + set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER); + set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING); + set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC); + set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST); + set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT); + set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT); + set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL); + set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB); + + set_vim_var_bool(VV_FALSE, kBoolVarFalse); + set_vim_var_bool(VV_TRUE, kBoolVarTrue); + set_vim_var_special(VV_NULL, kSpecialVarNull); + set_vim_var_nr(VV_NUMBERMAX, VARNUMBER_MAX); + set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN); + set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8); + set_vim_var_special(VV_EXITING, kSpecialVarNull); + + set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); + + vimvars[VV_LUA].vv_type = VAR_PARTIAL; + vvlua_partial = xcalloc(1, sizeof(partial_T)); + vimvars[VV_LUA].vv_partial = vvlua_partial; + // this value shouldn't be printed, but if it is, do not crash + vvlua_partial->pt_name = xmallocz(0); + vvlua_partial->pt_refcount++; + + set_reg_var(0); // default for v:register is not 0 but '"' +} + +#if defined(EXITFREE) +void eval_clear(void) +{ + struct vimvar *p; + + for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { + p = &vimvars[i]; + if (p->vv_di.di_tv.v_type == VAR_STRING) { + XFREE_CLEAR(p->vv_str); + } else if (p->vv_di.di_tv.v_type == VAR_LIST) { + tv_list_unref(p->vv_list); + p->vv_list = NULL; + } + } + hash_clear(&vimvarht); + hash_init(&vimvarht); // garbage_collect() will access it + hash_clear(&compat_hashtab); + + free_scriptnames(); +# ifdef HAVE_WORKING_LIBINTL + free_locales(); +# endif + + // global variables + vars_clear(&globvarht); + + // autoloaded script names + ga_clear_strings(&ga_loaded); + + /* Script-local variables. First clear all the variables and in a second + * loop free the scriptvar_T, because a variable in one script might hold + * a reference to the whole scope of another script. */ + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + vars_clear(&SCRIPT_VARS(i)); + } + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + xfree(SCRIPT_SV(i)); + } + ga_clear(&ga_scripts); + + // unreferenced lists and dicts + (void)garbage_collect(false); + + // functions not garbage collected + free_all_functions(); +} + +#endif + +/// Set an internal variable to a string value. Creates the variable if it does +/// not already exist. +void set_internal_string_var(const char *name, char *value) + FUNC_ATTR_NONNULL_ARG(1) +{ + typval_T tv = { + .v_type = VAR_STRING, + .vval.v_string = value, + }; + + set_var(name, strlen(name), &tv, true); +} + +static lval_T *redir_lval = NULL; +static garray_T redir_ga; // Only valid when redir_lval is not NULL. +static char *redir_endp = NULL; +static char *redir_varname = NULL; + +/// Start recording command output to a variable +/// +/// @param append append to an existing variable +/// +/// @return OK if successfully completed the setup. FAIL otherwise. +int var_redir_start(char *name, int append) +{ + int save_emsg; + int err; + typval_T tv; + + // Catch a bad name early. + if (!eval_isnamec1(*name)) { + emsg(_(e_invarg)); + return FAIL; + } + + // Make a copy of the name, it is used in redir_lval until redir ends. + redir_varname = xstrdup(name); + + redir_lval = xcalloc(1, sizeof(lval_T)); + + // The output is stored in growarray "redir_ga" until redirection ends. + ga_init(&redir_ga, (int)sizeof(char), 500); + + // Parse the variable name (can be a dict or list entry). + redir_endp = get_lval(redir_varname, NULL, redir_lval, false, false, + 0, FNE_CHECK_START); + if (redir_endp == NULL || redir_lval->ll_name == NULL + || *redir_endp != NUL) { + clear_lval(redir_lval); + if (redir_endp != NULL && *redir_endp != NUL) { + // Trailing characters are present after the variable name + emsg(_(e_trailing)); + } else { + emsg(_(e_invarg)); + } + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + /* check if we can write to the variable: set it to or append an empty + * string */ + save_emsg = did_emsg; + did_emsg = FALSE; + tv.v_type = VAR_STRING; + tv.vval.v_string = ""; + if (append) { + set_var_lval(redir_lval, redir_endp, &tv, true, false, "."); + } else { + set_var_lval(redir_lval, redir_endp, &tv, true, false, "="); + } + clear_lval(redir_lval); + err = did_emsg; + did_emsg |= save_emsg; + if (err) { + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + return OK; +} + +/// Append "value[value_len]" to the variable set by var_redir_start(). +/// The actual appending is postponed until redirection ends, because the value +/// appended may in fact be the string we write to, changing it may cause freed +/// memory to be used: +/// :redir => foo +/// :let foo +/// :redir END +void var_redir_str(char *value, int value_len) +{ + int len; + + if (redir_lval == NULL) { + return; + } + + if (value_len == -1) { + len = (int)STRLEN(value); // Append the entire string + } else { + len = value_len; // Append only "value_len" characters + } + + ga_grow(&redir_ga, len); + memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, (size_t)len); + redir_ga.ga_len += len; +} + +/// Stop redirecting command output to a variable. +/// Frees the allocated memory. +void var_redir_stop(void) +{ + typval_T tv; + + if (redir_lval != NULL) { + // If there was no error: assign the text to the variable. + if (redir_endp != NULL) { + ga_append(&redir_ga, NUL); // Append the trailing NUL. + tv.v_type = VAR_STRING; + tv.vval.v_string = redir_ga.ga_data; + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = get_lval(redir_varname, NULL, redir_lval, + false, false, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) { + set_var_lval(redir_lval, redir_endp, &tv, false, false, "."); + } + clear_lval(redir_lval); + } + + // free the collected output + XFREE_CLEAR(redir_ga.ga_data); + + XFREE_CLEAR(redir_lval); + } + XFREE_CLEAR(redir_varname); +} + +int eval_charconvert(const char *const enc_from, const char *const enc_to, + const char *const fname_from, const char *const fname_to) +{ + bool 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((char *)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) { + return FAIL; + } + return OK; +} + +int eval_printexpr(const char *const fname, const char *const args) +{ + bool err = false; + + set_vim_var_string(VV_FNAME_IN, fname, -1); + set_vim_var_string(VV_CMDARG, args, -1); + if (eval_to_bool((char *)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(fname); + return FAIL; + } + return OK; +} + +void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) +{ + bool err = false; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_NEW, newfile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool((char *)p_dex, &err, NULL, false); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_NEW, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} + +void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) +{ + bool err = false; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_DIFF, difffile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool((char *)p_pex, &err, NULL, false); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_DIFF, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} + +/// Top level evaluation function, returning a boolean. +/// Sets "error" to TRUE if there was an error. +/// +/// @param skip only parse, don't execute +/// +/// @return TRUE or FALSE. +int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip) +{ + typval_T tv; + bool retval = false; + + if (skip) { + emsg_skip++; + } + if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + *error = true; + } else { + *error = false; + if (!skip) { + retval = (tv_get_number_chk(&tv, error) != 0); + tv_clear(&tv); + } + } + if (skip) { + emsg_skip--; + } + + return retval; +} + +/// Call eval1() and give an error message if not done at a lower level. +static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + const char *const start = *arg; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; + + const int ret = eval1(arg, rettv, evaluate); + if (ret == FAIL) { + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() + && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { + semsg(_(e_invexpr2), start); + } + } + return ret; +} + +/// @return whether a typval is a valid expression to pass to eval_expr_typval() +/// or eval_expr_to_bool(). An empty string returns false; +bool eval_expr_valid_arg(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST +{ + return tv->v_type != VAR_UNKNOWN + && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL)); +} + +int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) +{ + funcexe_T funcexe = FUNCEXE_INIT; + + if (expr->v_type == VAR_FUNC) { + const char *const s = expr->vval.v_string; + if (s == NULL || *s == NUL) { + return FAIL; + } + funcexe.evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + } else if (expr->v_type == VAR_PARTIAL) { + partial_T *const partial = expr->vval.v_partial; + const char *const s = partial_name(partial); + if (s == NULL || *s == NUL) { + return FAIL; + } + funcexe.evaluate = true; + funcexe.partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + } else { + char buf[NUMBUFLEN]; + char *s = (char *)tv_get_string_buf_chk(expr, buf); + if (s == NULL) { + return FAIL; + } + s = skipwhite(s); + if (eval1_emsg(&s, rettv, true) == FAIL) { + return FAIL; + } + if (*skipwhite(s) != NUL) { // check for trailing chars after expr + tv_clear(rettv); + semsg(_(e_invexpr2), s); + return FAIL; + } + } + return OK; +} + +/// Like eval_to_bool() but using a typval_T instead of a string. +/// Works for string, funcref and partial. +bool eval_expr_to_bool(const typval_T *expr, bool *error) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T argv, rettv; + + if (eval_expr_typval(expr, &argv, 0, &rettv) == FAIL) { + *error = true; + return false; + } + const bool res = (tv_get_number_chk(&rettv, error) != 0); + tv_clear(&rettv); + return res; +} + +/// Top level evaluation function, returning a string +/// +/// @param[in] arg String to evaluate. +/// @param nextcmd Pointer to the start of the next Ex command. +/// @param[in] skip If true, only do parsing to nextcmd without reporting +/// errors or actually evaluating anything. +/// +/// @return [allocated] string result of evaluation or NULL in case of error or +/// when skipping. +char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + typval_T tv; + char *retval; + + if (skip) { + emsg_skip++; + } + if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) { + retval = NULL; + } else { + retval = xstrdup(tv_get_string(&tv)); + tv_clear(&tv); + } + if (skip) { + emsg_skip--; + } + + return retval; +} + +/// Skip over an expression at "*pp". +/// +/// @return FAIL for an error, OK otherwise. +int skip_expr(char **pp) +{ + typval_T rettv; + + *pp = skipwhite(*pp); + return eval1(pp, &rettv, false); +} + +/// Top level evaluation function, returning a string. +/// +/// @param convert when true convert a List into a sequence of lines and convert +/// a Float to a String. +/// +/// @return pointer to allocated memory, or NULL for failure. +char *eval_to_string(char *arg, char **nextcmd, bool convert) +{ + typval_T tv; + char *retval; + garray_T ga; + + if (eval0(arg, &tv, nextcmd, true) == FAIL) { + retval = NULL; + } else { + if (convert && tv.v_type == VAR_LIST) { + ga_init(&ga, (int)sizeof(char), 80); + if (tv.vval.v_list != NULL) { + tv_list_join(&ga, tv.vval.v_list, "\n"); + if (tv_list_len(tv.vval.v_list) > 0) { + ga_append(&ga, NL); + } + } + ga_append(&ga, NUL); + retval = (char *)ga.ga_data; + } else if (convert && tv.v_type == VAR_FLOAT) { + char numbuf[NUMBUFLEN]; + vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float); + retval = xstrdup(numbuf); + } else { + retval = xstrdup(tv_get_string(&tv)); + } + tv_clear(&tv); + } + + return retval; +} + +/// Call eval_to_string() without using current local variables and using +/// textlock. +/// +/// @param use_sandbox when TRUE, use the sandbox. +char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox) +{ + char *retval; + funccal_entry_T funccal_entry; + + save_funccal(&funccal_entry); + if (use_sandbox) { + sandbox++; + } + textlock++; + retval = eval_to_string(arg, nextcmd, false); + if (use_sandbox) { + sandbox--; + } + textlock--; + restore_funccal(); + return retval; +} + +/// Top level evaluation function, returning a number. +/// Evaluates "expr" silently. +/// +/// @return -1 for an error. +varnumber_T eval_to_number(char *expr) +{ + typval_T rettv; + varnumber_T retval; + char *p = skipwhite(expr); + + ++emsg_off; + + if (eval1(&p, &rettv, true) == FAIL) { + retval = -1; + } else { + retval = tv_get_number_chk(&rettv, NULL); + tv_clear(&rettv); + } + --emsg_off; + + return retval; +} + +/// Top level evaluation function. +/// +/// @return an allocated typval_T with the result or +/// NULL when there is an error. +typval_T *eval_expr(char *arg) +{ + typval_T *tv = xmalloc(sizeof(*tv)); + if (eval0(arg, tv, NULL, true) == FAIL) { + XFREE_CLEAR(tv); + } + return tv; +} + +/// List Vim variables. +void list_vim_vars(int *first) +{ + list_hashtable_vars(&vimvarht, "v:", false, first); +} + +/// List script-local variables, if there is a script. +void list_script_vars(int *first) +{ + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { + list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); + } +} + +bool is_vimvarht(const hashtab_T *ht) +{ + return ht == &vimvarht; +} + +bool is_compatht(const hashtab_T *ht) +{ + return ht == &compat_hashtab; +} + +/// Prepare v: variable "idx" to be used. +/// Save the current typeval in "save_tv". +/// When not used yet add the variable to the v: hashtable. +void prepare_vimvar(int idx, typval_T *save_tv) +{ + *save_tv = vimvars[idx].vv_tv; + if (vimvars[idx].vv_type == VAR_UNKNOWN) { + hash_add(&vimvarht, vimvars[idx].vv_di.di_key); + } +} + +/// Restore v: variable "idx" to typeval "save_tv". +/// When no longer defined, remove the variable from the v: hashtable. +void restore_vimvar(int idx, typval_T *save_tv) +{ + hashitem_T *hi; + + vimvars[idx].vv_tv = *save_tv; + if (vimvars[idx].vv_type == VAR_UNKNOWN) { + hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key); + if (HASHITEM_EMPTY(hi)) { + internal_error("restore_vimvar()"); + } else { + hash_remove(&vimvarht, hi); + } + } +} + +/// If there is a window for "curbuf", make it the current window. +void find_win_for_curbuf(void) +{ + for (wininfo_T *wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) { + if (wip->wi_win != NULL) { + curwin = wip->wi_win; + break; + } + } +} + +/// Evaluate an expression to a list with suggestions. +/// For the "expr:" part of 'spellsuggest'. +/// +/// @return NULL when there is an error. +list_T *eval_spell_expr(char *badword, char *expr) +{ + typval_T save_val; + typval_T rettv; + list_T *list = NULL; + char *p = skipwhite(expr); + + // Set "v:val" to the bad word. + prepare_vimvar(VV_VAL, &save_val); + vimvars[VV_VAL].vv_type = VAR_STRING; + vimvars[VV_VAL].vv_str = badword; + if (p_verbose == 0) { + ++emsg_off; + } + + if (eval1(&p, &rettv, true) == OK) { + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + } else { + list = rettv.vval.v_list; + } + } + + if (p_verbose == 0) { + --emsg_off; + } + restore_vimvar(VV_VAL, &save_val); + + return list; +} + +/// Get spell word from an entry from spellsuggest=expr: +/// +/// Entry in question is supposed to be a list (to be checked by the caller) +/// with two items: a word and a score represented as an unsigned number +/// (whether it actually is unsigned is not checked). +/// +/// Used to get the good word and score from the eval_spell_expr() result. +/// +/// @param[in] list List to get values from. +/// @param[out] ret_word Suggested word. Not initialized if return value is +/// -1. +/// +/// @return -1 in case of error, score otherwise. +int get_spellword(list_T *const list, const char **ret_word) +{ + if (tv_list_len(list) != 2) { + emsg(_("E5700: Expression from 'spellsuggest' must yield lists with " + "exactly two values")); + return -1; + } + *ret_word = tv_list_find_str(list, 0); + if (*ret_word == NULL) { + return -1; + } + return (int)tv_list_find_nr(list, -1, NULL); +} + +// Call some vim script function and return the result in "*rettv". +// Uses argv[0] to argv[argc-1] for the function arguments. argv[argc] +// should have type VAR_UNKNOWN. +// +// @return OK or FAIL. +int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + int ret; + int len = (int)STRLEN(func); + partial_T *pt = NULL; + + if (len >= 6 && !memcmp(func, "v:lua.", 6)) { + func += 6; + len = check_luafunc_name(func, false); + if (len == 0) { + ret = FAIL; + goto fail; + } + pt = vvlua_partial; + } + + rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = pt; + ret = call_func(func, len, rettv, argc, argv, &funcexe); + +fail: + if (ret == FAIL) { + tv_clear(rettv); + } + + return ret; +} +/// Call Vim script function and return the result as a number +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with typval_T arguments. +/// +/// @return -1 when calling function fails, result of function otherwise. +varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv) + FUNC_ATTR_NONNULL_ALL +{ + typval_T rettv; + varnumber_T retval; + + if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) { + return -1; + } + retval = tv_get_number_chk(&rettv, NULL); + tv_clear(&rettv); + return retval; +} +/// Call Vim script function and return the result as a string +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with typval_T arguments. +/// +/// @return [allocated] NULL when calling function fails, allocated string +/// otherwise. +char *call_func_retstr(const char *const func, int argc, typval_T *argv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC +{ + typval_T rettv; + // All arguments are passed as strings, no conversion to number. + if (call_vim_function(func, argc, argv, &rettv) + == FAIL) { + return NULL; + } + + char *const retval = xstrdup(tv_get_string(&rettv)); + tv_clear(&rettv); + return retval; +} +/// Call Vim script function and return the result as a List +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with typval_T arguments. +/// +/// @return [allocated] NULL when calling function fails or return tv is not a +/// List, allocated List otherwise. +void *call_func_retlist(const char *func, int argc, typval_T *argv) + FUNC_ATTR_NONNULL_ALL +{ + typval_T rettv; + + // All arguments are passed as strings, no conversion to number. + if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) { + return NULL; + } + + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + return NULL; + } + + return rettv.vval.v_list; +} + +/// Prepare profiling for entering a child or something else that is not +/// counted for the script/function itself. +/// Should always be called in pair with prof_child_exit(). +/// +/// @param tm place to store waittime +void prof_child_enter(proftime_T *tm) +{ + funccall_T *fc = get_current_funccal(); + + if (fc != NULL && fc->func->uf_profiling) { + fc->prof_child = profile_start(); + } + + script_prof_save(tm); +} + +/// Take care of time spent in a child. +/// Should always be called after prof_child_enter(). +/// +/// @param tm where waittime was stored +void prof_child_exit(proftime_T *tm) +{ + funccall_T *fc = get_current_funccal(); + + if (fc != NULL && fc->func->uf_profiling) { + fc->prof_child = profile_end(fc->prof_child); + // don't count waiting time + fc->prof_child = profile_sub_wait(*tm, fc->prof_child); + fc->func->uf_tm_children = + profile_add(fc->func->uf_tm_children, fc->prof_child); + fc->func->uf_tml_children = + profile_add(fc->func->uf_tml_children, fc->prof_child); + } + script_prof_restore(tm); +} + +/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding +/// it in "*cp". Doesn't give error messages. +int eval_foldexpr(char *arg, int *cp) +{ + typval_T tv; + varnumber_T retval; + int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL); + + ++emsg_off; + if (use_sandbox) { + ++sandbox; + } + ++textlock; + *cp = NUL; + if (eval0(arg, &tv, NULL, true) == FAIL) { + retval = 0; + } else { + // If the result is a number, just return the number. + if (tv.v_type == VAR_NUMBER) { + retval = tv.vval.v_number; + } else if (tv.v_type != VAR_STRING || tv.vval.v_string == NULL) { + retval = 0; + } else { + // If the result is a string, check if there is a non-digit before + // the number. + char *s = tv.vval.v_string; + if (!ascii_isdigit(*s) && *s != '-') { + *cp = (char_u)(*s++); + } + retval = atol(s); + } + tv_clear(&tv); + } + --emsg_off; + if (use_sandbox) { + --sandbox; + } + --textlock; + + return (int)retval; +} + +<<<<<<< HEAD +/// ":cons[t] var = expr1" define constant +/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list +/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list +void ex_const(exarg_T *eap) +{ + ex_let_const(eap, true); +} + +/// Get a list of lines from a HERE document. The here document is a list of +/// lines surrounded by a marker. +/// cmd << {marker} +/// {line1} +/// {line2} +/// .... +/// {marker} +/// +/// The {marker} is a string. If the optional 'trim' word is supplied before the +/// marker, then the leading indentation before the lines (matching the +/// indentation in the 'cmd' line) is stripped. +/// +/// @return a List with {lines} or NULL. +static list_T *heredoc_get(exarg_T *eap, char *cmd) +{ + char *marker; + char *p; + int marker_indent_len = 0; + int text_indent_len = 0; + char *text_indent = NULL; + + if (eap->getline == NULL) { + emsg(_("E991: cannot use =<< here")); + return NULL; + } + + // Check for the optional 'trim' word before the marker + cmd = skipwhite(cmd); + if (STRNCMP(cmd, "trim", 4) == 0 + && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { + cmd = skipwhite(cmd + 4); + + // Trim the indentation from all the lines in the here document. + // The amount of indentation trimmed is the same as the indentation of + // the first line after the :let command line. To find the end marker + // the indent of the :let command line is trimmed. + p = *eap->cmdlinep; + while (ascii_iswhite(*p)) { + p++; + marker_indent_len++; + } + text_indent_len = -1; + } + + // The marker is the next word. + if (*cmd != NUL && *cmd != '"') { + marker = skipwhite(cmd); + p = (char *)skiptowhite((char_u *)marker); + if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { + emsg(_(e_trailing)); + return NULL; + } + *p = NUL; + if (islower(*marker)) { + emsg(_("E221: Marker cannot start with lower case letter")); + return NULL; + } + } else { + emsg(_("E172: Missing marker")); + return NULL; + } + + list_T *l = tv_list_alloc(0); + for (;;) { + int mi = 0; + int ti = 0; + + char *theline = eap->getline(NUL, eap->cookie, 0, false); + if (theline == NULL) { + semsg(_("E990: Missing end marker '%s'"), marker); + break; + } + + // with "trim": skip the indent matching the :let line to find the + // marker + if (marker_indent_len > 0 + && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { + mi = marker_indent_len; + } + if (STRCMP(marker, theline + mi) == 0) { + xfree(theline); + break; + } + if (text_indent_len == -1 && *theline != NUL) { + // set the text indent from the first line. + p = theline; + text_indent_len = 0; + while (ascii_iswhite(*p)) { + p++; + text_indent_len++; + } + text_indent = xstrnsave(theline, (size_t)text_indent_len); + } + // with "trim": skip the indent matching the first line + if (text_indent != NULL) { + for (ti = 0; ti < text_indent_len; ti++) { + if (theline[ti] != text_indent[ti]) { + break; + } + } + } + + tv_list_append_string(l, theline + ti, -1); + xfree(theline); + } + xfree(text_indent); + + return l; +} + +/// ":let" list all variable values +/// ":let var1 var2" list variable values +/// ":let var = expr" assignment command. +/// ":let var += expr" assignment command. +/// ":let var -= expr" assignment command. +/// ":let var *= expr" assignment command. +/// ":let var /= expr" assignment command. +/// ":let var %= expr" assignment command. +/// ":let var .= expr" assignment command. +/// ":let var ..= expr" assignment command. +/// ":let [var1, var2] = expr" unpack list. +/// ":let [name, ..., ; lastname] = expr" unpack list. +void ex_let(exarg_T *eap) +{ + ex_let_const(eap, false); +} + +static void ex_let_const(exarg_T *eap, const bool is_const) +{ + char *arg = eap->arg; + char *expr = NULL; + typval_T rettv; + int i; + int var_count = 0; + int semicolon = 0; + char op[2]; + char *argend; + int first = true; + + argend = (char *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { + return; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } + expr = skipwhite(argend); + if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL + && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { + // ":let" without "=": list variables + if (*arg == '[') { + emsg(_(e_invarg)); + } else if (!ends_excmd(*arg)) { + // ":let var1 var2" + arg = (char *)list_arg_vars(eap, (const char *)arg, &first); + } else if (!eap->skip) { + // ":let" + list_glob_vars(&first); + list_buf_vars(&first); + list_win_vars(&first); + list_tab_vars(&first); + list_script_vars(&first); + list_func_vars(&first); + list_vim_vars(&first); + } + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { + // HERE document + list_T *l = heredoc_get(eap, expr + 3); + if (l != NULL) { + tv_list_set_ret(&rettv, l); + if (!eap->skip) { + op[0] = '='; + op[1] = NUL; + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + } + tv_clear(&rettv); + } + } else { + op[0] = '='; + op[1] = NUL; + if (*expr != '=') { + if (vim_strchr("+-*/%.", *expr) != NULL) { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') { // ..= + expr++; + } + } + expr = skipwhite(expr + 2); + } else { + expr = skipwhite(expr + 1); + } + + if (eap->skip) { + ++emsg_skip; + } + i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); + if (eap->skip) { + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; + } else if (i != FAIL) { + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + tv_clear(&rettv); + } + } +} + +/// Assign the typevalue "tv" to the variable or variables at "arg_start". +/// Handles both "var" with any type and "[var, var; var]" with a list type. +/// When "op" is not NULL it points to a string with characters that +/// must appear after the variable(s). Use "+", "-" or "." for add, subtract +/// or concatenate. +/// +/// @param copy copy values from "tv", don't move +/// @param semicolon from skip_var_list() +/// @param var_count from skip_var_list() +/// @param is_const lock variables for :const +/// +/// @return OK or FAIL; +static int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count, + int is_const, char *op) +{ + char *arg = arg_start; + typval_T ltv; + + if (*arg != '[') { + /* + * ":let var = expr" or ":for var in list" + */ + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { + return FAIL; + } + return OK; + } + + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { + emsg(_(e_listreq)); + return FAIL; + } + list_T *const l = tv->vval.v_list; + + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { + emsg(_("E687: Less targets than List items")); + return FAIL; + } + if (var_count - semicolon > len) { + emsg(_("E688: More targets than List items")); + return FAIL; + } + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". + assert(l != NULL); + + listitem_T *item = tv_list_first(l); + size_t rest_len = (size_t)tv_list_len(l); + while (*arg != ']') { + arg = skipwhite(arg + 1); + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op); + if (arg == NULL) { + return FAIL; + } + rest_len--; + + item = TV_LIST_ITEM_NEXT(l, item); + arg = skipwhite(arg); + if (*arg == ';') { + /* Put the rest of the list (may be empty) in the var after ';'. + * Create a new list for this. */ + list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len); + while (item != NULL) { + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); + } + + ltv.v_type = VAR_LIST; + ltv.v_lock = VAR_UNLOCKED; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); + + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, "]", op); + tv_clear(<v); + if (arg == NULL) { + return FAIL; + } + break; + } else if (*arg != ',' && *arg != ']') { + internal_error("ex_let_vars()"); + return FAIL; + } + } + + return OK; +} + +/// Skip over assignable variable "var" or list of variables "[var, var]". +/// Used for ":let varvar = expr" and ":for varvar in expr". +/// For "[var, var]" increment "*var_count" for each variable. +/// for "[var, var; var]" set "semicolon". +/// +/// @return NULL for an error. +static const char *skip_var_list(const char *arg, int *var_count, int *semicolon) +{ + const char *p; + const char *s; + + if (*arg == '[') { + // "[var, var]": find the matching ']'. + p = arg; + for (;;) { + p = skipwhite(p + 1); // skip whites after '[', ';' or ',' + s = skip_var_one((char *)p); + if (s == p) { + semsg(_(e_invarg2), p); + return NULL; + } + ++*var_count; + + p = skipwhite(s); + if (*p == ']') { + break; + } else if (*p == ';') { + if (*semicolon == 1) { + emsg(_("E452: Double ; in list of variables")); + return NULL; + } + *semicolon = 1; + } else if (*p != ',') { + semsg(_(e_invarg2), p); + return NULL; + } + } + return p + 1; + } else { + return skip_var_one((char *)arg); + } +} + +/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, +/// l[idx]. +static const char *skip_var_one(const char *arg) +{ + if (*arg == '@' && arg[1] != NUL) { + return arg + 1 + utfc_ptr2len(arg + 1); + } + return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, + NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); +} + +/// List variables for hashtab "ht" with prefix "prefix". +/// +/// @param empty if TRUE also list NULL strings as empty strings. +void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) +{ + hashitem_T *hi; + dictitem_T *di; + int todo; + + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + di = TV_DICT_HI2DI(hi); + char buf[IOSIZE]; + + // apply :filter /pat/ to variable name + xstrlcpy(buf, prefix, IOSIZE); + xstrlcat(buf, (char *)di->di_key, IOSIZE); + if (message_filtered((char_u *)buf)) { + continue; + } + + if (empty || di->di_tv.v_type != VAR_STRING + || di->di_tv.vval.v_string != NULL) { + list_one_var(di, prefix, first); + } + } + } +} + +/// List global variables. +static void list_glob_vars(int *first) +{ + list_hashtable_vars(&globvarht, "", true, first); +} + +/// List buffer variables. +static void list_buf_vars(int *first) +{ + list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); +} + +/// List window variables. +static void list_win_vars(int *first) +{ + list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); +} + +/// List tab page variables. +static void list_tab_vars(int *first) +{ + list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); +} + +/// List Vim variables. +static void list_vim_vars(int *first) +{ + list_hashtable_vars(&vimvarht, "v:", false, first); +} + +/// List script-local variables, if there is a script. +static void list_script_vars(int *first) +{ + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { + list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); + } +} + +/// List variables in "arg". +static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) +{ + int error = FALSE; + int len; + const char *name; + const char *name_start; + typval_T tv; + + while (!ends_excmd(*arg) && !got_int) { + if (error || eap->skip) { + arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); + if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) { + emsg_severe = true; + emsg(_(e_trailing)); + break; + } + } else { + // get_name_len() takes care of expanding curly braces + name_start = name = arg; + char *tofree; + len = get_name_len(&arg, &tofree, true, true); + if (len <= 0) { + /* This is mainly to keep test 49 working: when expanding + * curly braces fails overrule the exception error message. */ + if (len < 0 && !aborting()) { + emsg_severe = true; + semsg(_(e_invarg2), arg); + break; + } + error = TRUE; + } else { + if (tofree != NULL) { + name = tofree; + } + if (get_var_tv(name, len, &tv, NULL, true, false) + == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) + const char *const arg_subsc = arg; + if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { + error = true; + } else { + if (arg == arg_subsc && len == 2 && name[1] == ':') { + switch (*name) { + case 'g': + list_glob_vars(first); break; + case 'b': + list_buf_vars(first); break; + case 'w': + list_win_vars(first); break; + case 't': + list_tab_vars(first); break; + case 'v': + list_vim_vars(first); break; + case 's': + list_script_vars(first); break; + case 'l': + list_func_vars(first); break; + default: + semsg(_("E738: Can't list variables for %s"), name); + } + } else { + char *const s = encode_tv2echo(&tv, NULL); + const char *const used_name = (arg == arg_subsc + ? name + : name_start); + const ptrdiff_t name_size = (used_name == tofree + ? (ptrdiff_t)strlen(used_name) + : (arg - used_name)); + list_one_var_a("", used_name, name_size, + tv.v_type, s == NULL ? "" : s, first); + xfree(s); + } + tv_clear(&tv); + } + } + } + + xfree(tofree); + } + + arg = (const char *)skipwhite(arg); + } + + return arg; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *arg_end = NULL; + int len; + int opt_flags; + char *tofree = NULL; + + /* + * ":let $VAR = expr": Set environment variable. + */ + if (*arg == '$') { + if (is_const) { + emsg(_("E996: Cannot lock an environment variable")); + return NULL; + } + // Find the end of the name. + arg++; + char *name = arg; + len = get_env_len((const char **)&arg); + if (len == 0) { + semsg(_(e_invarg2), name - 1); + } else { + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg)) == NULL) { + emsg(_(e_letunexp)); + } else if (!check_secure()) { + const char c1 = name[len]; + name[len] = NUL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = vim_getenv(name); + + if (s != NULL) { + tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; + xfree(s); + } + } + if (p != NULL) { + os_setenv(name, p, 1); + if (STRICMP(name, "HOME") == 0) { + init_homedir(); + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } + arg_end = arg; + } + name[len] = c1; + xfree(tofree); + } + } + // ":let &option = expr": Set option value. + // ":let &l:option = expr": Set local option value. + // ":let &g:option = expr": Set global option value. + } else if (*arg == '&') { + if (is_const) { + emsg(_("E996: Cannot lock an option")); + return NULL; + } + // Find the end of the name. + char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); + if (p == NULL + || (endchars != NULL + && vim_strchr(endchars, *skipwhite(p)) == NULL)) { + emsg(_(e_letunexp)); + } else { + int opt_type; + long numval; + char *stringval = NULL; + const char *s = NULL; + + const char c1 = *p; + *p = NUL; + + varnumber_T n = tv_get_number(tv); + if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + s = tv_get_string_chk(tv); // != NULL if number or string. + } + if (s != NULL && op != NULL && *op != '=') { + opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + if ((opt_type == 1 && *op == '.') + || (opt_type == 0 && *op != '.')) { + semsg(_(e_letwrong), op); + s = NULL; // don't set the value + } else { + if (opt_type == 1) { // number + switch (*op) { + case '+': + n = numval + n; break; + case '-': + n = numval - n; break; + case '*': + n = numval * n; break; + case '/': + n = num_divide(numval, n); break; + case '%': + n = num_modulus(numval, n); break; + } + } else if (opt_type == 0 && stringval != NULL) { // string + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; + } + } + } + if (s != NULL || tv->v_type == VAR_BOOL + || tv->v_type == VAR_SPECIAL) { + set_option_value((const char *)arg, n, s, opt_flags); + arg_end = p; + } + *p = c1; + xfree(stringval); + } + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + if (is_const) { + emsg(_("E996: Cannot lock a register")); + return NULL; + } + arg++; + + int regname = utf_ptr2char(arg); + int mblen = utf_ptr2len(arg); + + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) { + emsg(_(e_letunexp)); + } else { + char *s; + + char *ptofree = NULL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + s = get_reg_contents(regname == '@' ? '"' : regname, kGRegExprSrc); + if (s != NULL) { + ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); + p = (const char *)ptofree; + xfree(s); + } + } + if (p != NULL) { + + write_reg_contents(regname == '@' ? '"' : regname, + (const char_u *)p, (ssize_t)STRLEN(p), false); + arg_end = arg + mblen; + } + xfree(ptofree); + } + } + /* + * ":let var = expr": Set internal variable. + * ":let {expr} = expr": Idem, name made with curly braces + */ + else if (eval_isnamec1(*arg) || *arg == '{') { + lval_T lv; + + char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); + if (p != NULL && lv.ll_name != NULL) { + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { + emsg(_(e_letunexp)); + } else { + set_var_lval(&lv, p, tv, copy, is_const, op); + arg_end = p; + } + } + clear_lval(&lv); + } else { + semsg(_(e_invarg2), arg); + } + + return arg_end; +} + +======= +>>>>>>> upstream/master +// TODO(ZyX-I): move to eval/executor + +/// Get an lvalue +/// +/// Lvalue may be +/// - variable: "name", "na{me}" +/// - dictionary item: "dict.key", "dict['key']" +/// - list item: "list[expr]" +/// - list slice: "list[expr:expr]" +/// +/// Indexing only works if trying to use it with an existing List or Dictionary. +/// +/// @param[in] name Name to parse. +/// @param rettv Pointer to the value to be assigned or NULL. +/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name` +/// is NULL. +/// @param[in] unlet True if using `:unlet`. This results in slightly +/// different behaviour when something is wrong; must end in +/// space or cmd separator. +/// @param[in] skip True when skipping. +/// @param[in] flags @see GetLvalFlags. +/// @param[in] fne_flags Flags for find_name_end(). +/// +/// @return A pointer to just after the name, including indexes. Returns NULL +/// for a parsing error, but it is still needed to free items in lp. +char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const bool unlet, + const bool skip, const int flags, const int fne_flags) + FUNC_ATTR_NONNULL_ARG(1, 3) +{ + dictitem_T *v; + typval_T var1; + typval_T var2; + int empty1 = FALSE; + listitem_T *ni; + hashtab_T *ht = NULL; + int quiet = flags & GLV_QUIET; + + // Clear everything in "lp". + memset(lp, 0, sizeof(lval_T)); + + if (skip) { + // When skipping just find the end of the name. + lp->ll_name = (const char *)name; + return (char *)find_name_end(name, NULL, NULL, + FNE_INCL_BR | fne_flags); + } + + // Find the end of the name. + char *expr_start; + char *expr_end; + char *p = (char *)find_name_end(name, (const char **)&expr_start, + (const char **)&expr_end, + fne_flags); + if (expr_start != NULL) { + // Don't expand the name when we already know there is an error. + if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) + && *p != '[' && *p != '.') { + emsg(_(e_trailing)); + return NULL; + } + + lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); + lp->ll_name = lp->ll_exp_name; + if (lp->ll_exp_name == NULL) { + // Report an invalid expression in braces, unless the + // expression evaluation has been cancelled due to an + // aborting error, an interrupt, or an exception. + if (!aborting() && !quiet) { + emsg_severe = true; + semsg(_(e_invarg2), name); + return NULL; + } + lp->ll_name_len = 0; + } else { + lp->ll_name_len = strlen(lp->ll_name); + } + } else { + lp->ll_name = (const char *)name; + lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); + } + + // Without [idx] or .key we are done. + if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { + return p; + } + + // Only pass &ht when we would write to the variable, it prevents autoload + // as well. + v = find_var(lp->ll_name, lp->ll_name_len, + (flags & GLV_READ_ONLY) ? NULL : &ht, + flags & GLV_NO_AUTOLOAD); + if (v == NULL && !quiet) { + semsg(_("E121: Undefined variable: %.*s"), + (int)lp->ll_name_len, lp->ll_name); + } + if (v == NULL) { + return NULL; + } + + // Loop until no more [idx] or .key is following. + lp->ll_tv = &v->di_tv; + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; + while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) { + if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) + && !(lp->ll_tv->v_type == VAR_DICT && lp->ll_tv->vval.v_dict != NULL) + && !(lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob != NULL)) { + if (!quiet) { + emsg(_("E689: Can only index a List, Dictionary or Blob")); + } + return NULL; + } + if (lp->ll_range) { + if (!quiet) { + emsg(_("E708: [:] must come last")); + } + return NULL; + } + + int len = -1; + char *key = NULL; + if (*p == '.') { + key = p + 1; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {} + if (len == 0) { + if (!quiet) { + emsg(_("E713: Cannot use empty key after .")); + } + return NULL; + } + p = key + len; + } else { + // Get the index [expr] or the first index [expr: ]. + p = skipwhite(p + 1); + if (*p == ':') { + empty1 = true; + } else { + empty1 = false; + if (eval1(&p, &var1, true) == FAIL) { // Recursive! + return NULL; + } + if (!tv_check_str(&var1)) { + // Not a number or string. + tv_clear(&var1); + return NULL; + } + p = skipwhite(p); + } + + // Optionally get the second index [ :expr]. + if (*p == ':') { + if (lp->ll_tv->v_type == VAR_DICT) { + if (!quiet) { + emsg(_(e_dictrange)); + } + tv_clear(&var1); + return NULL; + } + if (rettv != NULL + && !(rettv->v_type == VAR_LIST && rettv->vval.v_list != NULL) + && !(rettv->v_type == VAR_BLOB && rettv->vval.v_blob != NULL)) { + if (!quiet) { + emsg(_("E709: [:] requires a List or Blob value")); + } + tv_clear(&var1); + return NULL; + } + p = skipwhite(p + 1); + if (*p == ']') { + lp->ll_empty2 = true; + } else { + lp->ll_empty2 = false; + if (eval1(&p, &var2, true) == FAIL) { // Recursive! + tv_clear(&var1); + return NULL; + } + if (!tv_check_str(&var2)) { + // Not a number or string. + tv_clear(&var1); + tv_clear(&var2); + return NULL; + } + } + lp->ll_range = true; + } else { + lp->ll_range = false; + } + + if (*p != ']') { + if (!quiet) { + emsg(_(e_missbrac)); + } + tv_clear(&var1); + tv_clear(&var2); + return NULL; + } + + // Skip to past ']'. + p++; + } + + if (lp->ll_tv->v_type == VAR_DICT) { + if (len == -1) { + // "[key]": get key from "var1" + key = (char *)tv_get_string(&var1); // is number or string + } + lp->ll_list = NULL; + lp->ll_dict = lp->ll_tv->vval.v_dict; + lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len); + + // When assigning to a scope dictionary check that a function and + // variable name is valid (only variable name unless it is l: or + // g: dictionary). Disallow overwriting a builtin function. + if (rettv != NULL && lp->ll_dict->dv_scope != 0) { + char prevval; + int wrong; + + if (len != -1) { + prevval = key[len]; + key[len] = NUL; + } else { + prevval = 0; // Avoid compiler warning. + } + wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && tv_is_func(*rettv) + && !var_check_func_name((const char *)key, lp->ll_di == NULL)) + || !valid_varname((const char *)key)); + if (len != -1) { + key[len] = prevval; + } + if (wrong) { + return NULL; + } + } + + if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) + && len == -1 && rettv == NULL) { + tv_clear(&var1); + semsg(e_illvar, "v:['lua']"); + return NULL; + } + + if (lp->ll_di == NULL) { + // Can't add "v:" or "a:" variable. + if (lp->ll_dict == &vimvardict + || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + tv_clear(&var1); + return NULL; + } + + // Key does not exist in dict: may need to add it. + if (*p == '[' || *p == '.' || unlet) { + if (!quiet) { + semsg(_(e_dictkey), key); + } + tv_clear(&var1); + return NULL; + } + if (len == -1) { + lp->ll_newkey = xstrdup(key); + } else { + lp->ll_newkey = xstrnsave(key, (size_t)len); + } + tv_clear(&var1); + break; + // existing variable, need to check if it can be changed + } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, + (const char *)name, + (size_t)(p - name))) { + tv_clear(&var1); + return NULL; + } + + tv_clear(&var1); + lp->ll_tv = &lp->ll_di->di_tv; + } else if (lp->ll_tv->v_type == VAR_BLOB) { + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (long)tv_get_number(&var1); + } + tv_clear(&var1); + + const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); + if (lp->ll_n1 < 0 || lp->ll_n1 > bloblen + || (lp->ll_range && lp->ll_n1 == bloblen)) { + if (!quiet) { + semsg(_(e_blobidx), (int64_t)lp->ll_n1); + } + tv_clear(&var2); + return NULL; + } + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (long)tv_get_number(&var2); + tv_clear(&var2); + if (lp->ll_n2 < 0 || lp->ll_n2 >= bloblen || lp->ll_n2 < lp->ll_n1) { + if (!quiet) { + semsg(_(e_blobidx), (int64_t)lp->ll_n2); + } + return NULL; + } + } + lp->ll_blob = lp->ll_tv->vval.v_blob; + lp->ll_tv = NULL; + break; + } else { + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (long)tv_get_number(&var1); + } + tv_clear(&var1); + + lp->ll_dict = NULL; + lp->ll_list = lp->ll_tv->vval.v_list; + lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); + if (lp->ll_li == NULL) { + if (lp->ll_n1 < 0) { + lp->ll_n1 = 0; + lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); + } + } + if (lp->ll_li == NULL) { + tv_clear(&var2); + if (!quiet) { + semsg(_(e_listidx), (int64_t)lp->ll_n1); + } + return NULL; + } + + // May need to find the item or absolute index for the second + // index of a range. + // When no index given: "lp->ll_empty2" is true. + // Otherwise "lp->ll_n2" is set to the second index. + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. + tv_clear(&var2); + if (lp->ll_n2 < 0) { + ni = tv_list_find(lp->ll_list, (int)lp->ll_n2); + if (ni == NULL) { + if (!quiet) { + semsg(_(e_listidx), (int64_t)lp->ll_n2); + } + return NULL; + } + lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); + } + + // Check that lp->ll_n2 isn't before lp->ll_n1. + if (lp->ll_n1 < 0) { + lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); + } + if (lp->ll_n2 < lp->ll_n1) { + if (!quiet) { + semsg(_(e_listidx), (int64_t)lp->ll_n2); + } + return NULL; + } + } + + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); + } + } + + tv_clear(&var1); + return p; +} + +// TODO(ZyX-I): move to eval/executor + +/// Clear lval "lp" that was filled by get_lval(). +void clear_lval(lval_T *lp) +{ + xfree(lp->ll_exp_name); + xfree(lp->ll_newkey); +} + +// TODO(ZyX-I): move to eval/executor + +/// Set a variable that was parsed by get_lval() to "rettv". +/// +/// @param endp points to just after the parsed name. +/// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", +/// "%" for "%=", "." for ".=" or "=" for "=". +void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const, + const char *op) +{ + int cc; + listitem_T *ri; + dictitem_T *di; + + if (lp->ll_tv == NULL) { + cc = (char_u)(*endp); + *endp = NUL; + if (lp->ll_blob != NULL) { + if (op != NULL && *op != '=') { + semsg(_(e_letwrong), op); + return; + } + if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, TV_CSTRING)) { + return; + } + + if (lp->ll_range && rettv->v_type == VAR_BLOB) { + if (lp->ll_empty2) { + lp->ll_n2 = tv_blob_len(lp->ll_blob) - 1; + } + + if (lp->ll_n2 - lp->ll_n1 + 1 != tv_blob_len(rettv->vval.v_blob)) { + emsg(_("E972: Blob value does not have the right number of bytes")); + return; + } + if (lp->ll_empty2) { + lp->ll_n2 = tv_blob_len(lp->ll_blob); + } + + for (int il = (int)lp->ll_n1, ir = 0; il <= (int)lp->ll_n2; il++) { + tv_blob_set(lp->ll_blob, il, tv_blob_get(rettv->vval.v_blob, ir++)); + } + } else { + bool error = false; + const char val = (char)tv_get_number_chk(rettv, &error); + if (!error) { + garray_T *const gap = &lp->ll_blob->bv_ga; + + // Allow for appending a byte. Setting a byte beyond + // the end is an error otherwise. + if (lp->ll_n1 < gap->ga_len || lp->ll_n1 == gap->ga_len) { + ga_grow(&lp->ll_blob->bv_ga, 1); + tv_blob_set(lp->ll_blob, (int)lp->ll_n1, (char_u)val); + if (lp->ll_n1 == gap->ga_len) { + gap->ga_len++; + } + } + // error for invalid range was already given in get_lval() + } + } + } else if (op != NULL && *op != '=') { + typval_T tv; + + if (is_const) { + emsg(_(e_cannot_mod)); + *endp = (char)cc; + return; + } + + // handle +=, -=, *=, /=, %= and .= + di = NULL; + if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), + &tv, &di, true, false) == OK) { + if ((di == NULL + || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) + && !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING))) + && eexe_mod_op(&tv, rettv, op) == OK) { + set_var(lp->ll_name, lp->ll_name_len, &tv, false); + } + tv_clear(&tv); + } + } else { + set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const); + } + *endp = (char)cc; + } else if (var_check_lock(lp->ll_newkey == NULL + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, TV_CSTRING)) { + // Skip + } else if (lp->ll_range) { + listitem_T *ll_li = lp->ll_li; + int ll_n1 = (int)lp->ll_n1; + + if (is_const) { + emsg(_("E996: Cannot lock a range")); + return; + } + + // Check whether any of the list items is locked + for (ri = tv_list_first(rettv->vval.v_list); + ri != NULL && ll_li != NULL;) { + if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, + TV_CSTRING)) { + return; + } + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { + break; + } + ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); + ll_n1++; + } + + /* + * Assign the List values to the list items. + */ + for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) { + if (op != NULL && *op != '=') { + eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); + } else { + tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); + tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); + } + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { + break; + } + assert(lp->ll_li != NULL); + if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { + // Need to add an empty item. + tv_list_append_number(lp->ll_list, 0); + // ll_li may have become invalid after append, don’t use it. + lp->ll_li = tv_list_last(lp->ll_list); // Valid again. + } else { + lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + } + lp->ll_n1++; + } + if (ri != NULL) { + emsg(_("E710: List value has more items than target")); + } else if (lp->ll_empty2 + ? (lp->ll_li != NULL + && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) + : lp->ll_n1 != lp->ll_n2) { + emsg(_("E711: List value has not enough items")); + } + } else { + typval_T oldtv = TV_INITIAL_VALUE; + dict_T *dict = lp->ll_dict; + bool watched = tv_dict_is_watched(dict); + + if (is_const) { + emsg(_("E996: Cannot lock a list or dict")); + return; + } + + // Assign to a List or Dictionary item. + if (lp->ll_newkey != NULL) { + if (op != NULL && *op != '=') { + semsg(_(e_letwrong), op); + return; + } + + // Need to add an item to the Dictionary. + di = tv_dict_item_alloc((const char *)lp->ll_newkey); + if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { + xfree(di); + return; + } + lp->ll_tv = &di->di_tv; + } else { + if (watched) { + tv_copy(lp->ll_tv, &oldtv); + } + + if (op != NULL && *op != '=') { + eexe_mod_op(lp->ll_tv, rettv, op); + goto notify; + } else { + tv_clear(lp->ll_tv); + } + } + + // Assign the value to the variable or list item. + if (copy) { + tv_copy(rettv, lp->ll_tv); + } else { + *lp->ll_tv = *rettv; + lp->ll_tv->v_lock = VAR_UNLOCKED; + tv_init(rettv); + } + +notify: + if (watched) { + if (oldtv.v_type == VAR_UNKNOWN) { + assert(lp->ll_newkey != NULL); + tv_dict_watcher_notify(dict, lp->ll_newkey, lp->ll_tv, NULL); + } else { + dictitem_T *di_ = lp->ll_di; + assert(di_->di_key != NULL); + tv_dict_watcher_notify(dict, (char *)di_->di_key, lp->ll_tv, &oldtv); + tv_clear(&oldtv); + } + } + } +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Evaluate the expression used in a ":for var in expr" command. +/// "arg" points to "var". +/// +/// @param[out] *errp set to TRUE for an error, FALSE otherwise; +/// +/// @return a pointer that holds the info. Null when there is an error. +void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) +{ + forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); + const char *expr; + typval_T tv; + list_T *l; + + *errp = true; // Default: there is an error. + + expr = skip_var_list((char *)arg, &fi->fi_varcount, &fi->fi_semicolon); + if (expr == NULL) { + return fi; + } + + expr = skipwhite(expr); + if (expr[0] != 'i' || expr[1] != 'n' || !ascii_iswhite(expr[2])) { + emsg(_("E690: Missing \"in\" after :for")); + return fi; + } + + if (skip) { + ++emsg_skip; + } + if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { + *errp = false; + if (!skip) { + if (tv.v_type == VAR_LIST) { + l = tv.vval.v_list; + if (l == NULL) { + // a null list is like an empty list: do nothing + tv_clear(&tv); + } else { + // No need to increment the refcount, it's already set for + // the list being used in "tv". + fi->fi_list = l; + tv_list_watch_add(l, &fi->fi_lw); + fi->fi_lw.lw_item = tv_list_first(l); + } + } else if (tv.v_type == VAR_BLOB) { + fi->fi_bi = 0; + if (tv.vval.v_blob != NULL) { + typval_T btv; + + // Make a copy, so that the iteration still works when the + // blob is changed. + tv_blob_copy(&tv, &btv); + fi->fi_blob = btv.vval.v_blob; + } + tv_clear(&tv); + } else if (tv.v_type == VAR_STRING) { + fi->fi_byte_idx = 0; + fi->fi_string = tv.vval.v_string; + tv.vval.v_string = NULL; + if (fi->fi_string == NULL) { + fi->fi_string = xstrdup(""); + } + } else { + emsg(_(e_string_list_or_blob_required)); + tv_clear(&tv); + } + } + } + if (skip) { + --emsg_skip; + } + + return fi; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Use the first item in a ":for" list. Advance to the next. +/// Assign the values to the variable (list). "arg" points to the first one. +/// +/// @return true when a valid item was found, false when at end of list or +/// something wrong. +bool next_for_item(void *fi_void, char *arg) +{ + forinfo_T *fi = (forinfo_T *)fi_void; + + if (fi->fi_blob != NULL) { + if (fi->fi_bi >= tv_blob_len(fi->fi_blob)) { + return false; + } + typval_T tv; + tv.v_type = VAR_NUMBER; + tv.v_lock = VAR_FIXED; + tv.vval.v_number = tv_blob_get(fi->fi_blob, fi->fi_bi); + fi->fi_bi++; + return ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + } + + if (fi->fi_string != NULL) { + const int len = utfc_ptr2len(fi->fi_string + fi->fi_byte_idx); + if (len == 0) { + return false; + } + typval_T tv; + tv.v_type = VAR_STRING; + tv.v_lock = VAR_FIXED; + tv.vval.v_string = xstrnsave(fi->fi_string + fi->fi_byte_idx, (size_t)len); + fi->fi_byte_idx += len; + const int result + = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + xfree(tv.vval.v_string); + return result; + } + + listitem_T *item = fi->fi_lw.lw_item; + if (item == NULL) { + return false; + } else { + fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); + return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, + fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK); + } +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Free the structure used to store info used by ":for". +void free_for_info(void *fi_void) +{ + forinfo_T *fi = (forinfo_T *)fi_void; + + if (fi == NULL) { + return; + } + if (fi->fi_list != NULL) { + tv_list_watch_remove(fi->fi_list, &fi->fi_lw); + tv_list_unref(fi->fi_list); + } else if (fi->fi_blob != NULL) { + tv_blob_unref(fi->fi_blob); + } else { + xfree(fi->fi_string); + } + xfree(fi); +} + +void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) + FUNC_ATTR_NONNULL_ALL +{ + int got_eq = FALSE; + int c; + char *p; + + if (cmdidx == CMD_let || cmdidx == CMD_const) { + xp->xp_context = EXPAND_USER_VARS; + if (strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#") == NULL) { + // ":let var1 var2 ...": find last space. + for (p = arg + STRLEN(arg); p >= arg;) { + xp->xp_pattern = p; + MB_PTR_BACK(arg, p); + if (ascii_iswhite(*p)) { + break; + } + } + return; + } + } else { + xp->xp_context = cmdidx == CMD_call ? EXPAND_FUNCTIONS + : EXPAND_EXPRESSION; + } + while ((xp->xp_pattern = strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#")) != NULL) { + c = (uint8_t)(*xp->xp_pattern); + if (c == '&') { + c = (uint8_t)xp->xp_pattern[1]; + if (c == '&') { + ++xp->xp_pattern; + xp->xp_context = cmdidx != CMD_let || got_eq + ? EXPAND_EXPRESSION : EXPAND_NOTHING; + } else if (c != ' ') { + xp->xp_context = EXPAND_SETTINGS; + if ((c == 'l' || c == 'g') && xp->xp_pattern[2] == ':') { + xp->xp_pattern += 2; + } + } + } else if (c == '$') { + // environment variable + xp->xp_context = EXPAND_ENV_VARS; + } else if (c == '=') { + got_eq = TRUE; + xp->xp_context = EXPAND_EXPRESSION; + } else if (c == '#' + && xp->xp_context == EXPAND_EXPRESSION) { + // Autoload function/variable contains '#' + break; + } else if ((c == '<' || c == '#') + && xp->xp_context == EXPAND_FUNCTIONS + && vim_strchr(xp->xp_pattern, '(') == NULL) { + // Function name can start with "<SNR>" and contain '#'. + break; + } else if (cmdidx != CMD_let || got_eq) { + if (c == '"') { // string + while ((c = (uint8_t)(*++xp->xp_pattern)) != NUL && c != '"') { + if (c == '\\' && xp->xp_pattern[1] != NUL) { + xp->xp_pattern++; + } + } + xp->xp_context = EXPAND_NOTHING; + } else if (c == '\'') { // literal string + // Trick: '' is like stopping and starting a literal string. + while ((c = (uint8_t)(*++xp->xp_pattern)) != NUL && c != '\'') {} + xp->xp_context = EXPAND_NOTHING; + } else if (c == '|') { + if (xp->xp_pattern[1] == '|') { + ++xp->xp_pattern; + xp->xp_context = EXPAND_EXPRESSION; + } else { + xp->xp_context = EXPAND_COMMANDS; + } + } else { + xp->xp_context = EXPAND_EXPRESSION; + } + } else { + // Doesn't look like something valid, expand as an expression + // anyway. + xp->xp_context = EXPAND_EXPRESSION; + } + arg = xp->xp_pattern; + if (*arg != NUL) { + while ((c = (char_u)(*++arg)) != NUL && (c == ' ' || c == '\t')) {} + } + } + + // ":exe one two" completes "two" + if ((cmdidx == CMD_execute + || cmdidx == CMD_echo + || cmdidx == CMD_echon + || cmdidx == CMD_echomsg) + && xp->xp_context == EXPAND_EXPRESSION) { + for (;;) { + char *const n = (char *)skiptowhite((char_u *)arg); + + if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) { + break; + } + arg = skipwhite(n); + } + } + + xp->xp_pattern = arg; +} + +/// Delete all "menutrans_" variables. +void del_menutrans_vars(void) +{ + hash_lock(&globvarht); + HASHTAB_ITER(&globvarht, hi, { + if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) { + delete_var(&globvarht, hi); + } + }); + hash_unlock(&globvarht); +} + +/* + * Local string buffer for the next two functions to store a variable name + * with its prefix. Allocated in cat_prefix_varname(), freed later in + * get_user_var_name(). + */ + +static char *varnamebuf = NULL; +static size_t varnamebuflen = 0; + +/// Function to concatenate a prefix and a variable name. +char *cat_prefix_varname(int prefix, const char *name) + FUNC_ATTR_NONNULL_ALL +{ + size_t len = STRLEN(name) + 3; + + if (len > varnamebuflen) { + xfree(varnamebuf); + len += 10; // some additional space + varnamebuf = xmalloc(len); + varnamebuflen = len; + } + *varnamebuf = (char)prefix; + varnamebuf[1] = ':'; + STRCPY(varnamebuf + 2, name); + return varnamebuf; +} + +/// Function given to ExpandGeneric() to obtain the list of user defined +/// (global/buffer/window/built-in) variable names. +char *get_user_var_name(expand_T *xp, int idx) +{ + static size_t gdone; + static size_t bdone; + static size_t wdone; + static size_t tdone; + static size_t vidx; + static hashitem_T *hi; + + if (idx == 0) { + gdone = bdone = wdone = vidx = 0; + tdone = 0; + } + + // Global variables + if (gdone < globvarht.ht_used) { + if (gdone++ == 0) { + hi = globvarht.ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + if (STRNCMP("g:", xp->xp_pattern, 2) == 0) { + return cat_prefix_varname('g', (char *)hi->hi_key); + } + return (char *)hi->hi_key; + } + + // b: variables + const hashtab_T *ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab; + if (bdone < ht->ht_used) { + if (bdone++ == 0) { + hi = ht->ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + return cat_prefix_varname('b', (char *)hi->hi_key); + } + + // w: variables + ht = &prevwin_curwin()->w_vars->dv_hashtab; + if (wdone < ht->ht_used) { + if (wdone++ == 0) { + hi = ht->ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + return cat_prefix_varname('w', (char *)hi->hi_key); + } + + // t: variables + ht = &curtab->tp_vars->dv_hashtab; + if (tdone < ht->ht_used) { + if (tdone++ == 0) { + hi = ht->ht_array; + } else { + ++hi; + } + while (HASHITEM_EMPTY(hi)) { + ++hi; + } + return cat_prefix_varname('t', (char *)hi->hi_key); + } + + // v: variables + if (vidx < ARRAY_SIZE(vimvars)) { + return cat_prefix_varname('v', vimvars[vidx++].vv_name); + } + + XFREE_CLEAR(varnamebuf); + varnamebuflen = 0; + return NULL; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Does not use 'cpo' and always uses 'magic'. +/// +/// @return TRUE if "pat" matches "text". +int pattern_match(char *pat, char *text, bool ic) +{ + int matches = 0; + regmatch_T regmatch; + + // avoid 'l' flag in 'cpoptions' + char *save_cpo = p_cpo; + p_cpo = ""; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) { + regmatch.rm_ic = ic; + matches = vim_regexec_nl(®match, (char_u *)text, (colnr_T)0); + vim_regfree(regmatch.regprog); + } + p_cpo = save_cpo; + return matches; +} + +/// Handle a name followed by "(". Both for just "name(arg)" and for +/// "expr->name(arg)". +/// +/// @param arg Points to "(", will be advanced +/// @param basetv "expr" for "expr->name(arg)" +/// +/// @return OK or FAIL. +static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv, + const bool evaluate, typval_T *const basetv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) +{ + char *s = name; + int len = name_len; + + if (!evaluate) { + check_vars((const char *)s, (size_t)len); + } + + // If "s" is the name of a variable of type VAR_FUNC + // use its contents. + partial_T *partial; + s = (char *)deref_func_name((const char *)s, &len, &partial, !evaluate); + + // Need to make a copy, in case evaluating the arguments makes + // the name invalid. + s = xmemdupz(s, (size_t)len); + + // Invoke the function. + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = partial; + funcexe.basetv = basetv; + int ret = get_func_tv((char_u *)s, len, rettv, (char_u **)arg, &funcexe); + + xfree(s); + + // 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 = (char *)tv_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. + if (evaluate && aborting()) { + if (ret == OK) { + tv_clear(rettv); + } + ret = FAIL; + } + return ret; +} + +// TODO(ZyX-I): move to eval/expressions + +/* + * The "evaluate" argument: When FALSE, the argument is only parsed but not + * executed. The function may return OK, but the rettv will be of type + * VAR_UNKNOWN. The function still returns FAIL for a syntax error. + */ + +/// Handle zero level expression. +/// This calls eval1() and handles error message and nextcmd. +/// Put the result in "rettv" when returning OK and "evaluate" is TRUE. +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. +int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) +{ + int ret; + char *p; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; + + p = skipwhite(arg); + ret = eval1(&p, rettv, evaluate); + if (ret == FAIL || !ends_excmd(*p)) { + if (ret != FAIL) { + tv_clear(rettv); + } + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { + semsg(_(e_invexpr2), arg); + } + ret = FAIL; + } + if (nextcmd != NULL) { + *nextcmd = (char *)check_nextcmd((char_u *)p); + } + + return ret; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle top level expression: +/// expr2 ? expr1 : expr1 +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. +int eval1(char **arg, typval_T *rettv, int evaluate) +{ + int result; + typval_T var2; + + /* + * Get the first variable. + */ + if (eval2(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + if ((*arg)[0] == '?') { + result = FALSE; + if (evaluate) { + bool error = false; + + if (tv_get_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { + return FAIL; + } + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 1); + if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive! + return FAIL; + } + + /* + * Check for the ":". + */ + if ((*arg)[0] != ':') { + emsg(_("E109: Missing ':' after '?'")); + if (evaluate && result) { + tv_clear(rettv); + } + return FAIL; + } + + /* + * Get the third variable. + */ + *arg = skipwhite(*arg + 1); + if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive! + if (evaluate && result) { + tv_clear(rettv); + } + return FAIL; + } + if (evaluate && !result) { + *rettv = var2; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle first level expression: +/// expr2 || expr2 || expr2 logical OR +/// +/// "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 eval2(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + long result; + int first; + bool error = false; + + /* + * Get the first variable. + */ + if (eval3(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + /* + * Repeat until there is no following "||". + */ + first = TRUE; + result = FALSE; + while ((*arg)[0] == '|' && (*arg)[1] == '|') { + if (evaluate && first) { + if (tv_get_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { + return FAIL; + } + first = false; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 2); + if (eval3(arg, &var2, evaluate && !result) == FAIL) { + return FAIL; + } + + /* + * Compute the result. + */ + if (evaluate && !result) { + if (tv_get_number_chk(&var2, &error) != 0) { + result = true; + } + tv_clear(&var2); + if (error) { + return FAIL; + } + } + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = result; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle second level expression: +/// expr3 && expr3 && expr3 logical AND +/// +/// @param 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 eval3(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + long result; + int first; + bool error = false; + + /* + * Get the first variable. + */ + if (eval4(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + /* + * Repeat until there is no following "&&". + */ + first = TRUE; + result = TRUE; + while ((*arg)[0] == '&' && (*arg)[1] == '&') { + if (evaluate && first) { + if (tv_get_number_chk(rettv, &error) == 0) { + result = false; + } + tv_clear(rettv); + if (error) { + return FAIL; + } + first = false; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 2); + if (eval4(arg, &var2, evaluate && result) == FAIL) { + return FAIL; + } + + /* + * Compute the result. + */ + if (evaluate && result) { + if (tv_get_number_chk(&var2, &error) == 0) { + result = false; + } + tv_clear(&var2); + if (error) { + return FAIL; + } + } + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = result; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle third level expression: +/// var1 == var2 +/// var1 =~ var2 +/// var1 != var2 +/// var1 !~ var2 +/// var1 > var2 +/// var1 >= var2 +/// var1 < var2 +/// var1 <= var2 +/// var1 is var2 +/// var1 isnot var2 +/// +/// "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 eval4(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + char *p; + exprtype_T type = EXPR_UNKNOWN; + int len = 2; + bool ic; + + /* + * Get the first variable. + */ + if (eval5(arg, rettv, evaluate) == FAIL) { + return FAIL; + } + + p = *arg; + switch (p[0]) { + case '=': + if (p[1] == '=') { + type = EXPR_EQUAL; + } else if (p[1] == '~') { + type = EXPR_MATCH; + } + break; + case '!': + if (p[1] == '=') { + type = EXPR_NEQUAL; + } else if (p[1] == '~') { + type = EXPR_NOMATCH; + } + break; + case '>': + if (p[1] != '=') { + type = EXPR_GREATER; + len = 1; + } else { + type = EXPR_GEQUAL; + } + break; + case '<': + if (p[1] != '=') { + type = EXPR_SMALLER; + len = 1; + } else { + type = EXPR_SEQUAL; + } + break; + case 'i': + if (p[1] == 's') { + if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') { + len = 5; + } + if (!isalnum(p[len]) && p[len] != '_') { + type = len == 2 ? EXPR_IS : EXPR_ISNOT; + } + } + break; + } + + /* + * If there is a comparative operator, use it. + */ + if (type != EXPR_UNKNOWN) { + // extra question mark appended: ignore case + if (p[len] == '?') { + ic = true; + len++; + } else if (p[len] == '#') { // extra '#' appended: match case + ic = false; + len++; + } else { // nothing appended: use 'ignorecase' + ic = p_ic; + } + + // Get the second variable. + *arg = skipwhite(p + len); + if (eval5(arg, &var2, evaluate) == FAIL) { + tv_clear(rettv); + return FAIL; + } + if (evaluate) { + const int ret = typval_compare(rettv, &var2, type, ic); + + tv_clear(&var2); + return ret; + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle fourth level expression: +/// + number addition +/// - number subtraction +/// . string concatenation +/// .. string concatenation +/// +/// @param 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 eval5(char **arg, typval_T *rettv, int evaluate) +{ + typval_T var2; + typval_T var3; + int op; + varnumber_T n1, n2; + float_T f1 = 0, f2 = 0; + char *p; + + /* + * Get the first variable. + */ + if (eval6(arg, rettv, evaluate, FALSE) == FAIL) { + return FAIL; + } + + /* + * Repeat computing, until no '+', '-' or '.' is following. + */ + for (;;) { + op = (char_u)(**arg); + if (op != '+' && op != '-' && op != '.') { + break; + } + + if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) + && (op == '.' || rettv->v_type != VAR_FLOAT)) { + // For "list + ...", an illegal use of the first operand as + // a number cannot be determined before evaluating the 2nd + // operand: if this is also a list, all is ok. + // For "something . ...", "something - ..." or "non-list + ...", + // we know that the first operand needs to be a string or number + // without evaluating the 2nd operand. So check before to avoid + // side effects after an error. + if (evaluate && !tv_check_str(rettv)) { + tv_clear(rettv); + return FAIL; + } + } + + /* + * Get the second variable. + */ + if (op == '.' && *(*arg + 1) == '.') { // ..string concatenation + (*arg)++; + } + *arg = skipwhite(*arg + 1); + if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { + tv_clear(rettv); + return FAIL; + } + + if (evaluate) { + /* + * Compute the result. + */ + if (op == '.') { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = tv_get_string_buf_chk(&var2, buf2); + if (s2 == NULL) { // Type error? + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + p = (char *)concat_str((const char_u *)s1, (const char_u *)s2); + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; + } else if (op == '+' && rettv->v_type == VAR_BLOB + && var2.v_type == VAR_BLOB) { + const blob_T *const b1 = rettv->vval.v_blob; + const blob_T *const b2 = var2.vval.v_blob; + blob_T *const b = tv_blob_alloc(); + + for (int i = 0; i < tv_blob_len(b1); i++) { + ga_append(&b->bv_ga, (char)tv_blob_get(b1, i)); + } + for (int i = 0; i < tv_blob_len(b2); i++) { + ga_append(&b->bv_ga, (char)tv_blob_get(b2, i)); + } + + tv_clear(rettv); + tv_blob_set_ret(rettv, b); + } else if (op == '+' && rettv->v_type == VAR_LIST + && var2.v_type == VAR_LIST) { + // Concatenate Lists. + if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3) + == FAIL) { + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + tv_clear(rettv); + *rettv = var3; + } else { + bool error = false; + + if (rettv->v_type == VAR_FLOAT) { + f1 = rettv->vval.v_float; + n1 = 0; + } else { + n1 = tv_get_number_chk(rettv, &error); + if (error) { + // This can only happen for "list + non-list" or + // "blob + non-blob". For "non-list + ..." or + // "something - ...", we returned before evaluating the + // 2nd operand. + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + if (var2.v_type == VAR_FLOAT) { + f1 = (float_T)n1; + } + } + if (var2.v_type == VAR_FLOAT) { + f2 = var2.vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(&var2, &error); + if (error) { + tv_clear(rettv); + tv_clear(&var2); + return FAIL; + } + if (rettv->v_type == VAR_FLOAT) { + f2 = (float_T)n2; + } + } + tv_clear(rettv); + + // If there is a float on either side the result is a float. + if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { + if (op == '+') { + f1 = f1 + f2; + } else { + f1 = f1 - f2; + } + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f1; + } else { + if (op == '+') { + n1 = n1 + n2; + } else { + n1 = n1 - n2; + } + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = n1; + } + } + tv_clear(&var2); + } + } + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle fifth level expression: +/// - * number multiplication +/// - / number division +/// - % number modulo +/// +/// @param[in,out] arg Points to the first non-whitespace character of the +/// expression. Is advanced to the next non-whitespace +/// character after the recognized expression. +/// @param[out] rettv Location where result is saved. +/// @param[in] evaluate If not true, rettv is not populated. +/// @param[in] want_string True if "." is string_concatenation, otherwise +/// float +/// @return OK or FAIL. +static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) + FUNC_ATTR_NO_SANITIZE_UNDEFINED +{ + typval_T var2; + int op; + varnumber_T n1, n2; + bool use_float = false; + float_T f1 = 0, f2 = 0; + bool error = false; + + /* + * Get the first variable. + */ + if (eval7(arg, rettv, evaluate, want_string) == FAIL) { + return FAIL; + } + + /* + * Repeat computing, until no '*', '/' or '%' is following. + */ + for (;;) { + op = (char_u)(**arg); + if (op != '*' && op != '/' && op != '%') { + break; + } + + if (evaluate) { + if (rettv->v_type == VAR_FLOAT) { + f1 = rettv->vval.v_float; + use_float = true; + n1 = 0; + } else { + n1 = tv_get_number_chk(rettv, &error); + } + tv_clear(rettv); + if (error) { + return FAIL; + } + } else { + n1 = 0; + } + + /* + * Get the second variable. + */ + *arg = skipwhite(*arg + 1); + if (eval7(arg, &var2, evaluate, false) == FAIL) { + return FAIL; + } + + if (evaluate) { + if (var2.v_type == VAR_FLOAT) { + if (!use_float) { + f1 = (float_T)n1; + use_float = true; + } + f2 = var2.vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(&var2, &error); + tv_clear(&var2); + if (error) { + return FAIL; + } + if (use_float) { + f2 = (float_T)n2; + } + } + + /* + * Compute the result. + * When either side is a float the result is a float. + */ + if (use_float) { + if (op == '*') { + f1 = f1 * f2; + } else if (op == '/') { + // uncrustify:off + + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 ? ( +#ifdef NAN + f1 == 0 ? (float_T)NAN : +#endif + (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); + + // uncrustify:on + } else { + emsg(_("E804: Cannot use '%' with Float")); + return FAIL; + } + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f1; + } else { + if (op == '*') { + n1 = n1 * n2; + } else if (op == '/') { + n1 = num_divide(n1, n2); + } else { + n1 = num_modulus(n1, n2); + } + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = n1; + } + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Handle sixth level expression: +/// number number constant +/// 0zFFFFFFFF Blob 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 +/// #{key: val, key: val} Dictionary with literal keys +/// +/// 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 +/// trailing ->name() method call +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @param want_string after "." operator +/// +/// @return OK or FAIL. +static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) +{ + varnumber_T n; + int len; + char *s; + const char *start_leader, *end_leader; + int ret = OK; + char *alias; + + // Initialise variable so that tv_clear() can't mistake this for a + // string and free a string that isn't there. + rettv->v_type = VAR_UNKNOWN; + + // Skip '!', '-' and '+' characters. They are handled later. + start_leader = *arg; + while (**arg == '!' || **arg == '-' || **arg == '+') { + *arg = skipwhite(*arg + 1); + } + end_leader = *arg; + + switch (**arg) { + // Number constant. + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + char *p = skipdigits(*arg + 1); + 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. + if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) { + get_float = true; + p = skipdigits(p + 2); + if (*p == 'e' || *p == 'E') { + ++p; + if (*p == '-' || *p == '+') { + ++p; + } + if (!ascii_isdigit(*p)) { + get_float = false; + } else { + p = skipdigits(p + 1); + } + } + if (ASCII_ISALPHA(*p) || *p == '.') { + get_float = false; + } + } + if (get_float) { + float_T f; + + *arg += string2float(*arg, &f); + if (evaluate) { + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = f; + } + } else if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) { + blob_T *blob = NULL; + // Blob constant: 0z0123456789abcdef + if (evaluate) { + blob = tv_blob_alloc(); + } + char *bp; + for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) { + if (!ascii_isxdigit(bp[1])) { + if (blob != NULL) { + emsg(_("E973: Blob literal should have an even number of hex " + "characters")); + ga_clear(&blob->bv_ga); + XFREE_CLEAR(blob); + } + ret = FAIL; + break; + } + if (blob != NULL) { + ga_append(&blob->bv_ga, (char)((hex2nr(*bp) << 4) + hex2nr(*(bp + 1)))); + } + if (bp[2] == '.' && ascii_isxdigit(bp[3])) { + bp++; + } + } + if (blob != NULL) { + tv_blob_set_ret(rettv, blob); + } + *arg = bp; + } else { + // decimal, hex or octal number + vim_str2nr((char_u *)(*arg), NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + if (len == 0) { + semsg(_(e_invexpr2), *arg); + ret = FAIL; + break; + } + *arg += len; + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = n; + } + } + break; + } + + // String constant: "string". + case '"': + ret = get_string_tv(arg, rettv, evaluate); + break; + + // Literal string constant: 'str''ing'. + case '\'': + ret = get_lit_string_tv(arg, rettv, evaluate); + break; + + // List: [expr, expr] + case '[': + ret = get_list_tv(arg, rettv, evaluate); + break; + + // Dictionary: #{key: val, key: val} + case '#': + if ((*arg)[1] == '{') { + (*arg)++; + ret = dict_get_tv(arg, rettv, evaluate, true); + } else { + ret = NOTDONE; + } + break; + + // Lambda: {arg, arg -> expr} + // Dictionary: {'key': val, 'key': val} + case '{': + ret = get_lambda_tv((char_u **)arg, rettv, evaluate); + if (ret == NOTDONE) { + ret = dict_get_tv(arg, rettv, evaluate, false); + } + break; + + // Option value: &name + case '&': + ret = get_option_tv((const char **)arg, rettv, evaluate); + break; + // Environment variable: $VAR. + case '$': + ret = get_env_tv(arg, rettv, evaluate); + break; + + // Register contents: @r. + case '@': + ++*arg; + int regname = mb_cptr2char_adv((const char_u**) arg); + if (evaluate) { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_reg_contents(regname, kGRegExprSrc); + } + // if (**arg != NUL) { + // ++*arg; + // } + break; + + // nested expression: (expression). + case '(': + *arg = skipwhite(*arg + 1); + ret = eval1(arg, rettv, evaluate); // recursive! + if (**arg == ')') { + ++*arg; + } else if (ret == OK) { + emsg(_("E110: Missing ')'")); + tv_clear(rettv); + ret = FAIL; + } + break; + + default: + ret = NOTDONE; + break; + } + + if (ret == NOTDONE) { + // Must be a variable or function name. + // Can also be a curly-braces kind of name: {expr}. + s = *arg; + len = get_name_len((const char **)arg, &alias, evaluate, true); + if (alias != NULL) { + s = alias; + } + + if (len <= 0) { + ret = FAIL; + } else { + if (**arg == '(') { // recursive! + ret = eval_func(arg, s, len, rettv, evaluate, NULL); + } else if (evaluate) { + ret = get_var_tv((const char *)s, len, rettv, NULL, true, false); + } else { + check_vars((const char *)s, (size_t)len); + ret = OK; + } + } + xfree(alias); + } + + *arg = skipwhite(*arg); + + // Handle following '[', '(' and '.' for expr[expr], expr.name, + // expr(expr), expr->name(expr) + if (ret == OK) { + ret = handle_subscript((const char **)arg, rettv, evaluate, true, + (char *)start_leader, &end_leader); + } + + // Apply logical NOT and unary '-', from right to left, ignore '+'. + if (ret == OK && evaluate && end_leader > start_leader) { + ret = eval7_leader(rettv, (char *)start_leader, &end_leader); + } + return ret; +} + +/// Apply the leading "!" and "-" before an eval7 expression to "rettv". +/// Adjusts "end_leaderp" until it is at "start_leader". +/// +/// @return OK on success, FAIL on failure. +static int eval7_leader(typval_T *const rettv, const char *const start_leader, + const char **const end_leaderp) + FUNC_ATTR_NONNULL_ALL +{ + const char *end_leader = (char *)(*end_leaderp); + int ret = OK; + bool error = false; + varnumber_T val = 0; + float_T f = 0.0; + + if (rettv->v_type == VAR_FLOAT) { + f = rettv->vval.v_float; + } else { + val = tv_get_number_chk(rettv, &error); + } + if (error) { + tv_clear(rettv); + ret = FAIL; + } else { + while (end_leader > start_leader) { + end_leader--; + if (*end_leader == '!') { + if (rettv->v_type == VAR_FLOAT) { + f = !(bool)f; + } else { + val = !val; + } + } else if (*end_leader == '-') { + if (rettv->v_type == VAR_FLOAT) { + f = -f; + } else { + val = -val; + } + } + } + if (rettv->v_type == VAR_FLOAT) { + tv_clear(rettv); + rettv->vval.v_float = f; + } else { + tv_clear(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = val; + } + } + + *end_leaderp = end_leader; + return ret; +} + +/// Call the function referred to in "rettv". +/// @param lua_funcname If `rettv` refers to a v:lua function, this must point +/// to the name of the Lua function to call (after the +/// "v:lua." prefix). +/// @return OK on success, FAIL on failure. +static int call_func_rettv(char **const arg, typval_T *const rettv, const bool evaluate, + dict_T *const selfdict, typval_T *const basetv, + const char *const lua_funcname) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + partial_T *pt = NULL; + typval_T functv; + const char *funcname; + bool is_lua = false; + + // need to copy the funcref so that we can clear rettv + if (evaluate) { + functv = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Invoke the function. Recursive! + if (functv.v_type == VAR_PARTIAL) { + pt = functv.vval.v_partial; + is_lua = is_luafunc(pt); + funcname = is_lua ? lua_funcname : partial_name(pt); + } else { + funcname = functv.vval.v_string; + } + } else { + funcname = ""; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = pt; + funcexe.selfdict = selfdict; + funcexe.basetv = basetv; + const int ret = get_func_tv((char_u *)funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, + (char_u **)arg, &funcexe); + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } + + return ret; +} + +/// Evaluate "->method()". +/// +/// @param verbose if true, give error messages. +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. +/// +/// @note "*arg" is advanced to after the ')'. +static int eval_lambda(char **const arg, typval_T *const rettv, const bool evaluate, + const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + int ret = get_lambda_tv((char_u **)arg, rettv, evaluate); + if (ret != OK) { + return FAIL; + } else if (**arg != '(') { + if (verbose) { + if (*skipwhite(*arg) == '(') { + emsg(_(e_nowhitespace)); + } else { + semsg(_(e_missingparen), "lambda"); + } + } + tv_clear(rettv); + ret = FAIL; + } else { + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); + } + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + + return ret; +} + +/// Evaluate "->method()" or "->v:lua.method()". +/// +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. "*arg" is advanced to after the ')'. +static int eval_method(char **const arg, typval_T *const rettv, const bool evaluate, + const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Locate the method name. + int len; + char *name = *arg; + char *lua_funcname = NULL; + if (STRNCMP(name, "v:lua.", 6) == 0) { + lua_funcname = name + 6; + *arg = (char *)skip_luafunc_name((const char *)lua_funcname); + *arg = skipwhite(*arg); // to detect trailing whitespace later + len = (int)(*arg - lua_funcname); + } else { + char *alias; + len = get_name_len((const char **)arg, &alias, evaluate, true); + if (alias != NULL) { + name = alias; + } + } + + int ret; + if (len <= 0) { + if (verbose) { + if (lua_funcname == NULL) { + emsg(_("E260: Missing name after ->")); + } else { + semsg(_(e_invexpr2), name); + } + } + ret = FAIL; + } else { + if (**arg != '(') { + if (verbose) { + semsg(_(e_missingparen), name); + } + ret = FAIL; + } else if (ascii_iswhite((*arg)[-1])) { + if (verbose) { + emsg(_(e_nowhitespace)); + } + ret = FAIL; + } else if (lua_funcname != NULL) { + if (evaluate) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; + } + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); + } else { + ret = eval_func(arg, name, len, rettv, evaluate, &base); + } + } + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + + return ret; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". +/// "*arg" points to the '[' or '.'. +/// +/// @param verbose give error messages +/// +/// @returns FAIL or OK. "*arg" is advanced to after the ']'. +static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) +{ + bool empty1 = false; + bool empty2 = false; + long n1, n2 = 0; + ptrdiff_t len = -1; + int range = false; + char *key = NULL; + + switch (rettv->v_type) { + case VAR_FUNC: + case VAR_PARTIAL: + if (verbose) { + emsg(_("E695: Cannot index a Funcref")); + } + return FAIL; + case VAR_FLOAT: + if (verbose) { + emsg(_(e_float_as_string)); + } + return FAIL; + case VAR_BOOL: + 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: + case VAR_BLOB: + break; + } + + typval_T var1 = TV_INITIAL_VALUE; + typval_T var2 = TV_INITIAL_VALUE; + if (**arg == '.') { + /* + * dict.name + */ + key = *arg + 1; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {} + if (len == 0) { + return FAIL; + } + *arg = skipwhite(key + len); + } else { + /* + * something[idx] + * + * Get the (first) variable from inside the []. + */ + *arg = skipwhite(*arg + 1); + if (**arg == ':') { + empty1 = true; + } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! + return FAIL; + } else if (evaluate && !tv_check_str(&var1)) { + // Not a number or string. + tv_clear(&var1); + return FAIL; + } + + /* + * Get the second variable from inside the [:]. + */ + if (**arg == ':') { + range = true; + *arg = skipwhite(*arg + 1); + if (**arg == ']') { + empty2 = true; + } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } + return FAIL; + } else if (evaluate && !tv_check_str(&var2)) { + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); + return FAIL; + } + } + + // Check for the ']'. + if (**arg != ']') { + if (verbose) { + emsg(_(e_missbrac)); + } + tv_clear(&var1); + if (range) { + tv_clear(&var2); + } + return FAIL; + } + *arg = skipwhite(*arg + 1); // skip the ']' + } + + if (evaluate) { + n1 = 0; + if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) { + n1 = tv_get_number(&var1); + tv_clear(&var1); + } + if (range) { + if (empty2) { + n2 = -1; + } else { + n2 = tv_get_number(&var2); + tv_clear(&var2); + } + } + + switch (rettv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + const char *const s = tv_get_string(rettv); + char *v; + len = (ptrdiff_t)strlen(s); + if (range) { + // The resulting variable is a substring. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + v = NULL; + } else { + v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1)); + } + } else { + // The resulting variable is a string of a single + // character. If the index is too big or negative the + // result is empty. + if (n1 >= len || n1 < 0) { + v = NULL; + } else { + v = xmemdupz(s + n1, 1); + } + } + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = v; + break; + } + case VAR_BLOB: + len = tv_blob_len(rettv->vval.v_blob); + if (range) { + // The resulting variable is a sub-blob. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + tv_clear(rettv); + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } else { + blob_T *const blob = tv_blob_alloc(); + ga_grow(&blob->bv_ga, (int)(n2 - n1 + 1)); + blob->bv_ga.ga_len = (int)(n2 - n1 + 1); + for (long i = n1; i <= n2; i++) { + tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i)); + } + tv_clear(rettv); + tv_blob_set_ret(rettv, blob); + } + } else { + // The resulting variable is a byte value. + // If the index is too big or negative that is an error. + if (n1 < 0) { + n1 = len + n1; + } + if (n1 < len && n1 >= 0) { + const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)n1); + tv_clear(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = v; + } else { + semsg(_(e_blobidx), (int64_t)n1); + } + } + break; + case VAR_LIST: + len = tv_list_len(rettv->vval.v_list); + if (n1 < 0) { + n1 = len + n1; + } + if (!empty1 && (n1 < 0 || n1 >= len)) { + // For a range we allow invalid values and return an empty + // list. A list index out of range is an error. + if (!range) { + if (verbose) { + semsg(_(e_listidx), (int64_t)n1); + } + return FAIL; + } + n1 = len; + } + if (range) { + list_T *l; + listitem_T *item; + + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { + n2 = -1; + } + l = tv_list_alloc(n2 - n1 + 1); + item = tv_list_find(rettv->vval.v_list, (int)n1); + while (n1++ <= n2) { + tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item); + } + tv_clear(rettv); + tv_list_set_ret(rettv, l); + } else { + tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, (int)n1)), &var1); + tv_clear(rettv); + *rettv = var1; + } + break; + case VAR_DICT: { + if (range) { + if (verbose) { + emsg(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } + return FAIL; + } + + if (len == -1) { + key = (char *)tv_get_string_chk(&var1); + if (key == NULL) { + tv_clear(&var1); + return FAIL; + } + } + + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, + (const char *)key, len); + + if (item == NULL && verbose) { + semsg(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } + if (item == NULL || tv_is_luafunc(&item->di_tv)) { + return FAIL; + } + + tv_copy(&item->di_tv, &var1); + tv_clear(rettv); + *rettv = var1; + break; + } + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_UNKNOWN: + break; // Not evaluating, skipping over subscript + } + } + + return OK; +} + +// TODO(ZyX-I): move to eval/executor + +/// Get an option value +/// +/// @param[in,out] arg Points to the '&' or '+' before the option name. Is +/// advanced to the character after the option name. +/// @param[out] rettv Location where result is saved. +/// @param[in] evaluate If not true, rettv is not populated. +/// +/// @return OK or FAIL. +int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate) + FUNC_ATTR_NONNULL_ARG(1) +{ + long numval; + char *stringval; + getoption_T opt_type; + bool working = (**arg == '+'); // has("+option") + int ret = OK; + int opt_flags; + + // Isolate the option name and find its value. + char *option_end = (char *)find_option_end(arg, &opt_flags); + if (option_end == NULL) { + if (rettv != NULL) { + semsg(_("E112: Option name missing: %s"), *arg); + } + return FAIL; + } + + if (!evaluate) { + *arg = option_end; + return OK; + } + + char c = *option_end; + *option_end = NUL; + opt_type = get_option_value(*arg, &numval, + rettv == NULL ? NULL : &stringval, opt_flags); + + if (opt_type == gov_unknown) { + if (rettv != NULL) { + semsg(_("E113: Unknown option: %s"), *arg); + } + ret = FAIL; + } else if (rettv != NULL) { + if (opt_type == gov_hidden_string) { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + } else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + } else if (opt_type == gov_bool || opt_type == gov_number) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = numval; + } else { // string option + rettv->v_type = VAR_STRING; + rettv->vval.v_string = stringval; + } + } else if (working && (opt_type == gov_hidden_bool + || opt_type == gov_hidden_number + || opt_type == gov_hidden_string)) { + ret = FAIL; + } + + *option_end = c; // put back for error messages + *arg = option_end; + + return ret; +} + +/// Allocate a variable for a string constant. +/// +/// @return OK or FAIL. +static int get_string_tv(char **arg, typval_T *rettv, int evaluate) +{ + char *p; + unsigned int extra = 0; + + /* + * Find the end of the string, skipping backslashed characters. + */ + for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { + if (*p == '\\' && p[1] != NUL) { + p++; + // A "\<x>" form occupies at least 4 characters, and produces up + // to 9 characters (6 for the char and 3 for a modifier): + // reserve space for 5 extra. + if (*p == '<') { + extra += 5; + } + } + } + + if (*p != '"') { + semsg(_("E114: Missing quote: %s"), *arg); + return FAIL; + } + + // If only parsing, set *arg and return here + if (!evaluate) { + *arg = p + 1; + return OK; + } + + /* + * Copy the string into allocated memory, handling backslashed + * characters. + */ + const int len = (int)(p - *arg + extra); + char *name = xmalloc((size_t)len); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = name; + + for (p = *arg + 1; *p != NUL && *p != '"';) { + if (*p == '\\') { + switch (*++p) { + case 'b': + *name++ = BS; ++p; break; + case 'e': + *name++ = ESC; ++p; break; + case 'f': + *name++ = FF; ++p; break; + case 'n': + *name++ = NL; ++p; break; + case 'r': + *name++ = CAR; ++p; break; + case 't': + *name++ = TAB; ++p; break; + + case 'X': // hex: "\x1", "\x12" + case 'x': + case 'u': // Unicode: "\u0023" + case 'U': + if (ascii_isxdigit(p[1])) { + int n, nr; + int c = toupper(*p); + + if (c == 'X') { + n = 2; + } else if (*p == 'u') { + n = 4; + } else { + n = 8; + } + nr = 0; + while (--n >= 0 && ascii_isxdigit(p[1])) { + ++p; + nr = (nr << 4) + hex2nr(*p); + } + p++; + // For "\u" store the number according to + // 'encoding'. + if (c != 'X') { + name += utf_char2bytes(nr, name); + } else { + *name++ = (char)nr; + } + } + break; + + // octal: "\1", "\12", "\123" + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + *name = (char)(*p++ - '0'); + if (*p >= '0' && *p <= '7') { + *name = (char)((*name << 3) + *p++ - '0'); + if (*p >= '0' && *p <= '7') { + *name = (char)((*name << 3) + *p++ - '0'); + } + } + ++name; + break; + + // Special key, e.g.: "\<C-W>" + case '<': { + int flags = FSK_KEYCODE | FSK_IN_STRING; + + if (p[1] != '*') { + flags |= FSK_SIMPLIFY; + } + extra = trans_special((const char_u **)&p, STRLEN(p), (char_u *)name, flags, false, NULL); + if (extra != 0) { + name += extra; + if (name >= rettv->vval.v_string + len) { + iemsg("get_string_tv() used more space than allocated"); + } + break; + } + } + FALLTHROUGH; + + default: + mb_copy_char((const char_u **)&p, (char_u **)&name); + break; + } + } else { + mb_copy_char((const char_u **)&p, (char_u **)&name); + } + } + *name = NUL; + if (*p != NUL) { // just in case + p++; + } + *arg = p; + + return OK; +} + +/// Allocate a variable for a 'str''ing' constant. +/// +/// @return OK or FAIL. +static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate) +{ + char *p; + char *str; + int reduce = 0; + + /* + * Find the end of the string, skipping ''. + */ + for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) { + if (*p == '\'') { + if (p[1] != '\'') { + break; + } + ++reduce; + ++p; + } + } + + if (*p != '\'') { + semsg(_("E115: Missing quote: %s"), *arg); + return FAIL; + } + + // If only parsing return after setting "*arg" + if (!evaluate) { + *arg = p + 1; + return OK; + } + + /* + * Copy the string into allocated memory, handling '' to ' reduction. + */ + str = xmalloc((size_t)((p - *arg) - reduce)); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = str; + + for (p = *arg + 1; *p != NUL;) { + if (*p == '\'') { + if (p[1] != '\'') { + break; + } + ++p; + } + mb_copy_char((const char_u **)&p, (char_u **)&str); + } + *str = NUL; + *arg = p + 1; + + return OK; +} + +/// @return the function name of the partial. +char *partial_name(partial_T *pt) + FUNC_ATTR_PURE +{ + if (pt->pt_name != NULL) { + return (char *)pt->pt_name; + } + return (char *)pt->pt_func->uf_name; +} + +// TODO(ZyX-I): Move to eval/typval.h + +static void partial_free(partial_T *pt) +{ + for (int i = 0; i < pt->pt_argc; i++) { + tv_clear(&pt->pt_argv[i]); + } + xfree(pt->pt_argv); + tv_dict_unref(pt->pt_dict); + if (pt->pt_name != NULL) { + func_unref(pt->pt_name); + xfree(pt->pt_name); + } else { + func_ptr_unref(pt->pt_func); + } + xfree(pt); +} + +// TODO(ZyX-I): Move to eval/typval.h + +/// Unreference a closure: decrement the reference count and free it when it +/// becomes zero. +void partial_unref(partial_T *pt) +{ + if (pt != NULL && --pt->pt_refcount <= 0) { + partial_free(pt); + } +} + +/// Allocate a variable for a List and fill it from "*arg". +/// +/// @return OK or FAIL. +static int get_list_tv(char **arg, typval_T *rettv, int evaluate) +{ + list_T *l = NULL; + + if (evaluate) { + l = tv_list_alloc(kListLenShouldKnow); + } + + *arg = skipwhite(*arg + 1); + while (**arg != ']' && **arg != NUL) { + typval_T tv; + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + goto failret; + } + if (evaluate) { + tv.v_lock = VAR_UNLOCKED; + tv_list_append_owned_tv(l, tv); + } + + if (**arg == ']') { + break; + } + if (**arg != ',') { + semsg(_("E696: Missing comma in List: %s"), *arg); + goto failret; + } + *arg = skipwhite(*arg + 1); + } + + if (**arg != ']') { + semsg(_("E697: Missing end of List ']': %s"), *arg); +failret: + if (evaluate) { + tv_list_free(l); + } + return FAIL; + } + + *arg = skipwhite(*arg + 1); + if (evaluate) { + tv_list_set_ret(rettv, l); + } + + return OK; +} + +/// @param ic ignore case +bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) +{ + char_u *s1, *s2; + dict_T *d1, *d2; + int a1, a2; + + // empty and NULL function name considered the same + s1 = (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial)); + if (s1 != NULL && *s1 == NUL) { + s1 = NULL; + } + s2 = (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial)); + if (s2 != NULL && *s2 == NUL) { + s2 = NULL; + } + if (s1 == NULL || s2 == NULL) { + if (s1 != s2) { + return false; + } + } else if (STRCMP(s1, s2) != 0) { + return false; + } + + // empty dict and NULL dict is different + d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict; + d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict; + if (d1 == NULL || d2 == NULL) { + if (d1 != d2) { + return false; + } + } else if (!tv_dict_equal(d1, d2, ic, true)) { + return false; + } + + // empty list and no list considered the same + a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc; + a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc; + if (a1 != a2) { + return false; + } + for (int i = 0; i < a1; i++) { + if (!tv_equal(tv1->vval.v_partial->pt_argv + i, + tv2->vval.v_partial->pt_argv + i, ic, true)) { + return false; + } + } + return true; +} + +/// 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. + * + * We use reference counts to be able to free most items right away when they + * are no longer used. But for composite items it's possible that it becomes + * unused while the reference count is > 0: When there is a recursive + * reference. Example: + * :let l = [1, 2, 3] + * :let d = {9: l} + * :let l[1] = d + * + * Since this is quite unusual we handle this with garbage collection: every + * once in a while find out which lists and dicts are not referenced from any + * variable. + * + * Here is a good reference text about garbage collection (refers to Python + * but it applies to all reference-counting mechanisms): + * http://python.ca/nas/python/gc/ + */ + +/// Do garbage collection for lists and dicts. +/// +/// @param testing true if called from test_garbagecollect_now(). +/// +/// @return true if some memory was freed. +bool garbage_collect(bool testing) +{ + bool abort = false; +#define ABORTING(func) abort = abort || func + + if (!testing) { + // Only do this once. + want_garbage_collect = false; + may_garbage_collect = false; + garbage_collect_at_exit = false; + } + + // We advance by two (COPYID_INC) because we add one for items referenced + // through previous_funccal. + const int copyID = get_copyID(); + + // 1. Go through all accessible variables and mark all lists and dicts + // with copyID. + + // Don't free variables in the previous_funccal list unless they are only + // referenced through previous_funccal. This must be first, because if + // the item is referenced elsewhere the funccal must not be freed. + ABORTING(set_ref_in_previous_funccal)(copyID); + + // script-local variables + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + ABORTING(set_ref_in_ht)(&SCRIPT_VARS(i), copyID, NULL); + } + + FOR_ALL_BUFFERS(buf) { + // buffer-local variables + ABORTING(set_ref_in_item)(&buf->b_bufvar.di_tv, copyID, NULL, NULL); + // buffer marks (ShaDa additional data) + ABORTING(set_ref_in_fmark)(buf->b_last_cursor, copyID); + ABORTING(set_ref_in_fmark)(buf->b_last_insert, copyID); + ABORTING(set_ref_in_fmark)(buf->b_last_change, copyID); + for (size_t i = 0; i < NMARKS; i++) { + ABORTING(set_ref_in_fmark)(buf->b_namedm[i], copyID); + } + // buffer change list (ShaDa additional data) + for (int i = 0; i < buf->b_changelistlen; i++) { + ABORTING(set_ref_in_fmark)(buf->b_changelist[i], copyID); + } + // buffer ShaDa additional data + ABORTING(set_ref_dict)(buf->additional_data, copyID); + + // buffer callback functions + set_ref_in_callback(&buf->b_prompt_callback, copyID, NULL, NULL); + set_ref_in_callback(&buf->b_prompt_interrupt, copyID, NULL, NULL); + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + // window-local variables + ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL); + // window jump list (ShaDa additional data) + for (int i = 0; i < wp->w_jumplistlen; i++) { + ABORTING(set_ref_in_fmark)(wp->w_jumplist[i].fmark, copyID); + } + } + if (aucmd_win != NULL) { + ABORTING(set_ref_in_item)(&aucmd_win->w_winvar.di_tv, copyID, NULL, NULL); + } + + // registers (ShaDa additional data) + { + iter_register_T reg_iter = ITER_REGISTER_NULL; + do { + yankreg_T reg; + char name = NUL; + bool is_unnamed = false; + reg_iter = op_global_reg_iter(reg_iter, &name, ®, &is_unnamed); + if (name != NUL) { + ABORTING(set_ref_dict)(reg.additional_data, copyID); + } + } while (reg_iter != ITER_REGISTER_NULL); + } + + // global marks (ShaDa additional data) + { + const void *mark_iter = NULL; + do { + xfmark_T fm; + char name = NUL; + mark_iter = mark_global_iter(mark_iter, &name, &fm); + if (name != NUL) { + ABORTING(set_ref_dict)(fm.fmark.additional_data, copyID); + } + } while (mark_iter != NULL); + } + + // tabpage-local variables + FOR_ALL_TABS(tp) { + ABORTING(set_ref_in_item)(&tp->tp_winvar.di_tv, copyID, NULL, NULL); + } + + // global variables + ABORTING(set_ref_in_ht)(&globvarht, copyID, NULL); + + // function-local variables + ABORTING(set_ref_in_call_stack)(copyID); + + // named functions (matters for closures) + ABORTING(set_ref_in_functions)(copyID); + + // Channels + { + Channel *data; + map_foreach_value(&channels, data, { + set_ref_in_callback_reader(&data->on_data, copyID, NULL, NULL); + set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); + set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); + }) + } + + // Timers + { + timer_T *timer; + map_foreach_value(&timers, timer, { + set_ref_in_callback(&timer->callback, copyID, NULL, NULL); + }) + } + + // function call arguments, if v:testing is set. + ABORTING(set_ref_in_func_args)(copyID); + + // v: vars + ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL); + + // history items (ShaDa additional elements) + if (p_hi) { + for (uint8_t i = 0; i < HIST_COUNT; i++) { + const void *iter = NULL; + do { + histentry_T hist; + iter = hist_iter(iter, i, false, &hist); + if (hist.hisstr != NULL) { + ABORTING(set_ref_list)(hist.additional_elements, copyID); + } + } while (iter != NULL); + } + } + + // previously used search/substitute patterns (ShaDa additional data) + { + SearchPattern pat; + get_search_pattern(&pat); + ABORTING(set_ref_dict)(pat.additional_data, copyID); + get_substitute_pattern(&pat); + ABORTING(set_ref_dict)(pat.additional_data, copyID); + } + + // previously used replacement string + { + SubReplacementString sub; + sub_get_replacement(&sub); + ABORTING(set_ref_list)(sub.additional_elements, copyID); + } + + ABORTING(set_ref_in_quickfix)(copyID); + + bool did_free = false; + if (!abort) { + // 2. Free lists and dictionaries that are not referenced. + did_free = free_unref_items(copyID); + + // 3. Check if any funccal can be freed now. + // This may call us back recursively. + did_free = free_unref_funccal(copyID, testing) || did_free; + } else if (p_verbose > 0) { + verb_msg(_("Not enough memory to set references, garbage collection aborted!")); + } +#undef ABORTING + return did_free; +} + +/// Free lists and dictionaries that are no longer referenced. +/// +/// @note This function may only be called from garbage_collect(). +/// +/// @param copyID Free lists/dictionaries that don't have this ID. +/// +/// @return true, if something was freed. +static int free_unref_items(int copyID) +{ + bool did_free = false; + + // Let all "free" functions know that we are here. This means no + // dictionaries, lists, or jobs are to be freed, because we will + // do that here. + tv_in_free_unref_items = true; + + // PASS 1: free the contents of the items. We don't free the items + // themselves yet, so that it is possible to decrement refcount counters. + + // Go through the list of dicts and free items without the copyID. + // Don't free dicts that are referenced internally. + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd->dv_used_next) { + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { + // Free the Dictionary and ordinary items it contains, but don't + // recurse into Lists and Dictionaries, they will be in the list + // of dicts or list of lists. + tv_dict_free_contents(dd); + did_free = true; + } + } + + // Go through the list of lists and free items without the copyID. + // But don't free a list that has a watcher (used in a for loop), these + // are not referenced anywhere. + for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { + if ((tv_list_copyid(ll) & COPYID_MASK) != (copyID & COPYID_MASK) + && !tv_list_has_watchers(ll)) { + // Free the List and ordinary items it contains, but don't recurse + // into Lists and Dictionaries, they will be in the list of dicts + // or list of lists. + tv_list_free_contents(ll); + did_free = true; + } + } + + // PASS 2: free the items themselves. + dict_T *dd_next; + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd_next) { + dd_next = dd->dv_used_next; + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { + tv_dict_free_dict(dd); + } + } + + list_T *ll_next; + for (list_T *ll = gc_first_list; ll != NULL; ll = ll_next) { + ll_next = ll->lv_used_next; + if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) + && !tv_list_has_watchers(ll)) { + // Free the List and ordinary items it contains, but don't recurse + // into Lists and Dictionaries, they will be in the list of dicts + // or list of lists. + tv_list_free_list(ll); + } + } + tv_in_free_unref_items = false; + return did_free; +} + +/// Mark all lists and dicts referenced through hashtab "ht" with "copyID". +/// +/// @param ht Hashtab content will be marked. +/// @param copyID New mark for lists and dicts. +/// @param list_stack Used to add lists to be marked. Can be NULL. +/// +/// @returns true if setting references failed somehow. +bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool abort = false; + ht_stack_T *ht_stack = NULL; + + hashtab_T *cur_ht = ht; + for (;;) { + if (!abort) { + // Mark each item in the hashtab. If the item contains a hashtab + // it is added to ht_stack, if it contains a list it is added to + // list_stack. + HASHTAB_ITER(cur_ht, hi, { + abort = abort || set_ref_in_item(&TV_DICT_HI2DI(hi)->di_tv, copyID, &ht_stack, list_stack); + }); + } + + if (ht_stack == NULL) { + break; + } + + // take an item from the stack + cur_ht = ht_stack->ht; + ht_stack_T *tempitem = ht_stack; + ht_stack = ht_stack->prev; + xfree(tempitem); + } + + return abort; +} + +/// Mark all lists and dicts referenced through list "l" with "copyID". +/// +/// @param l List content will be marked. +/// @param copyID New mark for lists and dicts. +/// @param ht_stack Used to add hashtabs to be marked. Can be NULL. +/// +/// @returns true if setting references failed somehow. +bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool abort = false; + list_stack_T *list_stack = NULL; + + list_T *cur_l = l; + for (;;) { + // Mark each item in the list. If the item contains a hashtab + // it is added to ht_stack, if it contains a list it is added to + // list_stack. + TV_LIST_ITER(cur_l, li, { + if (abort) { + break; + } + abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, + &list_stack); + }); + + if (list_stack == NULL) { + break; + } + + // take an item from the stack + cur_l = list_stack->list; + list_stack_T *tempitem = list_stack; + list_stack = list_stack->prev; + xfree(tempitem); + } + + return abort; +} + +/// Mark all lists and dicts referenced through typval "tv" with "copyID". +/// +/// @param tv Typval content will be marked. +/// @param copyID New mark for lists and dicts. +/// @param ht_stack Used to add hashtabs to be marked. Can be NULL. +/// @param list_stack Used to add lists to be marked. Can be NULL. +/// +/// @returns true if setting references failed somehow. +bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool abort = false; + + switch (tv->v_type) { + case VAR_DICT: { + dict_T *dd = tv->vval.v_dict; + if (dd != NULL && dd->dv_copyID != copyID) { + // Didn't see this dict yet. + dd->dv_copyID = copyID; + if (ht_stack == NULL) { + abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack); + } else { + ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T)); + newitem->ht = &dd->dv_hashtab; + newitem->prev = *ht_stack; + *ht_stack = newitem; + } + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + QUEUE_FOREACH(w, &dd->watchers, { + watcher = tv_dict_watcher_node_data(w); + set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); + }) + } + break; + } + + case VAR_LIST: { + list_T *ll = tv->vval.v_list; + if (ll != NULL && ll->lv_copyID != copyID) { + // Didn't see this list yet. + ll->lv_copyID = copyID; + if (list_stack == NULL) { + abort = set_ref_in_list(ll, copyID, ht_stack); + } else { + list_stack_T *const newitem = xmalloc(sizeof(list_stack_T)); + newitem->list = ll; + newitem->prev = *list_stack; + *list_stack = newitem; + } + } + break; + } + + case VAR_PARTIAL: { + partial_T *pt = tv->vval.v_partial; + + // A partial does not have a copyID, because it cannot contain itself. + if (pt != NULL) { + abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); + if (pt->pt_dict != NULL) { + typval_T dtv; + + dtv.v_type = VAR_DICT; + dtv.vval.v_dict = pt->pt_dict; + abort = abort || set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + + for (int i = 0; i < pt->pt_argc; i++) { + abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID, + ht_stack, list_stack); + } + } + break; + } + case VAR_FUNC: + abort = set_ref_in_func((char_u *)tv->vval.v_string, NULL, copyID); + break; + case VAR_UNKNOWN: + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_NUMBER: + case VAR_STRING: + case VAR_BLOB: + break; + } + return abort; +} + +/// Mark all lists and dicts referenced in given mark +/// +/// @return true if setting references failed somehow. +static inline bool set_ref_in_fmark(fmark_T fm, int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (fm.additional_data != NULL + && fm.additional_data->dv_copyID != copyID) { + fm.additional_data->dv_copyID = copyID; + return set_ref_in_ht(&fm.additional_data->dv_hashtab, copyID, NULL); + } + return false; +} + +/// Mark all lists and dicts referenced in given list and the list itself +/// +/// @return true if setting references failed somehow. +static inline bool set_ref_list(list_T *list, int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (list != NULL) { + typval_T tv = (typval_T) { + .v_type = VAR_LIST, + .vval = { .v_list = list } + }; + return set_ref_in_item(&tv, copyID, NULL, NULL); + } + return false; +} + +/// Mark all lists and dicts referenced in given dict and the dict itself +/// +/// @return true if setting references failed somehow. +static inline bool set_ref_dict(dict_T *dict, int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (dict != NULL) { + typval_T tv = (typval_T) { + .v_type = VAR_DICT, + .vval = { .v_dict = dict } + }; + return set_ref_in_item(&tv, copyID, NULL, NULL); + } + return false; +} + +/// Get the key for #{key: val} into "tv" and advance "arg". +/// +/// @return FAIL when there is no valid key. +static int get_literal_key(char **arg, typval_T *tv) + FUNC_ATTR_NONNULL_ALL +{ + char *p; + + if (!ASCII_ISALNUM(**arg) && **arg != '_' && **arg != '-') { + return FAIL; + } + for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {} + tv->v_type = VAR_STRING; + tv->vval.v_string = xstrnsave(*arg, (size_t)(p - *arg)); + + *arg = skipwhite(p); + return OK; +} + +/// Allocate a variable for a Dictionary and fill it from "*arg". +/// "literal" is true for #{key: val} +/// +/// @return OK or FAIL. Returns NOTDONE for {expr}. +static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal) +{ + dict_T *d = NULL; + typval_T tvkey; + typval_T tv; + char *key = NULL; + dictitem_T *item; + char *start = skipwhite(*arg + 1); + char buf[NUMBUFLEN]; + + /* + * First check if it's not a curly-braces thing: {expr}. + * Must do this without evaluating, otherwise a function may be called + * twice. Unfortunately this means we need to call eval1() twice for the + * first item. + * But {} is an empty Dictionary. + */ + if (*start != '}') { + if (eval1(&start, &tv, false) == FAIL) { // recursive! + return FAIL; + } + if (*skipwhite(start) == '}') { + return NOTDONE; + } + } + + if (evaluate) { + d = tv_dict_alloc(); + } + tvkey.v_type = VAR_UNKNOWN; + tv.v_type = VAR_UNKNOWN; + + *arg = skipwhite(*arg + 1); + while (**arg != '}' && **arg != NUL) { + if ((literal + ? get_literal_key(arg, &tvkey) + : eval1(arg, &tvkey, evaluate)) == FAIL) { // recursive! + goto failret; + } + if (**arg != ':') { + semsg(_("E720: Missing colon in Dictionary: %s"), *arg); + tv_clear(&tvkey); + goto failret; + } + if (evaluate) { + key = (char *)tv_get_string_buf_chk(&tvkey, buf); + if (key == NULL) { + // "key" is NULL when tv_get_string_buf_chk() gave an errmsg + tv_clear(&tvkey); + goto failret; + } + } + + *arg = skipwhite(*arg + 1); + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (evaluate) { + tv_clear(&tvkey); + } + goto failret; + } + if (evaluate) { + item = tv_dict_find(d, (const char *)key, -1); + if (item != NULL) { + semsg(_("E721: Duplicate key in Dictionary: \"%s\""), key); + tv_clear(&tvkey); + tv_clear(&tv); + goto failret; + } + item = tv_dict_item_alloc((const char *)key); + item->di_tv = tv; + item->di_tv.v_lock = VAR_UNLOCKED; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + } + } + tv_clear(&tvkey); + + if (**arg == '}') { + break; + } + if (**arg != ',') { + semsg(_("E722: Missing comma in Dictionary: %s"), *arg); + goto failret; + } + *arg = skipwhite(*arg + 1); + } + + if (**arg != '}') { + semsg(_("E723: Missing end of Dictionary '}': %s"), *arg); +failret: + if (d != NULL) { + tv_dict_free(d); + } + return FAIL; + } + + *arg = skipwhite(*arg + 1); + if (evaluate) { + tv_dict_set_ret(rettv, d); + } + + return OK; +} + +/// Convert the string to a floating point number +/// +/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to +/// make sure this always uses a decimal point. +/// +/// @param[in] text String to convert. +/// @param[out] ret_value Location where conversion result is saved. +/// +/// @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 = NULL; + + // MS-Windows does not deal with "inf" and "nan" properly + if (STRNICMP(text, "inf", 3) == 0) { + *ret_value = (float_T)INFINITY; + return 3; + } + if (STRNICMP(text, "-inf", 3) == 0) { + *ret_value = (float_T) - INFINITY; + return 4; + } + if (STRNICMP(text, "nan", 3) == 0) { + *ret_value = (float_T)NAN; + return 3; + } + *ret_value = strtod(text, &s); + return (size_t)(s - text); +} + +/// Get the value of an environment variable. +/// +/// If the environment variable was not set, silently assume it is empty. +/// +/// @param arg Points to the '$'. It is advanced to after the name. +/// +/// @return FAIL if the name is invalid. +static int get_env_tv(char **arg, typval_T *rettv, int evaluate) +{ + char *name; + char *string = NULL; + int len; + int cc; + + ++*arg; + name = *arg; + len = get_env_len((const char **)arg); + + if (evaluate) { + if (len == 0) { + return FAIL; // Invalid empty name. + } + cc = (char_u)name[len]; + name[len] = NUL; + // First try vim_getenv(), fast for normal environment vars. + string = vim_getenv(name); + if (string == NULL || *string == NUL) { + xfree(string); + + // Next try expanding things like $VIM and ${HOME}. + string = expand_env_save(name - 1); + if (string != NULL && *string == '$') { + XFREE_CLEAR(string); + } + } + name[len] = (char)cc; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = string; + } + + return OK; +} + +/// Get the argument list for a given window +void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv) +{ + tv_list_alloc_ret(rettv, argcount); + if (arglist != NULL) { + for (int idx = 0; idx < argcount; idx++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)alist_name(&arglist[idx]), -1); + } + } +} + +/// Add an assert error to v:errors. +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, tv_list_alloc(1)); + } + tv_list_append_string(vimvars[VV_ERRORS].vv_list, + (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); +} + +/// Find a window: When using a Window ID in any tab page, when using a number +/// in the current tab page. +win_T *find_win_by_nr_or_id(typval_T *vp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr >= LOWEST_WIN_ID) { + return win_id2wp((int)tv_get_number(vp)); + } + + return find_win_by_nr(vp, NULL); +} + +/// Implementation of map() and filter(). +void filter_map(typval_T *argvars, typval_T *rettv, int map) +{ + typval_T *expr; + list_T *l = NULL; + dictitem_T *di; + hashtab_T *ht; + hashitem_T *hi; + dict_T *d = NULL; + typval_T save_val; + typval_T save_key; + blob_T *b = NULL; + int rem = false; + int todo; + char *ermsg = map ? "map()" : "filter()"; + const char *const arg_errmsg = (map + ? N_("map() argument") + : N_("filter() argument")); + int save_did_emsg; + int idx = 0; + + if (argvars[0].v_type == VAR_BLOB) { + tv_copy(&argvars[0], rettv); + if ((b = argvars[0].vval.v_blob) == NULL) { + return; + } + } else if (argvars[0].v_type == VAR_LIST) { + tv_copy(&argvars[0], rettv); + if ((l = argvars[0].vval.v_list) == NULL + || (!map + && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { + return; + } + } else if (argvars[0].v_type == VAR_DICT) { + tv_copy(&argvars[0], rettv); + if ((d = argvars[0].vval.v_dict) == NULL + || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { + return; + } + } else { + semsg(_(e_listdictblobarg), ermsg); + return; + } + + expr = &argvars[1]; + // On type errors, the preceding call has already displayed an error + // message. Avoid a misleading error message for an empty string that + // was not passed as argument. + if (expr->v_type != VAR_UNKNOWN) { + prepare_vimvar(VV_VAL, &save_val); + + // We reset "did_emsg" to be able to detect whether an error + // occurred during evaluation of the expression. + save_did_emsg = did_emsg; + did_emsg = FALSE; + + prepare_vimvar(VV_KEY, &save_key); + if (argvars[0].v_type == VAR_DICT) { + vimvars[VV_KEY].vv_type = VAR_STRING; + + const VarLockStatus prev_lock = d->dv_lock; + if (map && d->dv_lock == VAR_UNLOCKED) { + d->dv_lock = VAR_LOCKED; + } + ht = &d->dv_hashtab; + hash_lock(ht); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + --todo; + + di = TV_DICT_HI2DI(hi); + if (map + && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { + break; + } + + vimvars[VV_KEY].vv_str = (char *)vim_strsave(di->di_key); + int r = filter_map_one(&di->di_tv, expr, map, &rem); + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { + break; + } + if (!map && rem) { + if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { + break; + } + tv_dict_item_remove(d, di); + } + } + } + hash_unlock(ht); + d->dv_lock = prev_lock; + } else if (argvars[0].v_type == VAR_BLOB) { + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + for (int i = 0; i < b->bv_ga.ga_len; i++) { + typval_T tv; + tv.v_type = VAR_NUMBER; + const varnumber_T val = tv_blob_get(b, i); + tv.vval.v_number = val; + vimvars[VV_KEY].vv_nr = idx; + if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) { + break; + } + if (tv.v_type != VAR_NUMBER) { + emsg(_(e_invalblob)); + return; + } + if (map) { + if (tv.vval.v_number != val) { + tv_blob_set(b, i, (char_u)tv.vval.v_number); + } + } else if (rem) { + char *const p = argvars[0].vval.v_blob->bv_ga.ga_data; + memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); + b->bv_ga.ga_len--; + i--; + } + idx++; + } + } else { + assert(argvars[0].v_type == VAR_LIST); + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + const VarLockStatus prev_lock = tv_list_locked(l); + if (map && tv_list_locked(l) == VAR_UNLOCKED) { + tv_list_set_lock(l, VAR_LOCKED); + } + for (listitem_T *li = tv_list_first(l); li != NULL;) { + if (map + && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { + break; + } + vimvars[VV_KEY].vv_nr = idx; + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL + || did_emsg) { + break; + } + if (!map && rem) { + li = tv_list_item_remove(l, li); + } else { + li = TV_LIST_ITEM_NEXT(l, li); + } + idx++; + } + tv_list_set_lock(l, prev_lock); + } + + restore_vimvar(VV_KEY, &save_key); + restore_vimvar(VV_VAL, &save_val); + + did_emsg |= save_did_emsg; + } +} + +static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T rettv; + typval_T argv[3]; + int retval = FAIL; + + tv_copy(tv, &vimvars[VV_VAL].vv_tv); + argv[0] = vimvars[VV_KEY].vv_tv; + argv[1] = vimvars[VV_VAL].vv_tv; + if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) { + goto theend; + } + if (map) { + // map(): replace the list item value. + tv_clear(tv); + rettv.v_lock = VAR_UNLOCKED; + *tv = rettv; + } else { + bool error = false; + + // filter(): when expr is zero remove the item + *remp = (tv_get_number_chk(&rettv, &error) == 0); + tv_clear(&rettv); + // On type error, nothing has been removed; return FAIL to stop the + // loop. The error message was given by tv_get_number_chk(). + if (error) { + goto theend; + } + } + retval = OK; +theend: + tv_clear(&vimvars[VV_VAL].vv_tv); + return retval; +} + +void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr fptr) +{ + char *s; + char *name; + bool use_string = false; + partial_T *arg_pt = NULL; + char *trans_name = NULL; + + if (argvars[0].v_type == VAR_FUNC) { + // function(MyFunc, [arg], dict) + s = argvars[0].vval.v_string; + } else if (argvars[0].v_type == VAR_PARTIAL + && argvars[0].vval.v_partial != NULL) { + // function(dict.MyFunc, [arg]) + arg_pt = argvars[0].vval.v_partial; + s = partial_name(arg_pt); + // TODO(bfredl): do the entire nlua_is_table_from_lua dance + } else { + // function('MyFunc', [arg], dict) + s = (char *)tv_get_string(&argvars[0]); + use_string = true; + } + + if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { + name = s; + trans_name = (char *)trans_function_name((char_u **)&name, false, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD + | TFN_NO_DEREF, NULL, NULL); + if (*name != NUL) { + s = NULL; + } + } + if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) + || (is_funcref && trans_name == NULL)) { + semsg(_(e_invarg2), (use_string + ? tv_get_string(&argvars[0]) + : (const char *)s)); + // Don't check an autoload name for existence here. + } else if (trans_name != NULL + && (is_funcref + ? find_func((char_u *)trans_name) == NULL + : !translated_function_exists((const char *)trans_name))) { + semsg(_("E700: Unknown function: %s"), s); + } else { + int dict_idx = 0; + int arg_idx = 0; + list_T *list = NULL; + if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "<SID>", 5) == 0) { + char sid_buf[25]; + int off = *s == 's' ? 2 : 5; + + // Expand s: and <SID> into <SNR>nr_, so that the function can + // also be called from another script. Using trans_function_name() + // would also work, but some plugins depend on the name being + // printable text. + snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", + (int64_t)current_sctx.sc_sid); + name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1); + STRCPY(name, sid_buf); + STRCAT(name, s + off); + } else { + name = xstrdup(s); + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[2].v_type != VAR_UNKNOWN) { + // function(name, [args], dict) + arg_idx = 1; + dict_idx = 2; + } else if (argvars[1].v_type == VAR_DICT) { + // function(name, dict) + dict_idx = 1; + } else { + // function(name, [args]) + arg_idx = 1; + } + if (dict_idx > 0) { + if (argvars[dict_idx].v_type != VAR_DICT) { + emsg(_("E922: expected a dict")); + xfree(name); + goto theend; + } + if (argvars[dict_idx].vval.v_dict == NULL) { + dict_idx = 0; + } + } + if (arg_idx > 0) { + if (argvars[arg_idx].v_type != VAR_LIST) { + emsg(_("E923: Second argument of function() must be " + "a list or a dict")); + xfree(name); + goto theend; + } + list = argvars[arg_idx].vval.v_list; + if (tv_list_len(list) == 0) { + arg_idx = 0; + } else if (tv_list_len(list) > MAX_FUNC_ARGS) { + emsg_funcname((char *)e_toomanyarg, (char_u *)s); + xfree(name); + goto theend; + } + } + } + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { + partial_T *const pt = xcalloc(1, sizeof(*pt)); + + // result is a VAR_PARTIAL + if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { + const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); + const int lv_len = tv_list_len(list); + + pt->pt_argc = arg_len + lv_len; + pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc); + int i = 0; + for (; i < arg_len; i++) { + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + } + if (lv_len > 0) { + TV_LIST_ITER(list, li, { + tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); + }); + } + } + + // For "function(dict.func, [], dict)" and "func" is a partial + // use "dict". That is backwards compatible. + if (dict_idx > 0) { + // The dict is bound explicitly, pt_auto is false + pt->pt_dict = argvars[dict_idx].vval.v_dict; + (pt->pt_dict->dv_refcount)++; + } else if (arg_pt != NULL) { + // If the dict was bound automatically the result is also + // bound automatically. + pt->pt_dict = arg_pt->pt_dict; + pt->pt_auto = arg_pt->pt_auto; + if (pt->pt_dict != NULL) { + (pt->pt_dict->dv_refcount)++; + } + } + + pt->pt_refcount = 1; + if (arg_pt != NULL && arg_pt->pt_func != NULL) { + pt->pt_func = arg_pt->pt_func; + func_ptr_ref(pt->pt_func); + xfree(name); + } else if (is_funcref) { + pt->pt_func = find_func((char_u *)trans_name); + func_ptr_ref(pt->pt_func); + xfree(name); + } else { + pt->pt_name = (char_u *)name; + func_ref((char_u *)name); + } + + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } else { + // result is a VAR_FUNC + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = name; + func_ref((char_u *)name); + } + } +theend: + xfree(trans_name); +} + +/// @return buffer options, variables and other attributes in a dictionary. +dict_T *get_buffer_info(buf_T *buf) +{ + dict_T *const dict = tv_dict_alloc(); + + tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); + tv_dict_add_str(dict, S_LEN("name"), + buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); + tv_dict_add_nr(dict, S_LEN("lnum"), + buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count); + tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); + tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); + tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); + tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf)); + tv_dict_add_nr(dict, S_LEN("hidden"), + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); + + // Get a reference to buffer variables + tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); + + // List of windows displaying this buffer + list_T *const windows = tv_list_alloc(kListLenMayKnow); + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + tv_list_append_number(windows, (varnumber_T)wp->handle); + } + } + tv_dict_add_list(dict, S_LEN("windows"), windows); + + if (buf->b_signlist != NULL) { + // List of signs placed in this buffer + tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); + } + + tv_dict_add_nr(dict, S_LEN("lastused"), buf->b_last_used); + + return dict; +} + +/// Get the line number from VimL object +/// +/// @note Unlike tv_get_lnum(), this one supports only "$" special string. +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string "$". +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. +/// +/// @return Line number or 0 in case of error. +linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv->v_type == VAR_STRING + && tv->vval.v_string != NULL + && tv->vval.v_string[0] == '$' + && buf != NULL) { + return buf->b_ml.ml_line_count; + } + return (linenr_T)tv_get_number_chk(tv, NULL); +} + +void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) +{ + if (what_arg->v_type == VAR_UNKNOWN) { + tv_list_alloc_ret(rettv, kListLenMayKnow); + if (is_qf || wp != NULL) { + (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list); + } + } else { + tv_dict_alloc_ret(rettv); + if (is_qf || wp != NULL) { + if (what_arg->v_type == VAR_DICT) { + dict_T *d = what_arg->vval.v_dict; + + if (d != NULL) { + qf_get_properties(wp, d, rettv->vval.v_dict); + } + } else { + emsg(_(e_dictreq)); + } + } + } +} + +/// @return information (variables, options, etc.) about a tab page +/// as a dictionary. +dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) +{ + dict_T *const dict = tv_dict_alloc(); + + tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); + + list_T *const l = tv_list_alloc(kListLenMayKnow); + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + tv_list_append_number(l, (varnumber_T)wp->handle); + } + tv_dict_add_list(dict, S_LEN("windows"), l); + + // Make a reference to tabpage variables + tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); + + return dict; +} + +/// @return information about a window as a dictionary. +dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) +{ + dict_T *const dict = tv_dict_alloc(); + + // make sure w_botline is valid + validate_botline(wp); + + tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); + tv_dict_add_nr(dict, S_LEN("winnr"), winnr); + tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); + tv_dict_add_nr(dict, S_LEN("height"), wp->w_height); + tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1); + tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline); + tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1); + tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height); + tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); + tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); + tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); + tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp)); + tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("loclist"), + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); + + // Add a reference to window variables + tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); + + return dict; +} + +/// Find window specified by "vp" in tabpage "tp". +/// +/// @param tp NULL for current tab page +win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr < 0) { + return NULL; + } + + if (nr == 0) { + return curwin; + } + + // This method accepts NULL as an alias for curtab. + if (tp == NULL) { + tp = curtab; + } + + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (nr >= LOWEST_WIN_ID) { + if (wp->handle == nr) { + return wp; + } + } else if (--nr <= 0) { + return wp; + } + } + return NULL; +} + +/// Find window specified by "wvp" in tabpage "tvp". +win_T *find_tabwin(typval_T *wvp, typval_T *tvp) +{ + win_T *wp = NULL; + tabpage_T *tp = NULL; + + if (wvp->v_type != VAR_UNKNOWN) { + if (tvp->v_type != VAR_UNKNOWN) { + long n = tv_get_number(tvp); + if (n >= 0) { + tp = find_tabpage((int)n); + } + } else { + tp = curtab; + } + + if (tp != NULL) { + wp = find_win_by_nr(wvp, tp); + } + } else { + wp = curwin; + } + + return wp; +} + +/// This function is used by f_input() and f_inputdialog() functions. The third +/// argument to f_input() specifies the type of completion to use at the +/// prompt. The third argument to f_inputdialog() specifies the value to return +/// when the user cancels the prompt. +void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, + const bool secret) + FUNC_ATTR_NONNULL_ALL +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + const char *prompt = ""; + const char *defstr = ""; + typval_T *cancelreturn = NULL; + typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; + const char *xp_name = NULL; + Callback input_callback = { .type = kCallbackNone }; + char prompt_buf[NUMBUFLEN]; + char defstr_buf[NUMBUFLEN]; + char cancelreturn_buf[NUMBUFLEN]; + char xp_name_buf[NUMBUFLEN]; + char def[1] = { 0 }; + if (argvars[0].v_type == VAR_DICT) { + if (argvars[1].v_type != VAR_UNKNOWN) { + emsg(_("E5050: {opts} must be the only argument")); + return; + } + dict_T *const dict = argvars[0].vval.v_dict; + prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); + if (prompt == NULL) { + return; + } + defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); + if (defstr == NULL) { + return; + } + dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); + if (cancelreturn_di != NULL) { + cancelreturn = &cancelreturn_di->di_tv; + } + xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), + xp_name_buf, def); + if (xp_name == NULL) { // error + return; + } + if (xp_name == def) { // default to NULL + xp_name = NULL; + } + if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { + return; + } + } else { + prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); + if (prompt == NULL) { + return; + } + if (argvars[1].v_type != VAR_UNKNOWN) { + defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); + if (defstr == NULL) { + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); + if (strarg2 == NULL) { + return; + } + if (inputdialog) { + cancelreturn_strarg2.v_type = VAR_STRING; + cancelreturn_strarg2.vval.v_string = (char *)strarg2; + cancelreturn = &cancelreturn_strarg2; + } else { + xp_name = strarg2; + } + } + } + } + + int xp_type = EXPAND_NOTHING; + char *xp_arg = NULL; + if (xp_name != NULL) { + // input() with a third argument: completion + const int xp_namelen = (int)strlen(xp_name); + + uint32_t argt = 0; + if (parse_compl_arg(xp_name, xp_namelen, &xp_type, + &argt, &xp_arg) == FAIL) { + return; + } + } + + const bool cmd_silent_save = cmd_silent; + + cmd_silent = false; // Want to see the prompt. + // Only the part of the message after the last NL is considered as + // prompt for the command line, unlsess cmdline is externalized + const char *p = prompt; + if (!ui_has(kUICmdline)) { + const char *lastnl = strrchr(prompt, '\n'); + if (lastnl != NULL) { + p = lastnl + 1; + msg_start(); + msg_clr_eos(); + msg_puts_attr_len(prompt, p - prompt, echo_attr); + msg_didout = false; + msg_starthere(); + } + } + cmdline_row = msg_row; + + stuffReadbuffSpec(defstr); + + const int save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, echo_attr, xp_type, xp_arg, + input_callback); + ex_normal_busy = save_ex_normal_busy; + callback_free(&input_callback); + + if (rettv->vval.v_string == NULL && cancelreturn != NULL) { + tv_copy(cancelreturn, rettv); + } + + xfree(xp_arg); + + // Since the user typed this, no need to wait for return. + need_wait_return = false; + msg_didout = false; + cmd_silent = cmd_silent_save; +} + +/// Builds a process argument vector from a VimL object (typval_T). +/// +/// @param[in] cmd_tv VimL object +/// @param[out] cmd Returns the command or executable name. +/// @param[out] executable Returns `false` if argv[0] is not executable. +/// +/// @return Result of `shell_build_argv()` if `cmd_tv` is a String. +/// Else, string values of `cmd_tv` copied to a (char **) list with +/// argv[0] resolved to full path ($PATHEXT-resolved on Windows). +char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) +{ + if (cmd_tv->v_type == VAR_STRING) { // String => "shell semantics". + const char *cmd_str = tv_get_string(cmd_tv); + if (cmd) { + *cmd = cmd_str; + } + return shell_build_argv(cmd_str, NULL); + } + + if (cmd_tv->v_type != VAR_LIST) { + semsg(_(e_invarg2), "expected String or List"); + return NULL; + } + + list_T *argl = cmd_tv->vval.v_list; + int argc = tv_list_len(argl); + if (!argc) { + emsg(_(e_invarg)); // List must have at least one item. + return NULL; + } + + const char *arg0 = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_first(argl))); + char *exe_resolved = NULL; + if (!arg0 || !os_can_exe(arg0, &exe_resolved, true)) { + if (arg0 && executable) { + char buf[IOSIZE]; + snprintf(buf, sizeof(buf), "'%s' is not executable", arg0); + semsg(_(e_invargNval), "cmd", buf); + *executable = false; + } + return NULL; + } + + if (cmd) { + *cmd = exe_resolved; + } + + // Build the argument vector + int i = 0; + char **argv = xcalloc((size_t)argc + 1, sizeof(char *)); + TV_LIST_ITER_CONST(argl, arg, { + const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg)); + if (!a) { + // Did emsg in tv_get_string_chk; just deallocate argv. + shell_free_argv(argv); + xfree(exe_resolved); + return NULL; + } + argv[i++] = xstrdup(a); + }); + // Replace argv[0] with absolute path. The only reason for this is to make + // $PATHEXT work on Windows with jobstart([…]). #9569 + xfree(argv[0]); + argv[0] = exe_resolved; + + return argv; +} + +void return_register(int regname, typval_T *rettv) +{ + char buf[2] = { (char)regname, 0 }; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = xstrdup(buf); +} + +void screenchar_adjust(ScreenGrid **grid, int *row, int *col) +{ + // TODO(bfredl): this is a hack for legacy tests which use screenchar() + // to check printed messages on the screen (but not floats etc + // as these are not legacy features). If the compositor is refactored to + // have its own buffer, this should just read from it instead. + msg_scroll_flush(); + + *grid = ui_comp_get_grid_at_coord(*row, *col); + + // Make `row` and `col` relative to the grid + *row -= (*grid)->comp_row; + *col -= (*grid)->comp_col; +} + +/// Set line or list of lines in buffer "buf". +void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T *lines, + typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(4, 5) +{ + linenr_T lnum = lnum_arg + (append ? 1 : 0); + const char *line = NULL; + list_T *l = NULL; + listitem_T *li = NULL; + long added = 0; + linenr_T append_lnum; + buf_T *curbuf_save = NULL; + win_T *curwin_save = NULL; + const bool is_curbuf = buf == curbuf; + const bool save_VIsual_active = VIsual_active; + + // When using the current buffer ml_mfp will be set if needed. Useful when + // setline() is used on startup. For other buffers the buffer must be + // loaded. + if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) { + rettv->vval.v_number = 1; // FAIL + return; + } + + if (!is_curbuf) { + VIsual_active = false; + curbuf_save = curbuf; + curwin_save = curwin; + curbuf = buf; + find_win_for_curbuf(); + } + + if (append) { + // appendbufline() uses the line number below which we insert + append_lnum = lnum - 1; + } else { + // setbufline() uses the line number above which we insert, we only + // append if it's below the last line + append_lnum = curbuf->b_ml.ml_line_count; + } + + if (lines->v_type == VAR_LIST) { + l = lines->vval.v_list; + li = tv_list_first(l); + } else { + line = tv_get_string_chk(lines); + } + + // Default result is zero == OK. + for (;;) { + if (lines->v_type == VAR_LIST) { + // List argument, get next string. + if (li == NULL) { + break; + } + line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + li = TV_LIST_ITEM_NEXT(l, li); + } + + rettv->vval.v_number = 1; // FAIL + if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) { + break; + } + + // When coming here from Insert mode, sync undo, so that this can be + // undone separately from what was previously inserted. + if (u_sync_once == 2) { + u_sync_once = 1; // notify that u_sync() was called + u_sync(true); + } + + if (!append && lnum <= curbuf->b_ml.ml_line_count) { + // Existing line, replace it. + int old_len = (int)STRLEN(ml_get(lnum)); + if (u_savesub(lnum) == OK + && ml_replace(lnum, (char *)line, true) == OK) { + inserted_bytes(lnum, 0, old_len, (int)STRLEN(line)); + if (is_curbuf && lnum == curwin->w_cursor.lnum) { + check_cursor_col(); + } + rettv->vval.v_number = 0; // OK + } + } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { + // append the line. + added++; + if (ml_append(lnum - 1, (char *)line, 0, false) == OK) { + rettv->vval.v_number = 0; // OK + } + } + + if (l == NULL) { // only one string argument + break; + } + lnum++; + } + + if (added > 0) { + appended_lines_mark(append_lnum, added); + + // Only adjust the cursor for buffers other than the current, unless it + // is the current window. For curbuf and other windows it has been done + // in mark_adjust_internal(). + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf + && (wp->w_buffer != curbuf || wp == curwin) + && wp->w_cursor.lnum > append_lnum) { + wp->w_cursor.lnum += (linenr_T)added; + } + } + check_cursor_col(); + update_topline(curwin); + } + + if (!is_curbuf) { + curbuf = curbuf_save; + curwin = curwin_save; + VIsual_active = save_VIsual_active; + } +} + +/// "stdpath()" helper for list results +void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + const void *iter = NULL; + list_T *const list = tv_list_alloc(kListLenShouldKnow); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = list; + tv_list_ref(list); + char *const dirs = stdpaths_get_xdg_var(xdg); + if (dirs == NULL) { + return; + } + do { + size_t dir_len; + const char *dir; + iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len); + if (dir != NULL && dir_len > 0) { + char *dir_with_nvim = xmemdupz(dir, dir_len); + dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true); + tv_list_append_string(list, dir_with_nvim, (ssize_t)strlen(dir_with_nvim)); + xfree(dir_with_nvim); + } + } while (iter != NULL); + xfree(dirs); +} + +static list_T *string_to_list(const char *str, size_t len, const bool keepempty) +{ + if (!keepempty && str[len - 1] == NL) { + len--; + } + list_T *const list = tv_list_alloc(kListLenMayKnow); + encode_list_write(list, str, len); + return list; +} + +/// os_system wrapper. Handles 'verbose', :profile, and v:shell_error. +void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist) +{ + proftime_T wait_time; + bool profiling = do_profiling == PROF_YES; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (check_secure()) { + return; + } + + // get input to the shell command (if any), and its length + ptrdiff_t input_len; + char *input = save_tv_as_string(&argvars[1], &input_len, false); + if (input_len < 0) { + assert(input == NULL); + return; + } + + // get shell command to execute + bool executable = true; + char **argv = tv_to_argv(&argvars[0], NULL, &executable); + if (!argv) { + if (!executable) { + set_vim_var_nr(VV_SHELL_ERROR, (long)-1); + } + xfree(input); + return; // Already did emsg. + } + + if (p_verbose > 3) { + char *cmdstr = shell_argv_to_str(argv); + verbose_enter_scroll(); + smsg(_("Executing command: \"%s\""), cmdstr); + msg_puts("\n\n"); + verbose_leave_scroll(); + xfree(cmdstr); + } + + if (profiling) { + prof_child_enter(&wait_time); + } + + // execute the command + size_t nread = 0; + char *res = NULL; + int status = os_system(argv, input, (size_t)input_len, &res, &nread); + + if (profiling) { + prof_child_exit(&wait_time); + } + + xfree(input); + + set_vim_var_nr(VV_SHELL_ERROR, (long)status); + + if (res == NULL) { + if (retlist) { + // return an empty list when there's no output + tv_list_alloc_ret(rettv, 0); + } else { + rettv->vval.v_string = xstrdup(""); + } + return; + } + + if (retlist) { + int keepempty = 0; + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { + keepempty = (int)tv_get_number(&argvars[2]); + } + rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); + tv_list_ref(rettv->vval.v_list); + rettv->v_type = VAR_LIST; + + xfree(res); + } else { + // res may contain several NULs before the final terminating one. + // Replace them with SOH (1) like in get_cmd_output() to avoid truncation. + memchrsub(res, NUL, 1, nread); +#ifdef USE_CRNL + // translate <CR><NL> into <NL> + char *d = res; + for (char *s = res; *s; ++s) { + if (s[0] == CAR && s[1] == NL) { + ++s; + } + + *d++ = *s; + } + + *d = NUL; +#endif + rettv->vval.v_string = res; + } +} + +bool callback_from_typval(Callback *const callback, typval_T *const arg) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + int r = OK; + + if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { + callback->data.partial = arg->vval.v_partial; + callback->data.partial->pt_refcount++; + callback->type = kCallbackPartial; + } else if (arg->v_type == VAR_STRING + && arg->vval.v_string != NULL + && ascii_isdigit(*arg->vval.v_string)) { + r = FAIL; + } else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { + char *name = arg->vval.v_string; + if (name == NULL) { + r = FAIL; + } else if (*name == NUL) { + callback->type = kCallbackNone; + callback->data.funcref = NULL; + } else { + func_ref((char_u *)name); + callback->data.funcref = xstrdup(name); + callback->type = kCallbackFuncref; + } + } else if (nlua_is_table_from_lua(arg)) { + // TODO(tjdvries): UnifiedCallback + char *name = (char *)nlua_register_table_as_callable(arg); + + if (name != NULL) { + callback->data.funcref = xstrdup(name); + callback->type = kCallbackFuncref; + } else { + r = FAIL; + } + } else if (arg->v_type == VAR_SPECIAL + || (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)) { + callback->type = kCallbackNone; + callback->data.funcref = NULL; + } else { + r = FAIL; + } + + if (r == FAIL) { + emsg(_("E921: Invalid callback argument")); + return false; + } + return true; +} + +bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, + typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL +{ + partial_T *partial; + char *name; + Array args = ARRAY_DICT_INIT; + Object rv; + switch (callback->type) { + case kCallbackFuncref: + name = callback->data.funcref; + partial = NULL; + break; + + case kCallbackPartial: + partial = callback->data.partial; + name = partial_name(partial); + break; + + case kCallbackLua: + rv = nlua_call_ref(callback->data.luaref, NULL, args, true, NULL); + switch (rv.type) { + case kObjectTypeBoolean: + return rv.data.boolean; + default: + return false; + } + + case kCallbackNone: + return false; + break; + + default: + abort(); + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); +} + +static bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + typval_T tv; + switch (callback->type) { + case kCallbackFuncref: + case kCallbackNone: + break; + + case kCallbackPartial: + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = callback->data.partial; + return set_ref_in_item(&tv, copyID, ht_stack, list_stack); + break; + + default: + abort(); + } + return false; +} + +static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + if (set_ref_in_callback(&reader->cb, copyID, ht_stack, list_stack)) { + return true; + } + + if (reader->self) { + typval_T tv; + tv.v_type = VAR_DICT; + tv.vval.v_dict = reader->self; + return set_ref_in_item(&tv, copyID, ht_stack, list_stack); + } + return false; +} + +timer_T *find_timer_by_nr(varnumber_T xx) +{ + return pmap_get(uint64_t)(&timers, (uint64_t)xx); +} + +void add_timer_info(typval_T *rettv, timer_T *timer) +{ + list_T *list = rettv->vval.v_list; + dict_T *dict = tv_dict_alloc(); + + tv_list_append_dict(list, dict); + tv_dict_add_nr(dict, S_LEN("id"), timer->timer_id); + tv_dict_add_nr(dict, S_LEN("time"), timer->timeout); + tv_dict_add_nr(dict, S_LEN("paused"), timer->paused); + + tv_dict_add_nr(dict, S_LEN("repeat"), + (timer->repeat_count < 0 ? -1 : timer->repeat_count)); + + dictitem_T *di = tv_dict_item_alloc("callback"); + if (tv_dict_add(dict, di) == FAIL) { + xfree(di); + return; + } + + callback_put(&timer->callback, &di->di_tv); +} + +void add_timer_info_all(typval_T *rettv) +{ + tv_list_alloc_ret(rettv, map_size(&timers)); + timer_T *timer; + map_foreach_value(&timers, timer, { + if (!timer->stopped) { + add_timer_info(rettv, timer); + } + }) +} + +/// invoked on the main loop +void timer_due_cb(TimeWatcher *tw, void *data) +{ + timer_T *timer = (timer_T *)data; + int save_did_emsg = did_emsg; + const int called_emsg_before = called_emsg; + const bool save_ex_pressedreturn = get_pressedreturn(); + + if (timer->stopped || timer->paused) { + return; + } + + timer->refcount++; + // if repeat was negative repeat forever + if (timer->repeat_count >= 0 && --timer->repeat_count == 0) { + timer_stop(timer); + } + + typval_T argv[2] = { TV_INITIAL_VALUE, TV_INITIAL_VALUE }; + argv[0].v_type = VAR_NUMBER; + argv[0].vval.v_number = timer->timer_id; + typval_T rettv = TV_INITIAL_VALUE; + + callback_call(&timer->callback, 1, argv, &rettv); + + // Handle error message + if (called_emsg > called_emsg_before && did_emsg) { + timer->emsg_count++; + if (current_exception != NULL) { + discard_current_exception(); + } + } + did_emsg = save_did_emsg; + set_pressedreturn(save_ex_pressedreturn); + + if (timer->emsg_count >= 3) { + timer_stop(timer); + } + + tv_clear(&rettv); + + if (!timer->stopped && timer->timeout == 0) { + // special case: timeout=0 means the callback will be + // invoked again on the next event loop tick. + // we don't use uv_idle_t to not spin the event loop + // when the main loop is blocked. + time_watcher_start(&timer->tw, timer_due_cb, 0, 0); + } + timer_decref(timer); +} + +uint64_t timer_start(const long timeout, const int repeat_count, const Callback *const callback) +{ + timer_T *timer = xmalloc(sizeof *timer); + timer->refcount = 1; + timer->stopped = false; + timer->paused = false; + timer->emsg_count = 0; + timer->repeat_count = repeat_count; + timer->timeout = timeout; + timer->timer_id = (int)last_timer_id++; + timer->callback = *callback; + + time_watcher_init(&main_loop, &timer->tw, timer); + timer->tw.events = multiqueue_new_child(main_loop.events); + // if main loop is blocked, don't queue up multiple events + timer->tw.blockable = true; + time_watcher_start(&timer->tw, timer_due_cb, (uint64_t)timeout, (uint64_t)timeout); + + pmap_put(uint64_t)(&timers, (uint64_t)timer->timer_id, timer); + return (uint64_t)timer->timer_id; +} + +void timer_stop(timer_T *timer) +{ + if (timer->stopped) { + // avoid double free + return; + } + timer->stopped = true; + time_watcher_stop(&timer->tw); + time_watcher_close(&timer->tw, timer_close_cb); +} + +/// This will be run on the main loop after the last timer_due_cb, so at this +/// point it is safe to free the callback. +static void timer_close_cb(TimeWatcher *tw, void *data) +{ + timer_T *timer = (timer_T *)data; + multiqueue_free(timer->tw.events); + callback_free(&timer->callback); + pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id); + timer_decref(timer); +} + +static void timer_decref(timer_T *timer) +{ + if (--timer->refcount == 0) { + xfree(timer); + } +} + +void timer_stop_all(void) +{ + timer_T *timer; + map_foreach_value(&timers, timer, { + timer_stop(timer); + }) +} + +void timer_teardown(void) +{ + timer_stop_all(); +} + +/// Write "list" of strings to file "fd". +/// +/// @param fp File to write to. +/// @param[in] list List to write. +/// @param[in] binary Whether to write in binary mode. +/// +/// @return true in case of success, false otherwise. +bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary) + FUNC_ATTR_NONNULL_ARG(1) +{ + int error = 0; + TV_LIST_ITER_CONST(list, li, { + const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + if (s == NULL) { + return false; + } + const char *hunk_start = s; + for (const char *p = hunk_start;; p++) { + if (*p == NUL || *p == NL) { + if (p != hunk_start) { + const ptrdiff_t written = file_write(fp, hunk_start, + (size_t)(p - hunk_start)); + if (written < 0) { + error = (int)written; + goto write_list_error; + } + } + if (*p == NUL) { + break; + } else { + hunk_start = p + 1; + const ptrdiff_t written = file_write(fp, (char[]){ NUL }, 1); + if (written < 0) { + error = (int)written; + break; + } + } + } + } + if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { + const ptrdiff_t written = file_write(fp, "\n", 1); + if (written < 0) { + error = (int)written; + goto write_list_error; + } + } + }); + if ((error = file_flush(fp)) != 0) { + goto write_list_error; + } + return true; +write_list_error: + semsg(_(e_write2), os_strerror(error)); + return false; +} + +/// Write a blob to file with descriptor `fp`. +/// +/// @param[in] fp File to write to. +/// @param[in] blob Blob to write. +/// +/// @return true on success, or false on failure. +bool write_blob(FileDescriptor *const fp, const blob_T *const blob) + FUNC_ATTR_NONNULL_ARG(1) +{ + int error = 0; + const int len = tv_blob_len(blob); + if (len > 0) { + const ptrdiff_t written = file_write(fp, blob->bv_ga.ga_data, (size_t)len); + if (written < (ptrdiff_t)len) { + error = (int)written; + goto write_blob_error; + } + } + error = file_flush(fp); + if (error != 0) { + goto write_blob_error; + } + return true; +write_blob_error: + semsg(_(e_write2), os_strerror(error)); + return false; +} + +/// Read a blob from a file `fd`. +/// +/// @param[in] fd File to read from. +/// @param[in,out] blob Blob to write to. +/// +/// @return true on success, or false on failure. +bool read_blob(FILE *const fd, blob_T *const blob) + FUNC_ATTR_NONNULL_ALL +{ + FileInfo file_info; + if (!os_fileinfo_fd(fileno(fd), &file_info)) { + return false; + } + const int size = (int)os_fileinfo_size(&file_info); + ga_grow(&blob->bv_ga, size); + blob->bv_ga.ga_len = size; + if (fread(blob->bv_ga.ga_data, 1, (size_t)blob->bv_ga.ga_len, fd) + < (size_t)blob->bv_ga.ga_len) { + return false; + } + return true; +} + +/// Saves a typval_T as a string. +/// +/// For lists or buffers, replaces NLs with NUL and separates items with NLs. +/// +/// @param[in] tv Value to store as a string. +/// @param[out] len Length of the resulting string or -1 on error. +/// @param[in] endnl If true, the output will end in a newline (if a list). +/// @returns an allocated string if `tv` represents a VimL string, list, or +/// number; NULL otherwise. +char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL +{ + *len = 0; + if (tv->v_type == VAR_UNKNOWN) { + return NULL; + } + + // For other types, let tv_get_string_buf_chk() get the value or + // print an error. + if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) { + const char *ret = tv_get_string_chk(tv); + if (ret) { + *len = (ptrdiff_t)strlen(ret); + return xmemdupz(ret, (size_t)(*len)); + } else { + *len = -1; + return NULL; + } + } + + if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id. + buf_T *buf = buflist_findnr((int)tv->vval.v_number); + if (buf) { + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *len += 1; + } + *len += 1; + } + } else { + semsg(_(e_nobufnr), tv->vval.v_number); + *len = -1; + return NULL; + } + + if (*len == 0) { + return NULL; + } + + char *ret = xmalloc((size_t)(*len) + 1); + char *end = ret; + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *end++ = (*p == '\n') ? NUL : *p; + } + *end++ = '\n'; + } + *end = NUL; + *len = end - ret; + return ret; + } + + assert(tv->v_type == VAR_LIST); + // Pre-calculate the resulting length. + list_T *list = tv->vval.v_list; + TV_LIST_ITER_CONST(list, li, { + *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; + }); + + if (*len == 0) { + return NULL; + } + + char *ret = xmalloc((size_t)(*len) + endnl); + char *end = ret; + TV_LIST_ITER_CONST(list, li, { + for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) { + *end++ = (*s == '\n') ? NUL : *s; + } + if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) { + *end++ = '\n'; + } + }); + *end = NUL; + *len = end - ret; + return ret; +} + +/// Convert the specified byte index of line 'lnum' in buffer 'buf' to a +/// character index. Works only for loaded buffers. Returns -1 on failure. +/// The index of the first byte and the first character is zero. +int buf_byteidx_to_charidx(buf_T *buf, linenr_T lnum, int byteidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char *str = (char *)ml_get_buf(buf, lnum, false); + + if (*str == NUL) { + return 0; + } + + // count the number of characters + char *t = str; + int count; + for (count = 0; *t != NUL && t <= str + byteidx; count++) { + t += utfc_ptr2len(t); + } + + // In insert mode, when the cursor is at the end of a non-empty line, + // byteidx points to the NUL character immediately past the end of the + // string. In this case, add one to the character count. + if (*t == NUL && byteidx != 0 && t == str + byteidx) { + count++; + } + + return count - 1; +} + +/// Convert the specified character index of line 'lnum' in buffer 'buf' to a +/// byte index. Works only for loaded buffers. +/// The index of the first byte and the first character is zero. +/// +/// @return -1 on failure. +int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char *str = (char *)ml_get_buf(buf, lnum, false); + + // Convert the character offset to a byte offset + char *t = str; + while (*t != NUL && --charidx > 0) { + t += utfc_ptr2len(t); + } + + return (int)(t - str); +} + +/// Translate a VimL object into a position +/// +/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid +/// type. +/// +/// @param[in] tv Object to translate. +/// @param[in] dollar_lnum True when "$" is last line. +/// @param[out] ret_fnum Set to fnum for marks. +/// @param[in] charcol True to return character column. +/// +/// @return Pointer to position or NULL in case of error (e.g. invalid type). +pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum, + const bool charcol) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + static pos_T pos; + + // Argument can be [lnum, col, coladd]. + if (tv->v_type == VAR_LIST) { + list_T *l; + int len; + bool error = false; + listitem_T *li; + + l = tv->vval.v_list; + if (l == NULL) { + return NULL; + } + + // Get the line number. + pos.lnum = (linenr_T)tv_list_find_nr(l, 0L, &error); + if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) { + // Invalid line number. + return NULL; + } + + // Get the column number. + pos.col = (colnr_T)tv_list_find_nr(l, 1L, &error); + if (error) { + return NULL; + } + if (charcol) { + len = mb_charlen(ml_get(pos.lnum)); + } else { + len = (int)STRLEN(ml_get(pos.lnum)); + } + + // We accept "$" for the column number: last column. + li = tv_list_find(l, 1L); + if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING + && TV_LIST_ITEM_TV(li)->vval.v_string != NULL + && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { + pos.col = len + 1; + } + + // Accept a position up to the NUL after the line. + if (pos.col == 0 || (int)pos.col > len + 1) { + // Invalid column number. + return NULL; + } + pos.col--; + + // Get the virtual offset. Defaults to zero. + pos.coladd = (colnr_T)tv_list_find_nr(l, 2L, &error); + if (error) { + pos.coladd = 0; + } + + return &pos; + } + + const char *const name = tv_get_string_chk(tv); + if (name == NULL) { + return NULL; + } + + pos.lnum = 0; + if (name[0] == '.') { + // cursor + pos = curwin->w_cursor; + } else if (name[0] == 'v' && name[1] == NUL) { + // Visual start + if (VIsual_active) { + pos = VIsual; + } else { + pos = curwin->w_cursor; + } + } else if (name[0] == '\'') { + // mark + int mname = (uint8_t)name[1]; + const fmark_T *const fm = mark_get(curbuf, curwin, NULL, kMarkAll, mname); + if (fm == NULL || fm->mark.lnum <= 0) { + return NULL; + } + pos = fm->mark; + // Vimscript behavior, only provide fnum if mark is global. + *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum: *ret_fnum; + } + if (pos.lnum != 0) { + if (charcol) { + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); + } + return &pos; + } + + pos.coladd = 0; + + if (name[0] == 'w' && dollar_lnum) { + pos.col = 0; + if (name[1] == '0') { // "w0": first visible line + update_topline(curwin); + // In silent Ex mode topline is zero, but that's not a valid line + // number; use one instead. + pos.lnum = curwin->w_topline > 0 ? curwin->w_topline : 1; + return &pos; + } else if (name[1] == '$') { // "w$": last visible line + validate_botline(curwin); + // In silent Ex mode botline is zero, return zero then. + pos.lnum = curwin->w_botline > 0 ? curwin->w_botline - 1 : 0; + return &pos; + } + } else if (name[0] == '$') { // last column or line + if (dollar_lnum) { + pos.lnum = curbuf->b_ml.ml_line_count; + pos.col = 0; + } else { + pos.lnum = curwin->w_cursor.lnum; + if (charcol) { + pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); + } else { + pos.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } + } + return &pos; + } + return NULL; +} + +/// Convert list in "arg" into a position and optional file number. +/// When "fnump" is NULL there is no file number, only 3 items. +/// Note that the column is passed on as-is, the caller may want to decrement +/// it to use 1 for the first column. +/// +/// @return FAIL when conversion is not possible, doesn't check the position for +/// validity. +int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol) +{ + list_T *l; + int i = 0; + long n; + + // List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only + // there when "fnump" isn't NULL; "coladd" and "curswant" are optional. + if (arg->v_type != VAR_LIST + || (l = arg->vval.v_list) == NULL + || tv_list_len(l) < (fnump == NULL ? 2 : 3) + || tv_list_len(l) > (fnump == NULL ? 4 : 5)) { + return FAIL; + } + + if (fnump != NULL) { + n = tv_list_find_nr(l, i++, NULL); // fnum + if (n < 0) { + return FAIL; + } + if (n == 0) { + n = curbuf->b_fnum; // Current buffer. + } + *fnump = (int)n; + } + + n = tv_list_find_nr(l, i++, NULL); // lnum + if (n < 0) { + return FAIL; + } + posp->lnum = (linenr_T)n; + + n = tv_list_find_nr(l, i++, NULL); // col + if (n < 0) { + return FAIL; + } + // If character position is specified, then convert to byte position + if (charcol) { + // Get the text for the specified line in a loaded buffer + buf_T *buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump); + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return FAIL; + } + n = buf_charidx_to_byteidx(buf, posp->lnum, (int)n) + 1; + } + posp->col = (colnr_T)n; + + n = tv_list_find_nr(l, i, NULL); // off + if (n < 0) { + posp->coladd = 0; + } else { + posp->coladd = (colnr_T)n; + } + + if (curswantp != NULL) { + *curswantp = (colnr_T)tv_list_find_nr(l, i + 1, NULL); // curswant + } + + return OK; +} + +/// Get the length of an environment variable name. +/// Advance "arg" to the first character after the name. +/// +/// @return 0 for error. +int get_env_len(const char **arg) +{ + int len; + + const char *p; + for (p = *arg; vim_isIDc(*p); p++) {} + if (p == *arg) { // No name found. + return 0; + } + + len = (int)(p - *arg); + *arg = p; + return len; +} + +/// Get the length of the name of a function or internal variable. +/// +/// @param arg is advanced to the first non-white character after the name. +/// +/// @return 0 if something is wrong. +int get_id_len(const char **const arg) +{ + int len; + + // Find the end of the name. + const char *p; + 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 = (const char *)skipwhite(p); + + return len; +} + +/// Get the length of the name of a variable or function. +/// Only the name is recognized, does not handle ".key" or "[idx]". +/// +/// @param arg is advanced to the first non-white character after the name. +/// If the name contains 'magic' {}'s, expand them and return the +/// expanded name in an allocated string via 'alias' - caller must free. +/// +/// @return -1 if curly braces expansion failed or +/// 0 if something else is wrong. +int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbose) +{ + int len; + + *alias = NULL; // default to no alias + + if ((*arg)[0] == (char)K_SPECIAL && (*arg)[1] == (char)KS_EXTRA + && (*arg)[2] == (char)KE_SNR) { + // Hard coded <SNR>, already translated. + *arg += 3; + return get_id_len(arg) + 3; + } + len = eval_fname_script(*arg); + if (len > 0) { + // literal "<SID>", "s:" or "<SNR>" + *arg += len; + } + + // Find the end of the name; check for {} construction. + char *expr_start; + char *expr_end; + const char *p = find_name_end((*arg), (const char **)&expr_start, (const char **)&expr_end, + len > 0 ? 0 : FNE_CHECK_START); + if (expr_start != NULL) { + if (!evaluate) { + len += (int)(p - *arg); + *arg = (const char *)skipwhite(p); + return len; + } + + /* + * Include any <SID> etc in the expanded string: + * Thus the -len here. + */ + char *temp_string = make_expanded_name(*arg - len, expr_start, expr_end, (char *)p); + if (temp_string == NULL) { + return -1; + } + *alias = temp_string; + *arg = (const char *)skipwhite(p); + return (int)STRLEN(temp_string); + } + + len += get_id_len(arg); + // Only give an error when there is something, otherwise it will be + // reported at a higher level. + if (len == 0 && verbose && **arg != NUL) { + semsg(_(e_invexpr2), *arg); + } + + return len; +} + +/// Find the end of a variable or function name, taking care of magic braces. +/// +/// @param expr_start if not NULL, then `expr_start` and `expr_end` are set to the +/// start and end of the first magic braces item. +/// +/// @param 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. +const char *find_name_end(const char *arg, const char **expr_start, const char **expr_end, + int flags) +{ + int mb_nest = 0; + int br_nest = 0; + 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 != '{') { + return arg; + } + + const char *p; + for (p = arg; *p != NUL + && (eval_isnamec(*p) + || *p == '{' + || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.')) + || mb_nest != 0 + || br_nest != 0); MB_PTR_ADV(p)) { + if (*p == '\'') { + // skip over 'string' to avoid counting [ and ] inside it. + for (p = p + 1; *p != NUL && *p != '\''; MB_PTR_ADV(p)) {} + 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) { + ++p; + } + } + 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 == '[') { + ++br_nest; + } else if (*p == ']') { + --br_nest; + } + } + + if (br_nest == 0) { + if (*p == '{') { + mb_nest++; + 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) { + *expr_end = p; + } + } + } + } + + return p; +} + +/// Expands out the 'magic' {}'s in a variable/function name. +/// Note that this can call itself recursively, to deal with +/// constructs like foo{bar}{baz}{bam} +/// The four pointer arguments point to "foo{expre}ss{ion}bar" +/// "in_start" ^ +/// "expr_start" ^ +/// "expr_end" ^ +/// "in_end" ^ +/// +/// @return a new allocated string, which the caller must free or +/// NULL for failure. +static char *make_expanded_name(const char *in_start, char *expr_start, char *expr_end, + char *in_end) +{ + char c1; + char *retval = NULL; + char *temp_result; + char *nextcmd = NULL; + + if (expr_end == NULL || in_end == NULL) { + return NULL; + } + *expr_start = NUL; + *expr_end = NUL; + c1 = *in_end; + *in_end = NUL; + + temp_result = eval_to_string(expr_start + 1, &nextcmd, false); + if (temp_result != NULL && nextcmd == NULL) { + retval = xmalloc(STRLEN(temp_result) + (size_t)(expr_start - in_start) + + (size_t)(in_end - expr_end) + 1); + STRCPY(retval, in_start); + STRCAT(retval, temp_result); + STRCAT(retval, expr_end + 1); + } + xfree(temp_result); + + *in_end = c1; // put char back for error messages + *expr_start = '{'; + *expr_end = '}'; + + if (retval != NULL) { + temp_result = (char *)find_name_end(retval, + (const char **)&expr_start, + (const char **)&expr_end, 0); + if (expr_start != NULL) { + // Further expansion! + temp_result = make_expanded_name(retval, expr_start, + expr_end, temp_result); + xfree(retval); + retval = temp_result; + } + } + + return retval; +} + +/// @return TRUE if character "c" can be used in a variable or function name. +/// Does not include '{' or '}' for magic braces. +int eval_isnamec(int c) +{ + return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR; +} + +/// @return TRUE if character "c" can be used as the first character in a +/// variable or function name (excluding '{' and '}'). +int eval_isnamec1(int c) +{ + return ASCII_ISALPHA(c) || c == '_'; +} + +/// Get typval_T v: variable value. +typval_T *get_vim_var_tv(int idx) +{ + return &vimvars[idx].vv_tv; +} + +/// Get number v: variable value. +varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE +{ + return vimvars[idx].vv_nr; +} + +/// Get string v: variable value. Uses a static buffer, can only be used once. +/// If the String variable has never been set, return an empty string. +/// Never returns NULL. +char *get_vim_var_str(int idx) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET +{ + return (char *)tv_get_string(&vimvars[idx].vv_tv); +} + +/// Get List v: variable value. Caller must take care of reference count when +/// needed. +list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE +{ + return vimvars[idx].vv_list; +} + +/// Get Dictionary v: variable value. Caller must take care of reference count +/// when needed. +dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE +{ + return vimvars[idx].vv_dict; +} + +/// Set v:char to character "c". +void set_vim_var_char(int c) +{ + char buf[MB_MAXBYTES + 1]; + + buf[utf_char2bytes(c, buf)] = NUL; + set_vim_var_string(VV_CHAR, buf, -1); +} + +/// Set v:count to "count" and v:count1 to "count1". +/// +/// @param set_prevcount if TRUE, first set v:prevcount from v:count. +void set_vcount(long count, long count1, int set_prevcount) +{ + if (set_prevcount) { + vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr; + } + vimvars[VV_COUNT].vv_nr = count; + vimvars[VV_COUNT1].vv_nr = count1; +} + +/// 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) +{ + tv_clear(&vimvars[idx].vv_tv); + vimvars[idx].vv_type = VAR_NUMBER; + vimvars[idx].vv_nr = val; +} + +/// Set boolean v: {true, false} to the given value +/// +/// @param[in] idx Index of variable to set. +/// @param[in] val Value to set to. +void set_vim_var_bool(const VimVarIndex idx, const BoolVarValue val) +{ + tv_clear(&vimvars[idx].vv_tv); + vimvars[idx].vv_type = VAR_BOOL; + vimvars[idx].vv_bool = 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) +{ + tv_clear(&vimvars[idx].vv_tv); + vimvars[idx].vv_type = VAR_SPECIAL; + 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 Length 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) +{ + tv_clear(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_STRING; + if (val == NULL) { + vimvars[idx].vv_str = NULL; + } else if (len == -1) { + vimvars[idx].vv_str = xstrdup(val); + } else { + vimvars[idx].vv_str = xstrndup(val, (size_t)len); + } +} + +/// 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) +{ + tv_clear(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_LIST; + vimvars[idx].vv_list = val; + if (val != NULL) { + tv_list_ref(val); + } +} + +/// 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) +{ + tv_clear(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_DICT; + vimvars[idx].vv_dict = val; + + if (val != NULL) { + val->dv_refcount++; + // Set readonly + tv_dict_set_keys_readonly(val); + } +} + +/// Set the v:argv list. +void set_argv_var(char **argv, int argc) +{ + list_T *l = tv_list_alloc(argc); + int i; + + tv_list_set_lock(l, VAR_FIXED); + for (i = 0; i < argc; i++) { + tv_list_append_string(l, (const char *const)argv[i], -1); + TV_LIST_ITEM_TV(tv_list_last(l))->v_lock = VAR_FIXED; + } + set_vim_var_list(VV_ARGV, l); +} + +/// Set v:register if needed. +void set_reg_var(int c) +{ + char regname; + + if (c == 0 || c == ' ') { + regname = '"'; + } else { + regname = (char)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); + } +} + +/// Get or set v:exception. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:exception! Does not +/// take care of memory allocations. +char *v_exception(char *oldval) +{ + if (oldval == NULL) { + return vimvars[VV_EXCEPTION].vv_str; + } + + vimvars[VV_EXCEPTION].vv_str = oldval; + return NULL; +} + +/// Get or set v:throwpoint. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:throwpoint! Does not +/// take care of memory allocations. +char *v_throwpoint(char *oldval) +{ + if (oldval == NULL) { + return vimvars[VV_THROWPOINT].vv_str; + } + + vimvars[VV_THROWPOINT].vv_str = oldval; + return NULL; +} + +/// Set v:cmdarg. +/// If "eap" != NULL, use "eap" to generate the value and return the old value. +/// If "oldarg" != NULL, restore the value to "oldarg" and return NULL. +/// Must always be called in pairs! +char *set_cmdarg(exarg_T *eap, char *oldarg) +{ + char *oldval = vimvars[VV_CMDARG].vv_str; + if (eap == NULL) { + xfree(oldval); + vimvars[VV_CMDARG].vv_str = oldarg; + return NULL; + } + + size_t len = 0; + if (eap->force_bin == FORCE_BIN) { + len = 6; + } else if (eap->force_bin == FORCE_NOBIN) { + len = 8; + } + + if (eap->read_edit) { + len += 7; + } + + if (eap->force_ff != 0) { + len += 10; // " ++ff=unix" + } + if (eap->force_enc != 0) { + len += STRLEN(eap->cmd + eap->force_enc) + 7; + } + if (eap->bad_char != 0) { + len += 7 + 4; // " ++bad=" + "keep" or "drop" + } + + const size_t newval_len = len + 1; + char *newval = xmalloc(newval_len); + + if (eap->force_bin == FORCE_BIN) { + snprintf(newval, newval_len, " ++bin"); + } else if (eap->force_bin == FORCE_NOBIN) { + snprintf(newval, newval_len, " ++nobin"); + } else { + *newval = NUL; + } + + if (eap->read_edit) { + STRCAT(newval, " ++edit"); + } + + if (eap->force_ff != 0) { + snprintf(newval + STRLEN(newval), newval_len, " ++ff=%s", + eap->force_ff == 'u' ? "unix" : + eap->force_ff == 'd' ? "dos" : "mac"); + } + if (eap->force_enc != 0) { + snprintf(newval + STRLEN(newval), newval_len, " ++enc=%s", + eap->cmd + eap->force_enc); + } + if (eap->bad_char == BAD_KEEP) { + STRCPY(newval + STRLEN(newval), " ++bad=keep"); + } else if (eap->bad_char == BAD_DROP) { + STRCPY(newval + STRLEN(newval), " ++bad=drop"); + } else if (eap->bad_char != 0) { + snprintf(newval + STRLEN(newval), newval_len, " ++bad=%c", + eap->bad_char); + } + vimvars[VV_CMDARG].vv_str = newval; + return oldval; +} + +/// Check if variable "name[len]" is a local variable or an argument. +/// If so, "*eval_lavars_used" is set to true. +static void check_vars(const char *name, size_t len) +{ + if (eval_lavars_used == NULL) { + return; + } + + const char *varname; + hashtab_T *ht = find_var_ht(name, len, &varname); + + if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht()) { + if (find_var(name, len, NULL, true) != NULL) { + *eval_lavars_used = true; + } + } +} + +/// check if special v:lua value for calling lua functions +bool is_luafunc(partial_T *partial) + FUNC_ATTR_PURE +{ + return partial == vvlua_partial; +} + +/// check if special v:lua value for calling lua functions +static bool tv_is_luafunc(typval_T *tv) +{ + return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial); +} + +/// Skips one character past the end of the name of a v:lua function. +/// @param p Pointer to the char AFTER the "v:lua." prefix. +/// @return Pointer to the char one past the end of the function's name. +const char *skip_luafunc_name(const char *p) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '-' || *p == '.' || *p == '\'') { + p++; + } + return p; +} + +/// check the function name after "v:lua." +int check_luafunc_name(const char *const str, const bool paren) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const p = skip_luafunc_name(str); + if (*p != (paren ? '(' : NUL)) { + return 0; + } else { + return (int)(p - str); + } +} + +/// Handle: +/// - expr[expr], expr[expr:expr] subscript +/// - ".name" lookup +/// - function call with Funcref variable: func(expr) +/// - method call: var->method() +/// +/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len() +/// +/// @param evaluate do more than finding the end +/// @param verbose give error messages +/// @param start_leader start of '!' and '-' prefixes +/// @param end_leaderp end of '!' and '-' prefixes +int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int verbose, + const char *const start_leader, const char **const end_leaderp) +{ + int ret = OK; + dict_T *selfdict = NULL; + const char *lua_funcname = NULL; + + if (tv_is_luafunc(rettv)) { + if (**arg != '.') { + tv_clear(rettv); + ret = FAIL; + } else { + (*arg)++; + + lua_funcname = *arg; + const int len = check_luafunc_name(*arg, true); + if (len == 0) { + tv_clear(rettv); + ret = FAIL; + } + (*arg) += len; + } + } + + // "." is ".name" lookup when we found a dict. + while (ret == OK + && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) + && !ascii_iswhite(*(*arg - 1))) + || (**arg == '-' && (*arg)[1] == '>'))) { + if (**arg == '(') { + ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname); + + // 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) { + tv_clear(rettv); + } + ret = FAIL; + } + tv_dict_unref(selfdict); + selfdict = NULL; + } else if (**arg == '-') { + // Expression "-1.0->method()" applies the leader "-" before + // applying ->. + if (evaluate && *end_leaderp > start_leader) { + ret = eval7_leader(rettv, (char *)start_leader, end_leaderp); + } + if (ret == OK) { + if ((*arg)[2] == '{') { + // expr->{lambda}() + ret = eval_lambda((char **)arg, rettv, evaluate, verbose); + } else { + // expr->name() + ret = eval_method((char **)arg, rettv, evaluate, verbose); + } + } + } else { // **arg == '[' || **arg == '.' + tv_dict_unref(selfdict); + if (rettv->v_type == VAR_DICT) { + selfdict = rettv->vval.v_dict; + if (selfdict != NULL) { + ++selfdict->dv_refcount; + } + } else { + selfdict = NULL; + } + if (eval_index((char **)arg, rettv, evaluate, verbose) == FAIL) { + tv_clear(rettv); + ret = FAIL; + } + } + } + + // Turn "dict.Func" into a partial for "Func" bound to "dict". + if (selfdict != NULL && tv_is_func(*rettv)) { + set_selfdict(rettv, selfdict); + } + + tv_dict_unref(selfdict); + return ret; +} + +void set_selfdict(typval_T *const rettv, dict_T *const selfdict) +{ + // Don't do this when "dict.Func" is already a partial that was bound + // explicitly (pt_auto is false). + if (rettv->v_type == VAR_PARTIAL && !rettv->vval.v_partial->pt_auto + && rettv->vval.v_partial->pt_dict != NULL) { + return; + } + make_partial(selfdict, rettv); +} + +/// Find variable "name" in the list of variables. +/// Careful: "a:0" variables don't have a name. +/// When "htp" is not NULL we are writing to the variable, set "htp" to the +/// hashtab_T used. +/// +/// @return a pointer to it if found, NULL if not found. +dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, + int no_autoload) +{ + const char *varname; + hashtab_T *const ht = find_var_ht(name, name_len, &varname); + if (htp != NULL) { + *htp = ht; + } + if (ht == NULL) { + return NULL; + } + dictitem_T *const ret = find_var_in_ht(ht, *name, + varname, + name_len - (size_t)(varname - name), + no_autoload || htp != NULL); + if (ret != NULL) { + return ret; + } + + // Search in parent scope for lambda + return find_var_in_scoped_ht(name, name_len, no_autoload || htp != NULL); +} + +/// Find variable in hashtab. +/// When "varname" is empty returns curwin/curtab/etc vars dictionary. +/// +/// @param[in] ht Hashtab to find variable in. +/// @param[in] htname Hashtab name (first character). +/// @param[in] varname Variable name. +/// @param[in] varname_len Variable name length. +/// @param[in] no_autoload If true then autoload scripts will not be sourced +/// if autoload variable was not found. +/// +/// @return pointer to the dictionary item with the found variable or NULL if it +/// was not found. +dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const varname, + const size_t varname_len, int no_autoload) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + hashitem_T *hi; + + if (varname_len == 0) { + // Must be something like "s:", otherwise "ht" would be NULL. + switch (htname) { + case 's': + return (dictitem_T *)&SCRIPT_SV(current_sctx.sc_sid)->sv_var; + case 'g': + return (dictitem_T *)&globvars_var; + case 'v': + return (dictitem_T *)&vimvars_var; + case 'b': + return (dictitem_T *)&curbuf->b_bufvar; + case 'w': + return (dictitem_T *)&curwin->w_winvar; + case 't': + return (dictitem_T *)&curtab->tp_winvar; + case 'l': + return get_funccal_local_var(); + case 'a': + return get_funccal_args_var(); + } + return NULL; + } + + hi = hash_find_len(ht, varname, varname_len); + if (HASHITEM_EMPTY(hi)) { + // For global variables we may try auto-loading the script. If it + // worked find the variable again. Don't auto-load a script if it was + // loaded already, otherwise it would be loaded every time when + // checking if a function name is a Funcref variable. + if (ht == &globvarht && !no_autoload) { + // Note: script_autoload() may make "hi" invalid. It must either + // be obtained again or not used. + if (!script_autoload(varname, varname_len, false) || aborting()) { + return NULL; + } + hi = hash_find_len(ht, varname, varname_len); + } + if (HASHITEM_EMPTY(hi)) { + return NULL; + } + } + return TV_DICT_HI2DI(hi); +} + +/// Finds the dict (g:, l:, s:, …) and hashtable used for a variable. +/// +/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994 +/// +/// @param[in] name Variable name, possibly with scope prefix. +/// @param[in] name_len Variable name length. +/// @param[out] varname Will be set to the start of the name without scope +/// prefix. +/// @param[out] d Scope dictionary. +/// +/// @return Scope hashtab, NULL if name is not valid. +hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname, + dict_T **d) +{ + hashitem_T *hi; + funccall_T *funccal = get_funccal(); + *d = NULL; + + if (name_len == 0) { + return NULL; + } + if (name_len == 1 || name[1] != ':') { + // name has implicit scope + if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) { + // The name must not start with a colon or #. + return NULL; + } + *varname = name; + + // "version" is "v:version" in all scopes + hi = hash_find_len(&compat_hashtab, name, name_len); + if (!HASHITEM_EMPTY(hi)) { + return &compat_hashtab; + } + + if (funccal == NULL) { // global variable + *d = &globvardict; + } else { // l: variable + *d = &funccal->l_vars; + } + goto end; + } + + *varname = name + 2; + if (*name == 'g') { // global variable + *d = &globvardict; + } else if (name_len > 2 + && (memchr(name + 2, ':', name_len - 2) != NULL + || memchr(name + 2, AUTOLOAD_CHAR, name_len - 2) != NULL)) { + // There must be no ':' or '#' in the rest of the name if g: was not used + return NULL; + } + + if (*name == 'b') { // buffer variable + *d = curbuf->b_vars; + } else if (*name == 'w') { // window variable + *d = curwin->w_vars; + } else if (*name == 't') { // tab page variable + *d = curtab->tp_vars; + } else if (*name == 'v') { // v: variable + *d = &vimvardict; + } else if (*name == 'a' && funccal != NULL) { // function argument + *d = &funccal->l_avars; + } else if (*name == 'l' && funccal != NULL) { // local variable + *d = &funccal->l_vars; + } else if (*name == 's' // script variable + && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR + || current_sctx.sc_sid == SID_LUA) + && current_sctx.sc_sid <= ga_scripts.ga_len) { + // For anonymous scripts without a script item, create one now so script vars can be used + if (current_sctx.sc_sid == SID_LUA) { + // try to resolve lua filename & line no so it can be shown in lastset messages. + nlua_set_sctx(¤t_sctx); + if (current_sctx.sc_sid != SID_LUA) { + // Great we have valid location. Now here this out we'll create a new + // script context with the name and lineno of this one. why ? + // for behavioral consistency. With this different anonymous exec from + // same file can't access each others script local stuff. We need to do + // this all other cases except this will act like that otherwise. + const LastSet last_set = (LastSet){ + .script_ctx = current_sctx, + .channel_id = LUA_INTERNAL_CALL, + }; + bool should_free; + // should_free is ignored as script_sctx will be resolved to a fnmae + // & new_script_item will consume it. + char *sc_name = (char *)get_scriptname(last_set, &should_free); + new_script_item(sc_name, ¤t_sctx.sc_sid); + } + } + if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) { + // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994 + new_script_item(NULL, ¤t_sctx.sc_sid); + } + *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict; + } + +end: + return *d ? &(*d)->dv_hashtab : NULL; +} + +/// Find the hashtable used for a variable +/// +/// @param[in] name Variable name, possibly with scope prefix. +/// @param[in] name_len Variable name length. +/// @param[out] varname Will be set to the start of the name without scope +/// prefix. +/// +/// @return Scope hashtab, NULL if name is not valid. +hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **varname) +{ + dict_T *d; + return find_var_ht_dict(name, name_len, varname, &d); +} + +/// Allocate a new hashtab for a sourced script. It will be used while +/// sourcing this script and when executing functions defined in the script. +void new_script_vars(scid_T id) +{ + hashtab_T *ht; + scriptvar_T *sv; + + ga_grow(&ga_scripts, id - ga_scripts.ga_len); + { + /* Re-allocating ga_data means that an ht_array pointing to + * ht_smallarray becomes invalid. We can recognize this: ht_mask is + * at its init value. Also reset "v_dict", it's always the same. */ + for (int i = 1; i <= ga_scripts.ga_len; ++i) { + ht = &SCRIPT_VARS(i); + if (ht->ht_mask == HT_INIT_SIZE - 1) { + ht->ht_array = ht->ht_smallarray; + } + sv = SCRIPT_SV(i); + sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict; + } + + while (ga_scripts.ga_len < id) { + sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T)); + init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE); + ++ga_scripts.ga_len; + } + } +} + +/// Initialize dictionary "dict" as a scope and set variable "dict_var" to +/// point to it. +void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, ScopeType scope) +{ + hash_init(&dict->dv_hashtab); + dict->dv_lock = VAR_UNLOCKED; + dict->dv_scope = scope; + dict->dv_refcount = DO_NOT_FREE_CNT; + dict->dv_copyID = 0; + dict_var->di_tv.vval.v_dict = dict; + dict_var->di_tv.v_type = VAR_DICT; + dict_var->di_tv.v_lock = VAR_FIXED; + dict_var->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + dict_var->di_key[0] = NUL; + QUEUE_INIT(&dict->watchers); +} + +/// Unreference a dictionary initialized by init_var_dict(). +void unref_var_dict(dict_T *dict) +{ + /* Now the dict needs to be freed if no one else is using it, go back to + * normal reference counting. */ + dict->dv_refcount -= DO_NOT_FREE_CNT - 1; + tv_dict_unref(dict); +} + +/// Make a copy of an item +/// +/// Lists and Dictionaries are also copied. +/// +/// @param[in] conv If not NULL, convert all copied strings. +/// @param[in] from Value to copy. +/// @param[out] to Location where to copy to. +/// @param[in] deep If true, use copy the container and all of the contained +/// containers (nested). +/// @param[in] copyID If non-zero then when container is referenced more then +/// once then copy of it that was already done is used. E.g. +/// when copying list `list = [list2, list2]` (`list[0] is +/// list[1]`) var_item_copy with zero copyID will emit +/// a copy with (`copy[0] isnot copy[1]`), with non-zero it +/// will emit a copy with (`copy[0] is copy[1]`) like in the +/// original list. Not used when deep is false. +int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *const to, + const bool deep, const int copyID) + FUNC_ATTR_NONNULL_ARG(2, 3) +{ + static int recurse = 0; + int ret = OK; + + if (recurse >= DICT_MAXNEST) { + emsg(_("E698: variable nested too deep for making a copy")); + return FAIL; + } + ++recurse; + + switch (from->v_type) { + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_BOOL: + case VAR_SPECIAL: + tv_copy(from, to); + break; + case VAR_STRING: + if (conv == NULL || conv->vc_type == CONV_NONE + || from->vval.v_string == NULL) { + tv_copy(from, to); + } else { + to->v_type = VAR_STRING; + to->v_lock = VAR_UNLOCKED; + if ((to->vval.v_string = (char *)string_convert((vimconv_T *)conv, + (char_u *)from->vval.v_string, + NULL)) + == NULL) { + to->vval.v_string = xstrdup(from->vval.v_string); + } + } + break; + case VAR_LIST: + to->v_type = VAR_LIST; + to->v_lock = VAR_UNLOCKED; + if (from->vval.v_list == NULL) { + to->vval.v_list = NULL; + } else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { + // Use the copy made earlier. + to->vval.v_list = tv_list_latest_copy(from->vval.v_list); + tv_list_ref(to->vval.v_list); + } else { + to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); + } + if (to->vval.v_list == NULL && from->vval.v_list != NULL) { + ret = FAIL; + } + break; + case VAR_BLOB: + tv_blob_copy(from, to); + break; + case VAR_DICT: + to->v_type = VAR_DICT; + to->v_lock = VAR_UNLOCKED; + if (from->vval.v_dict == NULL) { + to->vval.v_dict = NULL; + } else if (copyID != 0 && from->vval.v_dict->dv_copyID == copyID) { + // use the copy made earlier + to->vval.v_dict = from->vval.v_dict->dv_copydict; + ++to->vval.v_dict->dv_refcount; + } else { + to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); + } + if (to->vval.v_dict == NULL && from->vval.v_dict != NULL) { + ret = FAIL; + } + break; + case VAR_UNKNOWN: + internal_error("var_item_copy(UNKNOWN)"); + ret = FAIL; + } + --recurse; + return ret; +} + +/// ":echo expr1 ..." print each argument separated with a space, add a +/// newline at the end. +/// ":echon expr1 ..." print each argument plain. +void ex_echo(exarg_T *eap) +{ + char *arg = eap->arg; + typval_T rettv; + bool atstart = true; + bool need_clear = true; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; + + if (eap->skip) { + ++emsg_skip; + } + while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) { + // If eval1() causes an error message the text from the command may + // still need to be cleared. E.g., "echo 22,44". + need_clr_eos = true; + + { + char *p = arg; + if (eval1(&arg, &rettv, !eap->skip) == FAIL) { + // Report the invalid expression unless the expression evaluation + // has been cancelled due to an aborting error, an interrupt, or an + // exception. + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { + semsg(_(e_invexpr2), p); + } + need_clr_eos = false; + break; + } + need_clr_eos = false; + } + + if (!eap->skip) { + if (atstart) { + atstart = false; + // Call msg_start() after eval1(), evaluating the expression + // may cause a message to appear. + if (eap->cmdidx == CMD_echo) { + // Mark the saved text as finishing the line, so that what + // follows is displayed on a new line when scrolling back + // at the more prompt. + msg_sb_eol(); + msg_start(); + } + } else if (eap->cmdidx == CMD_echo) { + msg_puts_attr(" ", echo_attr); + } + char *tofree = encode_tv2echo(&rettv, NULL); + if (*tofree != NUL) { + msg_ext_set_kind("echo"); + msg_multiline_attr(tofree, echo_attr, true, &need_clear); + } + xfree(tofree); + } + tv_clear(&rettv); + arg = skipwhite(arg); + } + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + + if (eap->skip) { + emsg_skip--; + } else { + // remove text that may still be there from the command + if (need_clear) { + msg_clr_eos(); + } + if (eap->cmdidx == CMD_echo) { + msg_end(); + } + } +} + +/// ":echohl {name}". +void ex_echohl(exarg_T *eap) +{ + echo_attr = syn_name2attr((char_u *)eap->arg); +} + +/// ":execute expr1 ..." execute the result of an expression. +/// ":echomsg expr1 ..." Print a message +/// ":echoerr expr1 ..." Print an error +/// Each gets spaces around each argument and a newline at the end for +/// echo commands +void ex_execute(exarg_T *eap) +{ + char *arg = eap->arg; + typval_T rettv; + int ret = OK; + garray_T ga; + int save_did_emsg; + + ga_init(&ga, 1, 80); + + if (eap->skip) { + ++emsg_skip; + } + while (*arg != NUL && *arg != '|' && *arg != '\n') { + ret = eval1_emsg(&arg, &rettv, !eap->skip); + if (ret == FAIL) { + break; + } + + if (!eap->skip) { + const char *const argstr = eap->cmdidx == CMD_execute + ? tv_get_string(&rettv) + : rettv.v_type == VAR_STRING + ? encode_tv2echo(&rettv, NULL) + : encode_tv2string(&rettv, NULL); + const size_t len = strlen(argstr); + ga_grow(&ga, (int)len + 2); + if (!GA_EMPTY(&ga)) { + ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; + } + memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1); + if (eap->cmdidx != CMD_execute) { + xfree((void *)argstr); + } + ga.ga_len += (int)len; + } + + tv_clear(&rettv); + arg = skipwhite(arg); + } + + if (ret != FAIL && ga.ga_data != NULL) { + if (eap->cmdidx == CMD_echomsg || eap->cmdidx == CMD_echoerr) { + // Mark the already saved text as finishing the line, so that what + // follows is displayed on a new line when scrolling back at the + // more prompt. + msg_sb_eol(); + } + + if (eap->cmdidx == CMD_echomsg) { + msg_ext_set_kind("echomsg"); + msg_attr(ga.ga_data, echo_attr); + ui_flush(); + } else if (eap->cmdidx == CMD_echoerr) { + // We don't want to abort following commands, restore did_emsg. + save_did_emsg = did_emsg; + msg_ext_set_kind("echoerr"); + emsg(ga.ga_data); + if (!force_abort) { + did_emsg = save_did_emsg; + } + } else if (eap->cmdidx == CMD_execute) { + do_cmdline(ga.ga_data, eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); + } + } + + ga_clear(&ga); + + if (eap->skip) { + --emsg_skip; + } + + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); +} + +/// Skip over the name of an option: "&option", "&g:option" or "&l:option". +/// +/// @param arg points to the "&" or '+' when called, to "option" when returning. +/// +/// @return NULL when no option name found. Otherwise pointer to the char +/// after the option name. +const char *find_option_end(const char **const arg, int *const opt_flags) +{ + const char *p = *arg; + + ++p; + if (*p == 'g' && p[1] == ':') { + *opt_flags = OPT_GLOBAL; + p += 2; + } else if (*p == 'l' && p[1] == ':') { + *opt_flags = OPT_LOCAL; + p += 2; + } else { + *opt_flags = 0; + } + + if (!ASCII_ISALPHA(*p)) { + return NULL; + } + *arg = p; + + if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) { + p += 4; // t_xx/termcap option + } else { + while (ASCII_ISALPHA(*p)) { + p++; + } + } + return p; +} + +/// Start profiling function "fp". +void func_do_profile(ufunc_T *fp) +{ + int len = fp->uf_lines.ga_len; + + if (!fp->uf_prof_initialized) { + if (len == 0) { + len = 1; // avoid getting error for allocating zero bytes + } + fp->uf_tm_count = 0; + fp->uf_tm_self = profile_zero(); + fp->uf_tm_total = profile_zero(); + + if (fp->uf_tml_count == NULL) { + fp->uf_tml_count = xcalloc((size_t)len, sizeof(int)); + } + + if (fp->uf_tml_total == NULL) { + fp->uf_tml_total = xcalloc((size_t)len, sizeof(proftime_T)); + } + + if (fp->uf_tml_self == NULL) { + fp->uf_tml_self = xcalloc((size_t)len, sizeof(proftime_T)); + } + + fp->uf_tml_idx = -1; + fp->uf_prof_initialized = true; + } + + fp->uf_profiling = TRUE; +} + +/// Dump the profiling results for all functions in file "fd". +void func_dump_profile(FILE *fd) +{ + hashitem_T *hi; + int todo; + ufunc_T *fp; + ufunc_T **sorttab; + int st_len = 0; + + todo = (int)func_hashtab.ht_used; + if (todo == 0) { + return; // nothing to dump + } + + sorttab = xmalloc(sizeof(ufunc_T *) * (size_t)todo); + + for (hi = func_hashtab.ht_array; todo > 0; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + --todo; + fp = HI2UF(hi); + if (fp->uf_prof_initialized) { + sorttab[st_len++] = fp; + + if (fp->uf_name[0] == K_SPECIAL) { + fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3); + } else { + fprintf(fd, "FUNCTION %s()\n", fp->uf_name); + } + if (fp->uf_script_ctx.sc_sid != 0) { + bool should_free; + const LastSet last_set = (LastSet){ + .script_ctx = fp->uf_script_ctx, + .channel_id = 0, + }; + char *p = (char *)get_scriptname(last_set, &should_free); + fprintf(fd, " Defined: %s:%" PRIdLINENR "\n", + p, fp->uf_script_ctx.sc_lnum); + if (should_free) { + xfree(p); + } + } + if (fp->uf_tm_count == 1) { + fprintf(fd, "Called 1 time\n"); + } else { + fprintf(fd, "Called %d times\n", fp->uf_tm_count); + } + fprintf(fd, "Total time: %s\n", profile_msg(fp->uf_tm_total)); + fprintf(fd, " Self time: %s\n", profile_msg(fp->uf_tm_self)); + fprintf(fd, "\n"); + fprintf(fd, "count total (s) self (s)\n"); + + for (int i = 0; i < fp->uf_lines.ga_len; ++i) { + if (FUNCLINE(fp, i) == NULL) { + continue; + } + prof_func_line(fd, fp->uf_tml_count[i], + &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE); + fprintf(fd, "%s\n", FUNCLINE(fp, i)); + } + fprintf(fd, "\n"); + } + } + } + + if (st_len > 0) { + qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), + prof_total_cmp); + prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE); + qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), + prof_self_cmp); + prof_sort_list(fd, sorttab, st_len, "SELF", TRUE); + } + + xfree(sorttab); +} + +/// @param prefer_self when equal print only self time +static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self) +{ + int i; + ufunc_T *fp; + + fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title); + fprintf(fd, "count total (s) self (s) function\n"); + for (i = 0; i < 20 && i < st_len; ++i) { + fp = sorttab[i]; + prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self, + prefer_self); + if (fp->uf_name[0] == K_SPECIAL) { + fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3); + } else { + fprintf(fd, " %s()\n", fp->uf_name); + } + } + fprintf(fd, "\n"); +} + +/// Print the count and times for one function or function line. +/// +/// @param prefer_self when equal print only self time +static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self, + int prefer_self) +{ + if (count > 0) { + fprintf(fd, "%5d ", count); + if (prefer_self && profile_equal(*total, *self)) { + fprintf(fd, " "); + } else { + fprintf(fd, "%s ", profile_msg(*total)); + } + if (!prefer_self && profile_equal(*total, *self)) { + fprintf(fd, " "); + } else { + fprintf(fd, "%s ", profile_msg(*self)); + } + } else { + fprintf(fd, " "); + } +} + +/// Compare function for total time sorting. +static int prof_total_cmp(const void *s1, const void *s2) +{ + ufunc_T *p1 = *(ufunc_T **)s1; + ufunc_T *p2 = *(ufunc_T **)s2; + return profile_cmp(p1->uf_tm_total, p2->uf_tm_total); +} + +/// Compare function for self time sorting. +static int prof_self_cmp(const void *s1, const void *s2) +{ + ufunc_T *p1 = *(ufunc_T **)s1; + ufunc_T *p2 = *(ufunc_T **)s2; + return profile_cmp(p1->uf_tm_self, p2->uf_tm_self); +} + +/// Return the autoload script name for a function or variable name +/// Caller must make sure that "name" contains AUTOLOAD_CHAR. +/// +/// @param[in] name Variable/function name. +/// @param[in] name_len Name length. +/// +/// @return [allocated] autoload script name. +char *autoload_name(const char *const name, const size_t name_len) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Get the script file name: replace '#' with '/', append ".vim". + char *const scriptname = xmalloc(name_len + sizeof("autoload/.vim")); + memcpy(scriptname, "autoload/", sizeof("autoload/") - 1); + memcpy(scriptname + sizeof("autoload/") - 1, name, name_len); + size_t auchar_idx = 0; + for (size_t i = sizeof("autoload/") - 1; + i - sizeof("autoload/") + 1 < name_len; + i++) { + if (scriptname[i] == AUTOLOAD_CHAR) { + scriptname[i] = '/'; + auchar_idx = i; + } + } + memcpy(scriptname + auchar_idx, ".vim", sizeof(".vim")); + + return scriptname; +} + +/// If name has a package name try autoloading the script for it +/// +/// @param[in] name Variable/function name. +/// @param[in] name_len Name length. +/// @param[in] reload If true, load script again when already loaded. +/// +/// @return true if a package was loaded. +bool script_autoload(const char *const name, const size_t name_len, const bool reload) +{ + // If there is no '#' after name[0] there is no package name. + const char *p = memchr(name, AUTOLOAD_CHAR, name_len); + if (p == NULL || p == name) { + return false; + } + + bool ret = false; + char *tofree = autoload_name(name, name_len); + char *scriptname = tofree; + + // Find the name in the list of previously loaded package names. Skip + // "autoload/", it's always the same. + int i = 0; + for (; i < ga_loaded.ga_len; i++) { + if (STRCMP(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) { + break; + } + } + if (!reload && i < ga_loaded.ga_len) { + ret = false; // Was loaded already. + } else { + // Remember the name if it wasn't loaded already. + if (i == ga_loaded.ga_len) { + GA_APPEND(char *, &ga_loaded, scriptname); + tofree = NULL; + } + + // Try loading the package from $VIMRUNTIME/autoload/<name>.vim + if (source_runtime(scriptname, 0) == OK) { + ret = true; + } + } + + xfree(tofree); + return ret; +} + +/// Called when starting to read a function line. +/// "sourcing_lnum" must be correct! +/// When skipping lines it may not actually be executed, but we won't find out +/// until later and we need to store the time now. +void func_line_start(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && sourcing_lnum >= 1 + && sourcing_lnum <= fp->uf_lines.ga_len) { + fp->uf_tml_idx = sourcing_lnum - 1; + // Skip continuation lines. + while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) { + fp->uf_tml_idx--; + } + fp->uf_tml_execed = false; + fp->uf_tml_start = profile_start(); + fp->uf_tml_children = profile_zero(); + fp->uf_tml_wait = profile_get_wait(); + } +} + +/// Called when actually executing a function line. +void func_line_exec(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) { + fp->uf_tml_execed = TRUE; + } +} + +/// Called when done with a function line. +void func_line_end(void *cookie) +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) { + if (fp->uf_tml_execed) { + ++fp->uf_tml_count[fp->uf_tml_idx]; + fp->uf_tml_start = profile_end(fp->uf_tml_start); + fp->uf_tml_start = profile_sub_wait(fp->uf_tml_wait, fp->uf_tml_start); + fp->uf_tml_total[fp->uf_tml_idx] = + profile_add(fp->uf_tml_total[fp->uf_tml_idx], fp->uf_tml_start); + fp->uf_tml_self[fp->uf_tml_idx] = + profile_self(fp->uf_tml_self[fp->uf_tml_idx], fp->uf_tml_start, + fp->uf_tml_children); + } + fp->uf_tml_idx = -1; + } +} + +static var_flavour_T var_flavour(char *varname) + FUNC_ATTR_PURE +{ + char *p = varname; + + if (ASCII_ISUPPER(*p)) { + while (*(++p)) { + if (ASCII_ISLOWER(*p)) { + return VAR_FLAVOUR_SESSION; + } + } + return VAR_FLAVOUR_SHADA; + } else { + return VAR_FLAVOUR_DEFAULT; + } +} + +/// Iterate over global variables +/// +/// @warning No modifications to global variable dictionary must be performed +/// while iteration is in progress. +/// +/// @param[in] iter Iterator. Pass NULL to start iteration. +/// @param[out] name Variable name. +/// @param[out] rettv Variable value. +/// +/// @return Pointer that needs to be passed to next `var_shada_iter` invocation +/// or NULL to indicate that iteration is over. +const void *var_shada_iter(const void *const iter, const char **const name, typval_T *rettv, + var_flavour_T flavour) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3) +{ + const hashitem_T *hi; + const hashitem_T *hifirst = globvarht.ht_array; + const size_t hinum = (size_t)globvarht.ht_mask + 1; + *name = NULL; + if (iter == NULL) { + hi = globvarht.ht_array; + while ((size_t)(hi - hifirst) < hinum + && (HASHITEM_EMPTY(hi) + || !(var_flavour((char *)hi->hi_key) & flavour))) { + hi++; + } + if ((size_t)(hi - hifirst) == hinum) { + return NULL; + } + } else { + hi = (const hashitem_T *)iter; + } + *name = (char *)TV_DICT_HI2DI(hi)->di_key; + tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); + while ((size_t)(++hi - hifirst) < hinum) { + if (!HASHITEM_EMPTY(hi) && (var_flavour((char *)hi->hi_key) & flavour)) { + return hi; + } + } + return NULL; +} + +void var_set_global(const char *const name, typval_T vartv) +{ + funccal_entry_T funccall_entry; + + save_funccal(&funccall_entry); + set_var(name, strlen(name), &vartv, false); + restore_funccal(); +} + +int store_session_globals(FILE *fd) +{ + TV_DICT_ITER(&globvardict, this_var, { + if ((this_var->di_tv.v_type == VAR_NUMBER + || this_var->di_tv.v_type == VAR_STRING) + && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) { + // Escape special characters with a backslash. Turn a LF and + // CR into \n and \r. + char *const p = (char *)vim_strsave_escaped((const char_u *)tv_get_string(&this_var->di_tv), + (const char_u *)"\\\"\n\r"); + for (char *t = p; *t != NUL; t++) { + if (*t == '\n') { + *t = 'n'; + } else if (*t == '\r') { + *t = 'r'; + } + } + if ((fprintf(fd, "let %s = %c%s%c", + this_var->di_key, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' '), + p, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' ')) < 0) + || put_eol(fd) == FAIL) { + xfree(p); + return FAIL; + } + xfree(p); + } else if (this_var->di_tv.v_type == VAR_FLOAT + && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) { + float_T f = this_var->di_tv.vval.v_float; + int sign = ' '; + + if (f < 0) { + f = -f; + sign = '-'; + } + if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) + || put_eol(fd) == FAIL) { + return FAIL; + } + } + }); + return OK; +} + +/// Display script name where an item was last set. +/// Should only be invoked when 'verbose' is non-zero. +void last_set_msg(sctx_T script_ctx) +{ + const LastSet last_set = (LastSet){ + .script_ctx = script_ctx, + .channel_id = 0, + }; + option_last_set_msg(last_set); +} + +/// Displays where an option was last set. +/// +/// Should only be invoked when 'verbose' is non-zero. +void option_last_set_msg(LastSet last_set) +{ + if (last_set.script_ctx.sc_sid != 0) { + bool should_free; + char *p = (char *)get_scriptname(last_set, &should_free); + verbose_enter(); + msg_puts(_("\n\tLast set from ")); + msg_puts(p); + if (last_set.script_ctx.sc_lnum > 0) { + msg_puts(_(line_msg)); + msg_outnum((long)last_set.script_ctx.sc_lnum); + } + if (should_free) { + xfree(p); + } + verbose_leave(); + } +} + +// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, +// v:option_type, and v:option_command. +void reset_v_option_vars(void) +{ + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1); + set_vim_var_string(VV_OPTION_COMMAND, NULL, -1); + set_vim_var_string(VV_OPTION_TYPE, NULL, -1); +} + +/// Adjust a filename, according to a string of modifiers. +/// *fnamep must be NUL terminated when called. When returning, the length is +/// determined by *fnamelen. +/// Returns VALID_ flags or -1 for failure. +/// When there is an error, *fnamep is set to NULL. +/// +/// @param src string with modifiers +/// @param tilde_file "~" is a file name, not $HOME +/// @param usedlen characters after src that are used +/// @param fnamep file name so far +/// @param bufp buffer for allocated file name or NULL +/// @param fnamelen length of fnamep +int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, char **bufp, + size_t *fnamelen) +{ + int valid = 0; + char *tail; + char *s, *p, *pbuf; + char dirname[MAXPATHL]; + int c; + bool has_fullname = false; + bool has_homerelative = false; + +repeat: + // ":p" - full path/file_name + if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') { + has_fullname = true; + + valid |= VALID_PATH; + *usedlen += 2; + + // Expand "~/path" for all systems and "~user/path" for Unix + if ((*fnamep)[0] == '~' +#if !defined(UNIX) + && ((*fnamep)[1] == '/' +# ifdef BACKSLASH_IN_FILENAME + || (*fnamep)[1] == '\\' +# endif + || (*fnamep)[1] == NUL) +#endif + && !(tilde_file && (*fnamep)[1] == NUL)) { + *fnamep = expand_env_save(*fnamep); + xfree(*bufp); // free any allocated file name + *bufp = *fnamep; + if (*fnamep == NULL) { + return -1; + } + } + + // When "/." or "/.." is used: force expansion to get rid of it. + for (p = *fnamep; *p != NUL; MB_PTR_ADV(p)) { + if (vim_ispathsep(*p) + && p[1] == '.' + && (p[2] == NUL + || vim_ispathsep(p[2]) + || (p[2] == '.' + && (p[3] == NUL || vim_ispathsep(p[3]))))) { + break; + } + } + + // FullName_save() is slow, don't use it when not needed. + if (*p != NUL || !vim_isAbsName((char_u *)(*fnamep))) { + *fnamep = FullName_save((*fnamep), *p != NUL); + xfree(*bufp); // free any allocated file name + *bufp = *fnamep; + if (*fnamep == NULL) { + return -1; + } + } + + // Append a path separator to a directory. + if (os_isdir((char_u *)(*fnamep))) { + // Make room for one or two extra characters. + *fnamep = xstrnsave(*fnamep, STRLEN(*fnamep) + 2); + xfree(*bufp); // free any allocated file name + *bufp = *fnamep; + add_pathsep(*fnamep); + } + } + + // ":." - path relative to the current directory + // ":~" - path relative to the home directory + // ":8" - shortname path - postponed till after + while (src[*usedlen] == ':' + && ((c = (char_u)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) { + *usedlen += 2; + if (c == '8') { + continue; + } + pbuf = NULL; + // Need full path first (use expand_env() to remove a "~/") + if (!has_fullname && !has_homerelative) { + if (**fnamep == '~') { + p = pbuf = expand_env_save(*fnamep); + } else { + p = pbuf = FullName_save(*fnamep, false); + } + } else { + p = *fnamep; + } + + has_fullname = false; + + if (p != NULL) { + if (c == '.') { + os_dirname((char_u *)dirname, MAXPATHL); + if (has_homerelative) { + s = xstrdup(dirname); + home_replace(NULL, s, dirname, MAXPATHL, true); + xfree(s); + } + size_t namelen = STRLEN(dirname); + + // Do not call shorten_fname() here since it removes the prefix + // even though the path does not have a prefix. + if (FNAMENCMP(p, dirname, namelen) == 0) { + p += namelen; + if (vim_ispathsep(*p)) { + while (*p && vim_ispathsep(*p)) { + p++; + } + *fnamep = p; + if (pbuf != NULL) { + // free any allocated file name + xfree(*bufp); + *bufp = pbuf; + pbuf = NULL; + } + } + } + } else { + home_replace(NULL, p, dirname, MAXPATHL, true); + // Only replace it when it starts with '~' + if (*dirname == '~') { + s = xstrdup(dirname); + *fnamep = s; + xfree(*bufp); + *bufp = s; + has_homerelative = true; + } + } + xfree(pbuf); + } + } + + tail = path_tail(*fnamep); + *fnamelen = STRLEN(*fnamep); + + // ":h" - head, remove "/file_name", can be repeated + // Don't remove the first "/" or "c:\" + while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') { + valid |= VALID_HEAD; + *usedlen += 2; + s = (char *)get_past_head((char_u *)(*fnamep)); + while (tail > s && after_pathsep(s, tail)) { + MB_PTR_BACK(*fnamep, tail); + } + *fnamelen = (size_t)(tail - *fnamep); + if (*fnamelen == 0) { + // Result is empty. Turn it into "." to make ":cd %:h" work. + xfree(*bufp); + *bufp = *fnamep = tail = xstrdup("."); + *fnamelen = 1; + } else { + while (tail > s && !after_pathsep(s, tail)) { + MB_PTR_BACK(*fnamep, tail); + } + } + } + + // ":8" - shortname + if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') { + *usedlen += 2; + } + + // ":t" - tail, just the basename + if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') { + *usedlen += 2; + *fnamelen -= (size_t)(tail - *fnamep); + *fnamep = tail; + } + + // ":e" - extension, can be repeated + // ":r" - root, without extension, can be repeated + while (src[*usedlen] == ':' + && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) { + /* find a '.' in the tail: + * - for second :e: before the current fname + * - otherwise: The last '.' + */ + const bool is_second_e = *fnamep > tail; + if (src[*usedlen + 1] == 'e' && is_second_e) { + s = (*fnamep) - 2; + } else { + s = (*fnamep) + *fnamelen - 1; + } + + for (; s > tail; s--) { + if (s[0] == '.') { + break; + } + } + if (src[*usedlen + 1] == 'e') { + if (s > tail || (0 && is_second_e && s == tail)) { + // we stopped at a '.' (so anchor to &'.' + 1) + char *newstart = s + 1; + size_t distance_stepped_back = (size_t)(*fnamep - newstart); + *fnamelen += distance_stepped_back; + *fnamep = newstart; + } else if (*fnamep <= tail) { + *fnamelen = 0; + } + } else { + // :r - Remove one extension + // + // Ensure that `s` doesn't go before `*fnamep`, + // since then we're taking too many roots: + // + // "path/to/this.file.ext" :e:e:r:r + // ^ ^-------- *fnamep + // +------------- tail + // + // Also ensure `s` doesn't go before `tail`, + // since then we're taking too many roots again: + // + // "path/to/this.file.ext" :r:r:r + // ^ ^------------- tail + // +--------------------- *fnamep + if (s > MAX(tail, (char *)(*fnamep))) { + *fnamelen = (size_t)(s - *fnamep); + } + } + *usedlen += 2; + } + + // ":s?pat?foo?" - substitute + // ":gs?pat?foo?" - global substitute + if (src[*usedlen] == ':' + && (src[*usedlen + 1] == 's' + || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) { + int sep; + char *flags; + int didit = false; + + flags = ""; + s = src + *usedlen + 2; + if (src[*usedlen + 1] == 'g') { + flags = "g"; + s++; + } + + sep = (char_u)(*s++); + if (sep) { + // find end of pattern + p = vim_strchr(s, sep); + if (p != NULL) { + char *const pat = xstrnsave(s, (size_t)(p - s)); + s = p + 1; + // find end of substitution + p = vim_strchr(s, sep); + if (p != NULL) { + char *const sub = xstrnsave(s, (size_t)(p - s)); + char *const str = xstrnsave(*fnamep, *fnamelen); + *usedlen = (size_t)(p + 1 - src); + s = do_string_sub(str, pat, sub, NULL, flags); + *fnamep = s; + *fnamelen = STRLEN(s); + xfree(*bufp); + *bufp = s; + didit = TRUE; + xfree(sub); + xfree(str); + } + xfree(pat); + } + // after using ":s", repeat all the modifiers + if (didit) { + goto repeat; + } + } + } + + if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') { + // vim_strsave_shellescape() needs a NUL terminated string. + c = (char_u)(*fnamep)[*fnamelen]; + if (c != NUL) { + (*fnamep)[*fnamelen] = NUL; + } + p = (char *)vim_strsave_shellescape((char_u *)(*fnamep), false, false); + if (c != NUL) { + (*fnamep)[*fnamelen] = (char)c; + } + xfree(*bufp); + *bufp = *fnamep = p; + *fnamelen = STRLEN(p); + *usedlen += 2; + } + + return valid; +} + +/// Perform a substitution on "str" with pattern "pat" and substitute "sub". +/// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL. +/// "flags" can be "g" to do a global substitute. +/// +/// @return an allocated string, NULL for error. +char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags) +{ + int sublen; + regmatch_T regmatch; + int do_all; + char *tail; + char *end; + garray_T ga; + char *save_cpo; + char *zero_width = NULL; + + // Make 'cpoptions' empty, so that the 'l' flag doesn't work here + save_cpo = p_cpo; + p_cpo = (char *)empty_option; + + ga_init(&ga, 1, 200); + + do_all = (flags[0] == 'g'); + + regmatch.rm_ic = p_ic; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog != NULL) { + tail = str; + end = str + STRLEN(str); + while (vim_regexec_nl(®match, (char_u *)str, (colnr_T)(tail - str))) { + // Skip empty match except for first match. + if (regmatch.startp[0] == regmatch.endp[0]) { + if ((char_u *)zero_width == regmatch.startp[0]) { + // avoid getting stuck on a match with an empty string + int i = utfc_ptr2len(tail); + memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + ga.ga_len += i; + tail += i; + continue; + } + zero_width = (char *)regmatch.startp[0]; + } + + // Get some space for a temporary buffer to do the substitution + // into. It will contain: + // - The text up to where the match is. + // - The substituted text. + // - The text after the match. + sublen = vim_regsub(®match, (char_u *)sub, expr, (char_u *)tail, 0, REGSUB_MAGIC); + ga_grow(&ga, (int)((end - tail) + sublen - + (regmatch.endp[0] - regmatch.startp[0]))); + + // copy the text up to where the match is + int i = (int)(regmatch.startp[0] - (char_u *)tail); + memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + // add the substituted text + (void)vim_regsub(®match, (char_u *)sub, expr, + (char_u *)ga.ga_data + ga.ga_len + i, sublen, + REGSUB_COPY | REGSUB_MAGIC); + ga.ga_len += i + sublen - 1; + tail = (char *)regmatch.endp[0]; + if (*tail == NUL) { + break; + } + if (!do_all) { + break; + } + } + + if (ga.ga_data != NULL) { + STRCPY((char *)ga.ga_data + ga.ga_len, tail); + } + + vim_regfree(regmatch.regprog); + } + + char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data); + ga_clear(&ga); + if ((char_u *)p_cpo == empty_option) { + p_cpo = save_cpo; + } else { + // Darn, evaluating {sub} expression or {expr} changed the value. + free_string_option((char_u *)save_cpo); + } + + return ret; +} + +/// common code for getting job callbacks for jobstart, termopen and rpcstart +/// +/// @return true/false on success/failure. +bool common_job_callbacks(dict_T *vopts, CallbackReader *on_stdout, CallbackReader *on_stderr, + Callback *on_exit) +{ + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), &on_stdout->cb) + && tv_dict_get_callback(vopts, S_LEN("on_stderr"), &on_stderr->cb) + && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { + on_stdout->buffered = tv_dict_get_number(vopts, "stdout_buffered"); + on_stderr->buffered = tv_dict_get_number(vopts, "stderr_buffered"); + if (on_stdout->buffered && on_stdout->cb.type == kCallbackNone) { + on_stdout->self = vopts; + } + if (on_stderr->buffered && on_stderr->cb.type == kCallbackNone) { + on_stderr->self = vopts; + } + vopts->dv_refcount++; + return true; + } + + callback_reader_free(on_stdout); + callback_reader_free(on_stderr); + callback_free(on_exit); + return false; +} + +Channel *find_job(uint64_t id, bool show_error) +{ + Channel *data = find_channel(id); + if (!data || data->streamtype != kChannelStreamProc + || process_is_stopped(&data->stream.proc)) { + if (show_error) { + if (data && data->streamtype != kChannelStreamProc) { + emsg(_(e_invchanjob)); + } else { + emsg(_(e_invchan)); + } + } + return NULL; + } + return data; +} + +void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) +{ + if (check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_STRING) { + emsg(_(e_invarg)); + return; + } + + list_T *args = tv_list_alloc(1); + tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); + *rettv = eval_call_provider(name, "eval", args, false); +} + +/// @param discard Clears the value returned by the provider and returns +/// an empty typval_T. +typval_T eval_call_provider(char *provider, char *method, list_T *arguments, bool discard) +{ + if (!eval_has_provider(provider)) { + semsg("E319: No \"%s\" provider found. Run \":checkhealth provider\"", + provider); + return (typval_T){ + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = (varnumber_T)0 + }; + } + + char func[256]; + int name_len = snprintf(func, sizeof(func), "provider#%s#Call", provider); + + // Save caller scope information + struct caller_scope saved_provider_caller_scope = provider_caller_scope; + provider_caller_scope = (struct caller_scope) { + .script_ctx = current_sctx, + .sourcing_name = sourcing_name, + .sourcing_lnum = sourcing_lnum, + .autocmd_fname = autocmd_fname, + .autocmd_match = autocmd_match, + .autocmd_bufnr = autocmd_bufnr, + .funccalp = (void *)get_current_funccal() + }; + funccal_entry_T funccal_entry; + save_funccal(&funccal_entry); + provider_call_nesting++; + + typval_T argvars[3] = { + { .v_type = VAR_STRING, .vval.v_string = method, + .v_lock = VAR_UNLOCKED }, + { .v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = VAR_UNLOCKED }, + { .v_type = VAR_UNKNOWN } + }; + typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; + tv_list_ref(arguments); + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func(func, name_len, &rettv, 2, argvars, &funcexe); + + tv_list_unref(arguments); + // Restore caller scope information + restore_funccal(); + provider_caller_scope = saved_provider_caller_scope; + provider_call_nesting--; + assert(provider_call_nesting >= 0); + + if (discard) { + tv_clear(&rettv); + } + + return rettv; +} + +/// Checks if provider for feature `feat` is enabled. +bool eval_has_provider(const char *feat) +{ + if (!strequal(feat, "clipboard") + && !strequal(feat, "python3") + && !strequal(feat, "python3_compiled") + && !strequal(feat, "python3_dynamic") + && !strequal(feat, "perl") + && !strequal(feat, "ruby") + && !strequal(feat, "node")) { + // Avoid autoload for non-provider has() features. + return false; + } + + char name[32]; // Normalized: "python_compiled" => "python". + snprintf(name, sizeof(name), "%s", feat); + strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. + + char buf[256]; + typval_T tv; + // Get the g:loaded_xx_provider variable. + int len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name); + if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) { + // Trigger autoload once. + len = snprintf(buf, sizeof(buf), "provider#%s#bogus", name); + script_autoload(buf, (size_t)len, false); + + // Retry the (non-autoload-style) variable. + len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name); + if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) { + // Show a hint if Call() is defined but g:loaded_xx_provider is missing. + snprintf(buf, sizeof(buf), "provider#%s#Call", name); + if (!!find_func((char_u *)buf) && p_lpl) { + semsg("provider: %s: missing required variable g:loaded_%s_provider", + name, name); + } + return false; + } + } + + bool ok = (tv.v_type == VAR_NUMBER) + ? 2 == tv.vval.v_number // Value of 2 means "loaded and working". + : false; + + if (ok) { + // Call() must be defined if provider claims to be working. + snprintf(buf, sizeof(buf), "provider#%s#Call", name); + if (!find_func((char_u *)buf)) { + semsg("provider: %s: g:loaded_%s_provider=2 but %s is not defined", + name, name, buf); + ok = false; + } + } + + return ok; +} + +/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`. +void eval_fmt_source_name_line(char *buf, size_t bufsize) +{ + if (sourcing_name) { + snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum); + } else { + snprintf(buf, bufsize, "?"); + } +} + +/// ":checkhealth [plugins]" +void ex_checkhealth(exarg_T *eap) +{ + bool found = !!find_func((char_u *)"health#check"); + if (!found + && script_autoload("health#check", sizeof("health#check") - 1, false)) { + found = !!find_func((char_u *)"health#check"); + } + if (!found) { + const char *vimruntime_env = os_getenv("VIMRUNTIME"); + if (vimruntime_env == NULL) { + emsg(_("E5009: $VIMRUNTIME is empty or unset")); + } else { + bool rtp_ok = NULL != strstr((char *)p_rtp, vimruntime_env); + if (rtp_ok) { + semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); + } else { + emsg(_("E5009: Invalid 'runtimepath'")); + } + } + return; + } + + size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')"); + char *buf = xmalloc(bufsize); + snprintf(buf, bufsize, "call health#check('%s')", eap->arg); + + do_cmdline_cmd(buf); + + xfree(buf); +} + +void invoke_prompt_callback(void) +{ + typval_T rettv; + typval_T argv[2]; + char *text; + char *prompt; + linenr_T lnum = curbuf->b_ml.ml_line_count; + + // Add a new line for the prompt before invoking the callback, so that + // text can always be inserted above the last line. + ml_append(lnum, "", 0, false); + curwin->w_cursor.lnum = lnum + 1; + curwin->w_cursor.col = 0; + + if (curbuf->b_prompt_callback.type == kCallbackNone) { + return; + } + text = (char *)ml_get(lnum); + prompt = (char *)prompt_text(); + if (STRLEN(text) >= STRLEN(prompt)) { + text += STRLEN(prompt); + } + argv[0].v_type = VAR_STRING; + argv[0].vval.v_string = xstrdup(text); + argv[1].v_type = VAR_UNKNOWN; + + callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv); + tv_clear(&argv[0]); + tv_clear(&rettv); +} + +/// @return true when the interrupt callback was invoked. +bool invoke_prompt_interrupt(void) +{ + typval_T rettv; + typval_T argv[1]; + + if (curbuf->b_prompt_interrupt.type == kCallbackNone) { + return false; + } + argv[0].v_type = VAR_UNKNOWN; + + got_int = false; // don't skip executing commands + callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv); + tv_clear(&rettv); + return true; +} + +/// Compare "typ1" and "typ2". Put the result in "typ1". +/// +/// @param typ1 first operand +/// @param typ2 second operand +/// @param type operator +/// @param ic ignore case +int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) + FUNC_ATTR_NONNULL_ALL +{ + varnumber_T n1, n2; + const bool type_is = type == EXPR_IS || type == EXPR_ISNOT; + + if (type_is && typ1->v_type != typ2->v_type) { + // For "is" a different type always means false, for "notis" + // it means true. + n1 = type == EXPR_ISNOT; + } else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_blob == typ2->vval.v_blob; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + emsg(_("E977: Can only compare Blob with Blob")); + } else { + emsg(_(e_invalblob)); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Blobs for being equal or unequal. + n1 = tv_blob_equal(typ1->vval.v_blob, typ2->vval.v_blob); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_list == typ2->vval.v_list; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + emsg(_("E691: Can only compare List with List")); + } else { + emsg(_("E692: Invalid operation for List")); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Lists for being equal or unequal. + n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_dict == typ2->vval.v_dict; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + emsg(_("E735: Can only compare Dictionary with Dictionary")); + } else { + emsg(_("E736: Invalid operation for Dictionary")); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Dictionaries for being equal or unequal. + n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (tv_is_func(*typ1) || tv_is_func(*typ2)) { + if (type != EXPR_EQUAL && type != EXPR_NEQUAL + && type != EXPR_IS && type != EXPR_ISNOT) { + emsg(_("E694: Invalid operation for Funcrefs")); + tv_clear(typ1); + return FAIL; + } + if ((typ1->v_type == VAR_PARTIAL && typ1->vval.v_partial == NULL) + || (typ2->v_type == VAR_PARTIAL && typ2->vval.v_partial == NULL)) { + // when a partial is NULL assume not equal + n1 = false; + } else if (type_is) { + if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) { + // strings are considered the same if their value is + // the same + n1 = tv_equal(typ1, typ2, ic, false); + } else if (typ1->v_type == VAR_PARTIAL && typ2->v_type == VAR_PARTIAL) { + n1 = typ1->vval.v_partial == typ2->vval.v_partial; + } else { + n1 = false; + } + } else { + n1 = tv_equal(typ1, typ2, ic, false); + } + if (type == EXPR_NEQUAL || type == EXPR_ISNOT) { + n1 = !n1; + } + } else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT) + && type != EXPR_MATCH && type != EXPR_NOMATCH) { + // If one of the two variables is a float, compare as a float. + // When using "=~" or "!~", always compare as string. + const float_T f1 = tv_get_float(typ1); + const float_T f2 = tv_get_float(typ2); + n1 = false; + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: + n1 = f1 == f2; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: + n1 = f1 != f2; break; + case EXPR_GREATER: + n1 = f1 > f2; break; + case EXPR_GEQUAL: + n1 = f1 >= f2; break; + case EXPR_SMALLER: + n1 = f1 < f2; break; + case EXPR_SEQUAL: + n1 = f1 <= f2; break; + case EXPR_UNKNOWN: + case EXPR_MATCH: + case EXPR_NOMATCH: + break; // avoid gcc warning + } + } else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER) + && type != EXPR_MATCH && type != EXPR_NOMATCH) { + // If one of the two variables is a number, compare as a number. + // When using "=~" or "!~", always compare as string. + n1 = tv_get_number(typ1); + n2 = tv_get_number(typ2); + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: + n1 = n1 == n2; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: + n1 = n1 != n2; break; + case EXPR_GREATER: + n1 = n1 > n2; break; + case EXPR_GEQUAL: + n1 = n1 >= n2; break; + case EXPR_SMALLER: + n1 = n1 < n2; break; + case EXPR_SEQUAL: + n1 = n1 <= n2; break; + case EXPR_UNKNOWN: + case EXPR_MATCH: + case EXPR_NOMATCH: + break; // avoid gcc warning + } + } else { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const s1 = tv_get_string_buf(typ1, buf1); + const char *const s2 = tv_get_string_buf(typ2, buf2); + int i; + if (type != EXPR_MATCH && type != EXPR_NOMATCH) { + i = mb_strcmp_ic(ic, s1, s2); + } else { + i = 0; + } + n1 = false; + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: + n1 = i == 0; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: + n1 = i != 0; break; + case EXPR_GREATER: + n1 = i > 0; break; + case EXPR_GEQUAL: + n1 = i >= 0; break; + case EXPR_SMALLER: + n1 = i < 0; break; + case EXPR_SEQUAL: + n1 = i <= 0; break; + + case EXPR_MATCH: + case EXPR_NOMATCH: + n1 = pattern_match((char *)s2, (char *)s1, ic); + if (type == EXPR_NOMATCH) { + n1 = !n1; + } + break; + case EXPR_UNKNOWN: + break; // avoid gcc warning + } + } + tv_clear(typ1); + typ1->v_type = VAR_NUMBER; + typ1->vval.v_number = n1; + return OK; +} + +char *typval_tostring(typval_T *arg) +{ + if (arg == NULL) { + return xstrdup("(does not exist)"); + } + return encode_tv2string(arg, NULL); +} diff --git a/src/nvim/eval.h b/src/nvim/eval.h index fa02b1ea0f..7dbd18737a 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -3,7 +3,6 @@ #include "nvim/buffer_defs.h" #include "nvim/channel.h" -#include "nvim/eval/funcs.h" // For FunPtr #include "nvim/event/time.h" // For TimeWatcher #include "nvim/ex_cmds_defs.h" // For exarg_T #include "nvim/os/fileio.h" // For FileDescriptor diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 3db0d27018..c8eb0334fa 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -250,6 +250,7 @@ return { map={args=2, base=1}, maparg={args={1, 4}, base=1}, mapcheck={args={1, 3}, base=1}, + mapset={args=3, base=1}, match={args={2, 4}, base=1}, matchadd={args={2, 5}, base=1}, matchaddpos={args={2, 5}, base=1}, diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 3e66150180..b461456a3a 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -12,8 +12,6 @@ # include "eval/executor.c.generated.h" #endif -static char *e_letwrong = N_("E734: Wrong variable type for %s="); - char *e_listidx = N_("E684: list index out of range: %" PRId64); /// Handle tv1 += tv2, -=, *=, /=, %=, .= diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 7bed21e99b..691ccfe535 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -25,6 +25,7 @@ #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" @@ -36,6 +37,7 @@ #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mapping.h" @@ -213,12 +215,11 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T } typval_T argv[MAX_FUNC_ARGS + 1]; - const ptrdiff_t base_index - = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; - memcpy(argv, argvars, base_index * sizeof(typval_T)); + const ptrdiff_t base_index = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; + memcpy(argv, argvars, (size_t)base_index * sizeof(typval_T)); argv[base_index] = *basetv; memcpy(argv + base_index + 1, argvars + base_index, - (argcount - base_index) * sizeof(typval_T)); + (size_t)(argcount - base_index) * sizeof(typval_T)); argv[argcount + 1].v_type = VAR_UNKNOWN; fdef->func(argv, rettv, fdef->data); @@ -326,7 +327,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) const varnumber_T n = tv_get_number_chk(&argvars[1], &error); if (!error) { - ga_append(&b->bv_ga, (int)n); + ga_append(&b->bv_ga, (char)n); tv_copy(&argvars[0], rettv); } } @@ -430,7 +431,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - int idx = tv_get_number_chk(&argvars[0], NULL); + int idx = (int)tv_get_number_chk(&argvars[0], NULL); if (arglist != NULL && idx >= 0 && idx < argcount) { rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx])); } else if (idx == -1) { @@ -831,7 +832,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } const char *error; - rettv->vval.v_number = channel_close(argvars[0].vval.v_number, part, &error); + rettv->vval.v_number = channel_close((uint64_t)argvars[0].vval.v_number, part, &error); if (!rettv->vval.v_number) { emsg(error); } @@ -859,7 +860,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) const blob_T *const b = argvars[1].vval.v_blob; input_len = tv_blob_len(b); if (input_len > 0) { - input = xmemdup(b->bv_ga.ga_data, input_len); + input = xmemdup(b->bv_ga.ga_data, (size_t)input_len); } } else { input = save_tv_as_string(&argvars[1], &input_len, false); @@ -870,9 +871,9 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) // or there is no input to send. return; } - uint64_t id = argvars[0].vval.v_number; + uint64_t id = (uint64_t)argvars[0].vval.v_number; const char *error = NULL; - rettv->vval.v_number = channel_send(id, input, input_len, true, &error); + rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error); if (error) { emsg(error); } @@ -1054,64 +1055,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_col(argvars, rettv, false); } -/// "complete()" function -static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if ((State & MODE_INSERT) == 0) { - emsg(_("E785: complete() can only be used in Insert mode")); - return; - } - - // Check for undo allowed here, because if something was already inserted - // the line was already saved for undo and this check isn't done. - if (!undo_allowed(curbuf)) { - return; - } - - if (argvars[1].v_type != VAR_LIST) { - emsg(_(e_invarg)); - } else { - const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL); - if (startcol > 0) { - set_completion(startcol - 1, argvars[1].vval.v_list); - } - } -} - -/// "complete_add()" function -static void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false); -} - -/// "complete_check()" function -static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int saved = RedrawingDisabled; - - RedrawingDisabled = 0; - ins_compl_check_keys(0, true); - rettv->vval.v_number = compl_interrupted; - RedrawingDisabled = saved; -} - -/// "complete_info()" function -static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_dict_alloc_ret(rettv); - - list_T *what_list = NULL; - - if (argvars[0].v_type != VAR_UNKNOWN) { - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - what_list = argvars[0].vval.v_list; - } - get_complete_info(what_list, rettv->vval.v_dict); -} - /// "confirm(message, buttons[, default [, type]])" function static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -1134,7 +1077,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) error = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - def = tv_get_number_chk(&argvars[2], &error); + def = (int)tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { typestr = tv_get_string_buf_chk(&argvars[3], buf2); if (typestr == NULL) { @@ -1181,7 +1124,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { - ic = tv_get_number_chk(&argvars[2], &error); + ic = (int)tv_get_number_chk(&argvars[2], &error); } if (argvars[0].v_type == VAR_STRING) { @@ -1219,7 +1162,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[3].v_type != VAR_UNKNOWN) { idx = tv_get_number_chk(&argvars[3], &error); if (!error) { - li = tv_list_find(l, idx); + li = tv_list_find(l, (int)idx); if (li == NULL) { semsg(_(e_listidx), (int64_t)idx); } @@ -1292,7 +1235,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { size_t index = 0; if (argvars[0].v_type == VAR_NUMBER) { - index = argvars[0].vval.v_number; + index = (size_t)argvars[0].vval.v_number; } else if (argvars[0].v_type != VAR_UNKNOWN) { semsg(_(e_invarg2), "expected nothing or a Number as an argument"); return; @@ -1360,7 +1303,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) size_t index = 0; if (argvars[1].v_type == VAR_NUMBER) { - index = argvars[1].vval.v_number; + index = (size_t)argvars[1].vval.v_number; } else if (argvars[1].v_type != VAR_UNKNOWN) { semsg(_(e_invarg2), "expected nothing or a Number as second argument"); return; @@ -1394,7 +1337,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = ctx_size(); + rettv->vval.v_number = (varnumber_T)ctx_size(); } /// Set the cursor position. @@ -1428,7 +1371,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) line = tv_get_lnum(argvars); col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) { - col = buf_charidx_to_byteidx(curbuf, line, col) + 1; + col = buf_charidx_to_byteidx(curbuf, (linenr_T)line, (int)col) + 1; } if (argvars[2].v_type != VAR_UNKNOWN) { coladd = (long)tv_get_number_chk(&argvars[2], NULL); @@ -1441,12 +1384,12 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) return; // type error; errmsg already given } if (line > 0) { - curwin->w_cursor.lnum = line; + curwin->w_cursor.lnum = (linenr_T)line; } if (col > 0) { - curwin->w_cursor.col = col - 1; + curwin->w_cursor.col = (colnr_T)col - 1; } - curwin->w_cursor.coladd = coladd; + curwin->w_cursor.coladd = (colnr_T)coladd; // Make sure the cursor is in a valid position. check_cursor(); @@ -1498,7 +1441,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) int noref = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - noref = tv_get_number_chk(&argvars[1], NULL); + noref = (int)tv_get_number_chk(&argvars[1], NULL); } if (noref < 0 || noref > 1) { emsg(_(e_invarg)); @@ -1675,7 +1618,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { if (wp->w_cursor.lnum > last) { - wp->w_cursor.lnum -= count; + wp->w_cursor.lnum -= (linenr_T)count; } else if (wp->w_cursor.lnum > first) { wp->w_cursor.lnum = first; } @@ -1712,7 +1655,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; - static int changedtick = 0; + static varnumber_T changedtick = 0; static int fnum = 0; static int change_start = 0; static int change_end = 0; @@ -1749,7 +1692,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. + col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. if (col >= change_start && col <= change_end) { hlID = HLF_TXD; // Changed text. } else { @@ -1820,7 +1763,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) os_copy_fullenv(env, env_size); - for (ssize_t i = env_size - 1; i >= 0; i--) { + for (ssize_t i = (ssize_t)env_size - 1; i >= 0; i--) { const char *str = env[i]; const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), '='); @@ -1848,9 +1791,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(key); continue; } - tv_dict_add_str(rettv->vval.v_dict, - key, len, - value); + tv_dict_add_str(rettv->vval.v_dict, key, (size_t)len, value); xfree(key); } os_free_fullenv(env); @@ -2029,7 +1970,7 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - int id = tv_get_number(argvars); + int id = (int)tv_get_number(argvars); tabpage_T *tp; win_T *wp = win_id2wp_tp(id, &tp); if (wp != NULL && tp != NULL) { @@ -2189,11 +2130,11 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *errormsg = NULL; rettv->v_type = VAR_STRING; - char_u *cmdstr = (char_u *)xstrdup(tv_get_string(&argvars[0])); + char *cmdstr = xstrdup(tv_get_string(&argvars[0])); exarg_T eap = { - .cmd = (char *)cmdstr, - .arg = (char *)cmdstr, + .cmd = cmdstr, + .arg = cmdstr, .usefilter = false, .nextcmd = NULL, .cmdidx = CMD_USER, @@ -2204,7 +2145,7 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (errormsg != NULL && *errormsg != NUL) { emsg(errormsg); } - rettv->vval.v_string = (char *)cmdstr; + rettv->vval.v_string = cmdstr; } /// "flatten(list[, {maxdepth}])" function @@ -2265,7 +2206,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (before == tv_list_len(l1)) { item = NULL; } else { - item = tv_list_find(l1, before); + item = tv_list_find(l1, (int)before); if (item == NULL) { semsg(_(e_listidx), (int64_t)before); return; @@ -2382,7 +2323,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } if (argvars[2].v_type != VAR_UNKNOWN) { - count = tv_get_number_chk(&argvars[2], &error); + count = (int)tv_get_number_chk(&argvars[2], &error); } } } @@ -2577,7 +2518,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - unsigned long count = (unsigned long)foldend - foldstart + 1; + unsigned long count = (unsigned long)foldend - (unsigned long)foldstart + 1; txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count); r = xmalloc(STRLEN(txt) + STRLEN(dashes) // for %s @@ -2613,7 +2554,7 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) foldinfo_T info = fold_info(curwin, lnum); if (info.fi_lines > 0) { - text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf); + text = get_foldtext(curwin, lnum, lnum + (linenr_T)info.fi_lines - 1, info, buf); if (text == buf) { text = vim_strsave(text); } @@ -2661,7 +2602,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_BLOB) { bool error = false; - int idx = tv_get_number_chk(&argvars[1], &error); + int idx = (int)tv_get_number_chk(&argvars[1], &error); if (!error) { rettv->v_type = VAR_NUMBER; @@ -2679,7 +2620,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; - li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); + li = tv_list_find(l, (int)tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { tv = TV_LIST_ITEM_TV(li); } @@ -2860,66 +2801,6 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_buffer_lines(buf, lnum, end, true, rettv); } -/// "getbufvar()" function -static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (!tv_check_str_or_nr(&argvars[0])) { - goto f_getbufvar_end; - } - - const char *varname = tv_get_string_chk(&argvars[1]); - emsg_off++; - buf_T *const buf = tv_get_buf(&argvars[0], false); - - if (buf != NULL && varname != NULL) { - if (*varname == '&') { // buffer-local-option - buf_T *const save_curbuf = curbuf; - - // set curbuf to be our buf, temporarily - curbuf = buf; - - if (varname[1] == NUL) { - // get all buffer-local options in a dict - dict_T *opts = get_winbuf_options(true); - - if (opts != NULL) { - tv_dict_set_ret(rettv, opts); - done = true; - } - } else if (get_option_tv(&varname, rettv, true) == OK) { - // buffer-local-option - done = true; - } - - // restore previous notion of curbuf - curbuf = save_curbuf; - } else { - // Look up the variable. - // Let getbufvar({nr}, "") return the "b:" dictionary. - dictitem_T *const v = *varname == NUL - ? (dictitem_T *)&buf->b_bufvar - : find_var_in_ht(&buf->b_vars->dv_hashtab, 'b', - varname, strlen(varname), false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - } - emsg_off--; - -f_getbufvar_end: - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - // use the default value - tv_copy(&argvars[2], rettv); - } -} - /// "getchangelist()" function static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -2929,7 +2810,7 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_UNKNOWN) { buf = curbuf; } else { - vim_ignored = tv_get_number(&argvars[0]); // issue errmsg if type error + vim_ignored = (int)tv_get_number(&argvars[0]); // issue errmsg if type error emsg_off++; buf = tv_get_buf(&argvars[0], false); emsg_off--; @@ -2940,13 +2821,23 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l = tv_list_alloc(buf->b_changelistlen); tv_list_append_list(rettv->vval.v_list, l); - // The current window change list index tracks only the position in the - // current buffer change list. For other buffers, use the change list - // length as the current index. - tv_list_append_number(rettv->vval.v_list, - (buf == curwin->w_buffer) - ? curwin->w_changelistidx - : buf->b_changelistlen); + // The current window change list index tracks only the position for the + // current buffer. For other buffers use the stored index for the current + // window, or, if that's not available, the change list length. + int changelistindex; + if (buf == curwin->w_buffer) { + changelistindex = curwin->w_changelistidx; + } else { + wininfo_T *wip; + + FOR_ALL_BUF_WININFO(buf, wip) { + if (wip->wi_win == curwin) { + break; + } + } + changelistindex = wip != NULL ? wip->wi_changelistidx : buf->b_changelistlen; + } + tv_list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex); for (int i = 0; i < buf->b_changelistlen; i++) { if (buf->b_changelist[i].mark.lnum == 0) { @@ -3008,6 +2899,11 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) no_mapping--; allow_keys--; + if (!ui_has_messages()) { + // redraw the screen after getchar() + update_screen(CLEAR); + } + set_vim_var_nr(VV_MOUSE_WIN, 0); set_vim_var_nr(VV_MOUSE_WINID, 0); set_vim_var_nr(VV_MOUSE_LNUM, 0); @@ -3022,21 +2918,21 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (mod_mask != 0) { temp[i++] = K_SPECIAL; temp[i++] = KS_MODIFIER; - temp[i++] = mod_mask; + temp[i++] = (char_u)mod_mask; } if (IS_SPECIAL(n)) { temp[i++] = K_SPECIAL; - temp[i++] = K_SECOND(n); + temp[i++] = (char_u)K_SECOND(n); temp[i++] = K_THIRD(n); } else { - i += utf_char2bytes(n, (char *)temp + i); + i += utf_char2bytes((int)n, (char *)temp + i); } assert(i < 10); temp[i++] = NUL; rettv->v_type = VAR_STRING; rettv->vval.v_string = (char *)vim_strsave(temp); - if (is_mouse_key(n)) { + if (is_mouse_key((int)n)) { int row = mouse_row; int col = mouse_col; int grid = mouse_grid; @@ -3081,7 +2977,7 @@ static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int i = 0; if (n != 0) { - i += utf_char2bytes(n, (char *)temp); + i += utf_char2bytes((int)n, (char *)temp); } assert(i < 7); temp[i++] = NUL; @@ -3200,7 +3096,7 @@ static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = get_cmdline_type(); + rettv->vval.v_string[0] = (char)get_cmdline_type(); } /// "getcmdwintype()" function @@ -3209,7 +3105,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = cmdwin_type; + rettv->vval.v_string[0] = (char)cmdwin_type; } /// "getcompletion()" function @@ -3249,7 +3145,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strcmp(type, "cmdline") == 0) { set_one_cmd_context(&xpc, pattern); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); - xpc.xp_col = STRLEN(pattern); + xpc.xp_col = (int)STRLEN(pattern); goto theend; } @@ -3330,7 +3226,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } - scope_number[i] = argvars[i].vval.v_number; + scope_number[i] = (int)argvars[i].vval.v_number; // It is an error for the scope number to be less than `-1`. if (scope_number[i] < -1) { emsg(_(e_invarg)); @@ -3430,7 +3326,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { if (file_perm & (1 << (8 - i))) { - perm[i] = flags[i % 3]; + perm[i] = (char)flags[i % 3]; } } } @@ -3764,50 +3660,6 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "gettabvar()" function -static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - const char *const varname = tv_get_string_chk(&argvars[1]); - tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - if (tp != NULL && varname != NULL) { - // Set tp to be our tabpage, temporarily. Also set the window to the - // first window in the tabpage, otherwise the window is not valid. - win_T *const window = tp == curtab || tp->tp_firstwin == NULL - ? firstwin - : tp->tp_firstwin; - switchwin_T switchwin; - if (switch_win(&switchwin, window, tp, true) == OK) { - // look up the variable - // Let gettabvar({nr}, "") return the "t:" dictionary. - const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', - varname, strlen(varname), - false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - - // restore previous notion of curwin - restore_win(&switchwin, true); - } - - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - tv_copy(&argvars[2], rettv); - } -} - -/// "gettabwinvar()" function -static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - getwinvar(argvars, rettv, 1); -} - /// "gettagstack()" function static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -3833,7 +3685,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv, kListLenMayKnow); if (argvars[0].v_type != VAR_UNKNOWN) { - wparg = win_id2wp(tv_get_number(&argvars[0])); + wparg = win_id2wp((int)tv_get_number(&argvars[0])); if (wparg == NULL) { return; } @@ -3886,10 +3738,10 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - int timeout = argvars[0].vval.v_number; + int timeout = (int)argvars[0].vval.v_number; typval_T expr = argvars[1]; int interval = argvars[2].v_type == VAR_NUMBER - ? argvars[2].vval.v_number + ? (int)argvars[2].vval.v_number : 200; // Default. TimeWatcher *tw = xmalloc(sizeof(TimeWatcher)); @@ -3897,7 +3749,7 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) time_watcher_init(&main_loop, tw, NULL); tw->events = main_loop.events; tw->blockable = true; - time_watcher_start(tw, dummy_timer_due_cb, interval, interval); + time_watcher_start(tw, dummy_timer_due_cb, (uint64_t)interval, (uint64_t)interval); typval_T argv = TV_INITIAL_VALUE; typval_T exprval = TV_INITIAL_VALUE; @@ -4005,7 +3857,7 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) { flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE; } - size = tv_dict_get_number(d, "size"); + size = (int)tv_dict_get_number(d, "size"); } win_move_into_split(wp, targetwin, size, flags); @@ -4031,12 +3883,6 @@ static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = -1; } -/// "getwinvar()" function -static void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - getwinvar(argvars, rettv, 0); -} - /// "glob()" function static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -4181,6 +4027,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "cindent", "cmdline_compl", "cmdline_hist", + "cmdwin", "comments", "conceal", "cscope", @@ -4282,7 +4129,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) }; // XXX: eval_has_provider() may shell out :( - const int save_shell_error = get_vim_var_nr(VV_SHELL_ERROR); + const int save_shell_error = (int)get_vim_var_nr(VV_SHELL_ERROR); bool n = false; const char *const name = tv_get_string(&argvars[0]); for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { @@ -4359,22 +4206,6 @@ static bool has_wsl(void) return has_wsl == kTrue; } -/// "has_key()" function -static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (argvars[0].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return; - } - if (argvars[0].vval.v_dict == NULL) { - return; - } - - rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, - tv_get_string(&argvars[1]), - -1) != NULL; -} - /// `haslocaldir([{win}[, {tab}]])` function /// /// Returns `1` if the scope object has a local directory, `0` otherwise. If a @@ -4413,7 +4244,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } - scope_number[i] = argvars[i].vval.v_number; + scope_number[i] = (int)argvars[i].vval.v_number; if (scope_number[i] < -1) { emsg(_(e_invarg)); return; @@ -4629,7 +4460,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) int start = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - start = tv_get_number_chk(&argvars[2], &error); + start = (int)tv_get_number_chk(&argvars[2], &error); if (error) { return; } @@ -4647,7 +4478,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (idx = start; idx < tv_blob_len(b); idx++) { typval_T tv; tv.v_type = VAR_NUMBER; - tv.vval.v_number = tv_blob_get(b, idx); + tv.vval.v_number = tv_blob_get(b, (int)idx); if (tv_equal(&tv, &argvars[1], ic, false)) { rettv->vval.v_number = idx; return; @@ -4665,11 +4496,11 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; // Start at specified item. - idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); + idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error)); if (error || idx == -1) { item = NULL; } else { - item = tv_list_find(l, idx); + item = tv_list_find(l, (int)idx); assert(item != NULL); } if (argvars[3].v_type != VAR_UNKNOWN) { @@ -4797,7 +4628,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - const int val = tv_get_number_chk(&argvars[1], &error); + const int val = (int)tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -4808,8 +4639,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_grow(&b->bv_ga, 1); char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove(p + before + 1, p + before, (size_t)len - before); - *(p + before) = val; + memmove(p + before + 1, p + before, (size_t)(len - before)); + *(p + before) = (char_u)val; b->bv_ga.ga_len++; tv_copy(&argvars[0], rettv); @@ -4828,7 +4659,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) listitem_T *item = NULL; if (before != tv_list_len(l)) { - item = tv_list_find(l, before); + item = tv_list_find(l, (int)before); if (item == NULL) { semsg(_(e_listidx), (int64_t)before); l = NULL; @@ -4925,14 +4756,8 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars); rettv->v_type = VAR_STRING; - rettv->vval.v_string = xmalloc(len + 1); - vim_vsnprintf_typval(rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars); -} - -/// "items(dict)" function -static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 2); + rettv->vval.v_string = xmalloc((size_t)len + 1); + vim_vsnprintf_typval(rettv->vval.v_string, (size_t)len + 1, "%p", dummy_ap, argvars); } /// "jobpid(id)" function @@ -4950,7 +4775,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, true); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); if (!data) { return; } @@ -4976,7 +4801,7 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, true); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); if (!data) { return; } @@ -4986,8 +4811,8 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, - argvars[2].vval.v_number); + pty_process_resize(&data->stream.pty, (uint16_t)argvars[1].vval.v_number, + (uint16_t)argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -5102,7 +4927,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en i < ARRAY_SIZE(required_env_vars) && required_env_vars[i]; i++) { size_t len = strlen(required_env_vars[i]); - dictitem_T *dv = tv_dict_find(env, required_env_vars[i], len); + dictitem_T *dv = tv_dict_find(env, required_env_vars[i], (ptrdiff_t)len); if (!dv) { const char *env_var = os_getenv(required_env_vars[i]); if (env_var) { @@ -5251,7 +5076,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, false); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, false); if (!data) { return; } @@ -5285,7 +5110,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) ui_busy_start(); list_T *args = argvars[0].vval.v_list; - Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs)); + Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs)); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); // Validate, prepare jobs for waiting. @@ -5293,7 +5118,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER - || !(chan = find_channel(TV_LIST_ITEM_TV(arg)->vval.v_number)) + || !(chan = find_channel((uint64_t)TV_LIST_ITEM_TV(arg)->vval.v_number)) || chan->streamtype != kChannelStreamProc) { jobs[i] = NULL; // Invalid job. } else if (process_is_stopped(&chan->stream.proc)) { @@ -5316,7 +5141,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) int remaining = -1; uint64_t before = 0; if (argvars[1].v_type == VAR_NUMBER && argvars[1].vval.v_number >= 0) { - remaining = argvars[1].vval.v_number; + remaining = (int)argvars[1].vval.v_number; before = os_hrtime(); } @@ -5367,30 +5192,6 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = rv; } -/// "join()" function -static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - const char *const sep = (argvars[1].v_type == VAR_UNKNOWN - ? " " - : tv_get_string_chk(&argvars[1])); - - rettv->v_type = VAR_STRING; - - if (sep != NULL) { - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - tv_list_join(&ga, argvars[0].vval.v_list, sep); - ga_append(&ga, NUL); - rettv->vval.v_string = ga.ga_data; - } else { - rettv->vval.v_string = NULL; - } -} - /// json_decode() function static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5432,12 +5233,6 @@ static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = encode_tv2json(&argvars[0], NULL); } -/// "keys()" function -static void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 0); -} - /// "last_buffer_nr()" function. static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5482,7 +5277,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) { - rettv->v_type = out_type; + rettv->v_type = (VarType)out_type; if (out_type != VAR_NUMBER) { rettv->vval.v_string = NULL; } @@ -5503,7 +5298,7 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) // input variables char *str_in = (in_type == VAR_STRING) ? argvars[2].vval.v_string : NULL; - int int_in = argvars[2].vval.v_number; + int int_in = (int)argvars[2].vval.v_number; // output variables char **str_out = (out_type == VAR_STRING) ? &rettv->vval.v_string : NULL; @@ -5594,35 +5389,6 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "list2str()" function -static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - garray_T ga; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_invarg)); - return; - } - - list_T *const l = argvars[0].vval.v_list; - if (l == NULL) { - return; // empty list results in empty string - } - - ga_init(&ga, 1, 80); - char buf[MB_MAXBYTES + 1]; - - TV_LIST_ITER_CONST(l, li, { - buf[utf_char2bytes(tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL; - ga_concat(&ga, (char *)buf); - }); - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; -} - /// "localtime()" function static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5716,11 +5482,11 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, goto theend; } if (l != NULL) { - idx = tv_list_uidx(l, start); + idx = tv_list_uidx(l, (int)start); if (idx == -1) { goto theend; } - li = tv_list_find(l, idx); + li = tv_list_find(l, (int)idx); } else { if (start < 0) { start = 0; @@ -5732,7 +5498,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, // otherwise skip part of the string. Differs when pattern is "^" // or "\<". if (argvars[3].v_type != VAR_UNKNOWN) { - startcol = start; + startcol = (colnr_T)start; } else { str += start; len -= start; @@ -5976,7 +5742,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { - prot = tv_get_number_chk(&argvars[2], NULL); + prot = (int)tv_get_number_chk(&argvars[2], NULL); if (prot == -1) { return; } @@ -6144,7 +5910,7 @@ static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret msgpack_unpacked_init(&unpacked); for (size_t offset = 0; offset < (size_t)len;) { const msgpack_unpack_return result - = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, len, &offset); + = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, (size_t)len, &offset); if (msgpackparse_convert_item(unpacked.data, result, ret_list, true) != OK) { break; @@ -6290,9 +6056,9 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *fmt = tv_get_string_buf(&argvars[0], buf); len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { - char *s = xmalloc(len + 1); + char *s = xmalloc((size_t)len + 1); rettv->vval.v_string = s; - (void)vim_vsnprintf_typval(s, len + 1, fmt, dummy_ap, argvars + 1); + (void)vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); } did_emsg |= saved_did_emsg; } @@ -6436,7 +6202,7 @@ static void init_srand(uint32_t *const x) // Reading /dev/urandom doesn't work, fall back to time(). #endif // uncrustify:off - *x = time(NULL); + *x = (uint32_t)time(NULL); #ifndef MSWIN } #endif @@ -6514,10 +6280,10 @@ static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tvw->v_type != VAR_NUMBER) { goto theend; } - uint32_t x = tvx->vval.v_number; - uint32_t y = tvy->vval.v_number; - uint32_t z = tvz->vval.v_number; - uint32_t w = tvw->vval.v_number; + uint32_t x = (uint32_t)tvx->vval.v_number; + uint32_t y = (uint32_t)tvy->vval.v_number; + uint32_t z = (uint32_t)tvz->vval.v_number; + uint32_t w = (uint32_t)tvw->vval.v_number; result = shuffle_xoshiro128starstar(&x, &y, &z, &w); @@ -6549,7 +6315,7 @@ static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) init_srand(&x); } else { bool error = false; - x = tv_get_number_chk(&argvars[0], &error); + x = (uint32_t)tv_get_number_chk(&argvars[0], &error); if (error) { return; } @@ -6716,7 +6482,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); while (maxline < 0 || tv_list_len(l) < maxline) { - readlen = (int)fread(buf, 1, io_size, fd); + readlen = (int)fread(buf, 1, (size_t)io_size, fd); // This for loop processes what was read, but is also entered at end // of file so that either: @@ -6730,7 +6496,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) p++) { if (*p == '\n' || readlen <= 0) { char_u *s = NULL; - size_t len = p - start; + size_t len = (size_t)(p - start); // Finished a line. Remove CRs before NL. if (readlen > 0 && !binary) { @@ -6751,9 +6517,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* Change "prev" buffer to be the right size. This way * the bytes are only copied once, and very long lines are * allocated only once. */ - s = xrealloc(prev, prevlen + len + 1); + s = xrealloc(prev, (size_t)prevlen + len + 1); memcpy(s + prevlen, start, len); - s[prevlen + len] = NUL; + s[(size_t)prevlen + len] = NUL; prev = NULL; // the list will own the string prevlen = prevsize = 0; } @@ -6808,7 +6574,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) dest = buf; } if (readlen > p - buf + 1) { - memmove(dest, p + 1, readlen - (p - buf) - 1); + memmove(dest, p + 1, (size_t)readlen - (size_t)(p - buf) - 1); } readlen -= 3 - adjust_prevlen; prevlen -= adjust_prevlen; @@ -6835,10 +6601,10 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) long growmin = (long)((p - start) * 2 + prevlen); prevsize = grow50pc > growmin ? grow50pc : growmin; } - prev = xrealloc(prev, prevsize); + prev = xrealloc(prev, (size_t)prevsize); } // Add the line part to end of "prev". - memmove(prev + prevlen, start, p - start); + memmove(prev + prevlen, start, (size_t)(p - start)); prevlen += (long)(p - start); } } // while @@ -6887,7 +6653,7 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); - buf[0] = get_register_name(get_unname_register()); + buf[0] = (char)get_register_name(get_unname_register()); buf[1] = NUL; if (regname == '"') { (void)tv_dict_add_str(dict, S_LEN("points_to"), buf); @@ -6938,7 +6704,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL union { struct { int32_t low, high; } split; proftime_T prof; - } u = { .split.high = n1, .split.low = n2 }; + } u = { .split.high = (int32_t)n1, .split.low = (int32_t)n2 }; *tm = u.prof; @@ -7008,134 +6774,16 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "remove()" function static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *item, *item2; - listitem_T *li; - long idx; - long end; - dict_T *d; - dictitem_T *di; const char *const arg_errmsg = N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) { - semsg(_(e_toomanyarg), "remove()"); - } else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { - const char *key = tv_get_string_chk(&argvars[1]); - if (key != NULL) { - di = tv_dict_find(d, key, -1); - if (di == NULL) { - semsg(_(e_dictkey), key); - } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) - && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { - *rettv = di->di_tv; - di->di_tv = TV_INITIAL_VALUE; - tv_dict_item_remove(d, di); - if (tv_dict_is_watched(d)) { - tv_dict_watcher_notify(d, key, NULL, rettv); - } - } - } - } + tv_dict_remove(argvars, rettv, arg_errmsg); } else if (argvars[0].v_type == VAR_BLOB) { - blob_T *const b = argvars[0].vval.v_blob; - - if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { - return; - } - - bool error = false; - idx = (long)tv_get_number_chk(&argvars[1], &error); - - if (!error) { - const int len = tv_blob_len(b); - - if (idx < 0) { - // count from the end - idx = len + idx; - } - if (idx < 0 || idx >= len) { - semsg(_(e_blobidx), (int64_t)idx); - return; - } - if (argvars[2].v_type == VAR_UNKNOWN) { - // Remove one item, return its value. - char_u *const p = (char_u *)b->bv_ga.ga_data; - rettv->vval.v_number = (varnumber_T)(*(p + idx)); - memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); - b->bv_ga.ga_len--; - } else { - // Remove range of items, return blob with values. - end = (long)tv_get_number_chk(&argvars[2], &error); - if (error) { - return; - } - if (end < 0) { - // count from the end - end = len + end; - } - if (end >= len || idx > end) { - semsg(_(e_blobidx), (int64_t)end); - return; - } - blob_T *const blob = tv_blob_alloc(); - blob->bv_ga.ga_len = end - idx + 1; - ga_grow(&blob->bv_ga, end - idx + 1); - - char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove((char_u *)blob->bv_ga.ga_data, p + idx, - (size_t)(end - idx + 1)); - tv_blob_set_ret(rettv, blob); - - if (len - end - 1 > 0) { - memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); - } - b->bv_ga.ga_len -= end - idx + 1; - } - } - } else if (argvars[0].v_type != VAR_LIST) { + tv_blob_remove(argvars, rettv, arg_errmsg); + } else if (argvars[0].v_type == VAR_LIST) { + tv_list_remove(argvars, rettv, arg_errmsg); + } else { semsg(_(e_listdictblobarg), "remove()"); - } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - arg_errmsg, TV_TRANSLATE)) { - bool error = false; - - idx = tv_get_number_chk(&argvars[1], &error); - if (error) { - // Type error: do nothing, errmsg already given. - } else if ((item = tv_list_find(l, idx)) == NULL) { - semsg(_(e_listidx), (int64_t)idx); - } else { - if (argvars[2].v_type == VAR_UNKNOWN) { - // Remove one item, return its value. - tv_list_drop_items(l, item, item); - *rettv = *TV_LIST_ITEM_TV(item); - xfree(item); - } else { - // Remove range of items, return list with values. - end = tv_get_number_chk(&argvars[2], &error); - if (error) { - // Type error: do nothing. - } else if ((item2 = tv_list_find(l, end)) == NULL) { - semsg(_(e_listidx), (int64_t)end); - } else { - int cnt = 0; - - for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - cnt++; - if (li == item2) { - break; - } - } - if (li == NULL) { // Didn't find "item2" after "item". - emsg(_(e_invrange)); - } else { - tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), - cnt); - } - } - } - } } } @@ -7173,15 +6821,15 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (slen == 0) { return; } - const size_t len = slen * n; + const size_t len = slen * (size_t)n; // Detect overflow. - if (len / n != slen) { + if (len / (size_t)n != slen) { return; } char *const r = xmallocz(len); for (varnumber_T i = 0; i < n; i++) { - memmove(r + i * slen, p, slen); + memmove(r + (size_t)i * slen, p, slen); } rettv->vval.v_string = r; @@ -7295,9 +6943,9 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q = (char *)path_next_component(remain + 1); len = q - remain - (*q != NUL); const size_t p_len = strlen(p); - cpy = xmallocz(p_len + len); + cpy = xmallocz(p_len + (size_t)len); memcpy(cpy, p, p_len + 1); - xstrlcat(cpy + p_len, remain, len + 1); + xstrlcat(cpy + p_len, remain, (size_t)len + 1); xfree(p); p = cpy; @@ -7861,7 +7509,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Allocate extra memory for the argument vector and the NULL pointer int argvl = argsl + 2; - char **argv = xmalloc(sizeof(char_u *) * argvl); + char **argv = xmalloc(sizeof(char_u *) * (size_t)argvl); // Copy program name argv[0] = xstrdup(argvars[0].vval.v_string); @@ -7904,13 +7552,13 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // if called with a job, stop it, else closes the channel - uint64_t id = argvars[0].vval.v_number; + uint64_t id = (uint64_t)argvars[0].vval.v_number; if (find_job(id, false)) { f_jobstop(argvars, rettv, NULL); } else { const char *error; - rettv->vval.v_number = channel_close(argvars[0].vval.v_number, - kChannelPartRpc, &error); + rettv->vval.v_number = + channel_close((uint64_t)argvars[0].vval.v_number, kChannelPartRpc, &error); if (!rettv->vval.v_number) { emsg(error); } @@ -7931,7 +7579,7 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { c = -1; } else { - c = grid->attrs[grid->line_offset[row] + col]; + c = grid->attrs[grid->line_offset[row] + (size_t)col]; } rettv->vval.v_number = c; } @@ -7942,15 +7590,15 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) int c; ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { c = -1; } else { - c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + col]); + c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col]); } rettv->vval.v_number = c; } @@ -7959,8 +7607,8 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); @@ -7969,7 +7617,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } int pcc[MAX_MCO]; - int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + col], pcc); + int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + (size_t)col], pcc); int composing_len = 0; while (pcc[composing_len] != 0) { composing_len++; @@ -8004,8 +7652,8 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - pos.lnum = tv_get_number(&argvars[1]); - pos.col = tv_get_number(&argvars[2]) - 1; + pos.lnum = (linenr_T)tv_get_number(&argvars[1]); + pos.col = (colnr_T)tv_get_number(&argvars[2]) - 1; pos.coladd = 0; textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false); @@ -8028,8 +7676,8 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); @@ -8037,7 +7685,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + col]); + rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + (size_t)col]); } /// "search()" function @@ -8135,8 +7783,8 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } } - retval = do_searchpair(spat, mpat, epat, dir, skip, - flags, match_pos, lnum_stop, time_limit); + retval = (int)do_searchpair(spat, mpat, epat, dir, skip, + flags, match_pos, (linenr_T)lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -8363,7 +8011,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *const l = tv_list_alloc_ret(rettv, n); + list_T *const l = tv_list_alloc_ret(rettv, (ptrdiff_t)n); for (size_t i = 0; i < n; i++) { tv_list_append_allocated_string(l, addrs[i]); } @@ -8450,50 +8098,6 @@ static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setbufvar()" function -static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (check_secure() - || !tv_check_str_or_nr(&argvars[0])) { - return; - } - const char *varname = tv_get_string_chk(&argvars[1]); - buf_T *const buf = tv_get_buf(&argvars[0], false); - typval_T *varp = &argvars[2]; - - if (buf != NULL && varname != NULL) { - if (*varname == '&') { - long numval; - bool error = false; - aco_save_T aco; - - // set curbuf to be our buf, temporarily - aucmd_prepbuf(&aco, buf); - - varname++; - numval = tv_get_number_chk(varp, &error); - char nbuf[NUMBUFLEN]; - const char *const strval = tv_get_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); - } - - // reset notion of buffer - aucmd_restbuf(&aco); - } else { - const size_t varname_len = STRLEN(varname); - char *const bufvarname = xmalloc(varname_len + 3); - buf_T *const save_curbuf = curbuf; - curbuf = buf; - memcpy(bufvarname, "b:", 2); - memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varname_len + 2, varp, true); - xfree(bufvarname); - curbuf = save_curbuf; - } - } -} - /// Set the cursor or mark position. /// If 'charpos' is TRUE, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. @@ -8746,7 +8350,7 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *const block_len) FUNC_ATTR_NONNULL_ALL { - char_u *stropt = *pp; + char *stropt = (char *)(*pp); switch (*stropt) { case 'v': case 'c': // character-wise selection @@ -8768,7 +8372,7 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c default: return FAIL; } - *pp = stropt; + *pp = (char_u *)stropt; return OK; } @@ -8788,7 +8392,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strregname == NULL) { return; // Type error; errmsg already given. } - char regname = (uint8_t)(*strregname); + char regname = *strregname; if (regname == 0 || regname == '@') { regname = '"'; } @@ -8867,7 +8471,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. - char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2)); + char **lstval = xmalloc(sizeof(char *) * (((size_t)len + 1) * 2)); const char **curval = (const char **)lstval; char **allocval = lstval + len + 2; char **curallocval = allocval; @@ -8889,8 +8493,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) }); *curval++ = NULL; - write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, - block_len); + write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, (colnr_T)block_len); free_lstval: while (curallocval > allocval) { @@ -8902,8 +8505,8 @@ free_lstval: if (strval == NULL) { return; } - write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), - append, yank_type, block_len); + write_reg_contents_ex(regname, (const char_u *)strval, (ssize_t)STRLEN(strval), + append, yank_type, (colnr_T)block_len); } if (pointreg != 0) { get_yank_register(pointreg, YREG_YANK); @@ -8916,43 +8519,6 @@ free_lstval: } } -/// "settabvar()" function -static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = 0; - - if (check_secure()) { - return; - } - - tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - const char *const varname = tv_get_string_chk(&argvars[1]); - typval_T *const varp = &argvars[2]; - - if (varname != NULL && tp != NULL) { - tabpage_T *const save_curtab = curtab; - goto_tabpage_tp(tp, false, false); - - const size_t varname_len = strlen(varname); - char *const tabvarname = xmalloc(varname_len + 3); - memcpy(tabvarname, "t:", 2); - memcpy(tabvarname + 2, varname, varname_len + 1); - set_var(tabvarname, varname_len + 2, varp, true); - xfree(tabvarname); - - // Restore current tabpage. - if (valid_tabpage(save_curtab)) { - goto_tabpage_tp(save_curtab, false, false); - } - } -} - -/// "settabwinvar()" function -static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - setwinvar(argvars, rettv, 1); -} - /// "settagstack()" function static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9006,12 +8572,6 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setwinvar()" function -static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - setwinvar(argvars, rettv, 0); -} - /// f_sha256 - sha256({string}) function static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9046,7 +8606,7 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (col < 0) { return; // type error; errmsg already given } - rettv->vval.v_number = get_sw_value_col(curbuf, col); + rettv->vval.v_number = get_sw_value_col(curbuf, (colnr_T)col); return; } rettv->vval.v_number = get_sw_value(curbuf); @@ -9113,341 +8673,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// struct storing information about current sort -typedef struct { - int item_compare_ic; - bool item_compare_lc; - bool item_compare_numeric; - bool item_compare_numbers; - bool item_compare_float; - const char *item_compare_func; - partial_T *item_compare_partial; - dict_T *item_compare_selfdict; - bool item_compare_func_err; -} sortinfo_T; -static sortinfo_T *sortinfo = NULL; - -#define ITEM_COMPARE_FAIL 999 - -/// Compare functions for f_sort() and f_uniq() below. -static int item_compare(const void *s1, const void *s2, bool keep_zero) -{ - ListSortItem *const si1 = (ListSortItem *)s1; - ListSortItem *const si2 = (ListSortItem *)s2; - - typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); - typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); - - int res; - - if (sortinfo->item_compare_numbers) { - const varnumber_T v1 = tv_get_number(tv1); - const varnumber_T v2 = tv_get_number(tv2); - - res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - goto item_compare_end; - } - - if (sortinfo->item_compare_float) { - const float_T v1 = tv_get_float(tv1); - const float_T v2 = tv_get_float(tv2); - - res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - goto item_compare_end; - } - - char *tofree1 = NULL; - char *tofree2 = NULL; - char *p1; - char *p2; - - // encode_tv2string() puts quotes around a string and allocates memory. Don't - // do that for string variables. Use a single quote when comparing with - // a non-string to do what the docs promise. - if (tv1->v_type == VAR_STRING) { - if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p1 = "'"; - } else { - p1 = tv1->vval.v_string; - } - } else { - tofree1 = p1 = encode_tv2string(tv1, NULL); - } - if (tv2->v_type == VAR_STRING) { - if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p2 = "'"; - } else { - p2 = tv2->vval.v_string; - } - } else { - tofree2 = p2 = encode_tv2string(tv2, NULL); - } - if (p1 == NULL) { - p1 = ""; - } - if (p2 == NULL) { - p2 = ""; - } - if (!sortinfo->item_compare_numeric) { - if (sortinfo->item_compare_lc) { - res = strcoll(p1, p2); - } else { - res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); - } - } else { - double n1, n2; - n1 = strtod(p1, &p1); - n2 = strtod(p2, &p2); - res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; - } - - xfree(tofree1); - xfree(tofree2); - -item_compare_end: - // When the result would be zero, compare the item indexes. Makes the - // sort stable. - if (res == 0 && !keep_zero) { - // WARNING: When using uniq si1 and si2 are actually listitem_T **, no - // indexes are there. - res = si1->idx > si2->idx ? 1 : -1; - } - return res; -} - -static int item_compare_keeping_zero(const void *s1, const void *s2) -{ - return item_compare(s1, s2, true); -} - -static int item_compare_not_keeping_zero(const void *s1, const void *s2) -{ - return item_compare(s1, s2, false); -} - -static int item_compare2(const void *s1, const void *s2, bool keep_zero) -{ - ListSortItem *si1, *si2; - int res; - typval_T rettv; - typval_T argv[3]; - const char *func_name; - partial_T *partial = sortinfo->item_compare_partial; - - // shortcut after failure in previous call; compare all items equal - if (sortinfo->item_compare_func_err) { - return 0; - } - - si1 = (ListSortItem *)s1; - si2 = (ListSortItem *)s2; - - if (partial == NULL) { - func_name = sortinfo->item_compare_func; - } else { - func_name = (const char *)partial_name(partial); - } - - // Copy the values. This is needed to be able to set v_lock to VAR_FIXED - // in the copy without changing the original list items. - tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); - tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); - - rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - funcexe_T funcexe = FUNCEXE_INIT; - funcexe.evaluate = true; - funcexe.partial = partial; - funcexe.selfdict = sortinfo->item_compare_selfdict; - res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); - tv_clear(&argv[0]); - tv_clear(&argv[1]); - - if (res == FAIL) { - res = ITEM_COMPARE_FAIL; - } else { - res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); - if (res > 0) { - res = 1; - } else if (res < 0) { - res = -1; - } - } - if (sortinfo->item_compare_func_err) { - res = ITEM_COMPARE_FAIL; // return value has wrong type - } - tv_clear(&rettv); - - // When the result would be zero, compare the pointers themselves. Makes - // the sort stable. - if (res == 0 && !keep_zero) { - // WARNING: When using uniq si1 and si2 are actually listitem_T **, no - // indexes are there. - res = si1->idx > si2->idx ? 1 : -1; - } - - return res; -} - -static int item_compare2_keeping_zero(const void *s1, const void *s2) -{ - return item_compare2(s1, s2, true); -} - -static int item_compare2_not_keeping_zero(const void *s1, const void *s2) -{ - return item_compare2(s1, s2, false); -} - -/// "sort({list})" function -static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) -{ - ListSortItem *ptrs; - long len; - long i; - - // Pointer to current info struct used in compare function. Save and restore - // the current one for nested calls. - sortinfo_T info; - sortinfo_T *old_sortinfo = sortinfo; - sortinfo = &info; - - const char *const arg_errmsg = (sort - ? N_("sort() argument") - : N_("uniq() argument")); - - if (argvars[0].v_type != VAR_LIST) { - semsg(_(e_listarg), sort ? "sort()" : "uniq()"); - } else { - list_T *const l = argvars[0].vval.v_list; - if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { - goto theend; - } - tv_list_set_ret(rettv, l); - - len = tv_list_len(l); - if (len <= 1) { - goto theend; // short list sorts pretty quickly - } - - info.item_compare_ic = false; - info.item_compare_lc = false; - info.item_compare_numeric = false; - info.item_compare_numbers = false; - info.item_compare_float = false; - info.item_compare_func = NULL; - info.item_compare_partial = NULL; - info.item_compare_selfdict = NULL; - - if (argvars[1].v_type != VAR_UNKNOWN) { - // optional second argument: {func} - if (argvars[1].v_type == VAR_FUNC) { - info.item_compare_func = (const char *)argvars[1].vval.v_string; - } else if (argvars[1].v_type == VAR_PARTIAL) { - info.item_compare_partial = argvars[1].vval.v_partial; - } else { - bool error = false; - - i = tv_get_number_chk(&argvars[1], &error); - if (error) { - goto theend; // type error; errmsg already given - } - if (i == 1) { - info.item_compare_ic = true; - } else if (argvars[1].v_type != VAR_NUMBER) { - info.item_compare_func = tv_get_string(&argvars[1]); - } else if (i != 0) { - emsg(_(e_invarg)); - goto theend; - } - if (info.item_compare_func != NULL) { - if (*info.item_compare_func == NUL) { - // empty string means default sort - info.item_compare_func = NULL; - } else if (strcmp(info.item_compare_func, "n") == 0) { - info.item_compare_func = NULL; - info.item_compare_numeric = true; - } else if (strcmp(info.item_compare_func, "N") == 0) { - info.item_compare_func = NULL; - info.item_compare_numbers = true; - } else if (strcmp(info.item_compare_func, "f") == 0) { - info.item_compare_func = NULL; - info.item_compare_float = true; - } else if (strcmp(info.item_compare_func, "i") == 0) { - info.item_compare_func = NULL; - info.item_compare_ic = true; - } else if (strcmp(info.item_compare_func, "l") == 0) { - info.item_compare_func = NULL; - info.item_compare_lc = true; - } - } - } - - if (argvars[2].v_type != VAR_UNKNOWN) { - // optional third argument: {dict} - if (argvars[2].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - goto theend; - } - info.item_compare_selfdict = argvars[2].vval.v_dict; - } - } - - // Make an array with each entry pointing to an item in the List. - ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); - - if (sort) { - info.item_compare_func_err = false; - tv_list_item_sort(l, ptrs, - ((info.item_compare_func == NULL - && info.item_compare_partial == NULL) - ? item_compare_not_keeping_zero - : item_compare2_not_keeping_zero), - &info.item_compare_func_err); - if (info.item_compare_func_err) { - emsg(_("E702: Sort compare function failed")); - } - } else { - ListSorter item_compare_func_ptr; - - // f_uniq(): ptrs will be a stack of items to remove. - info.item_compare_func_err = false; - if (info.item_compare_func != NULL - || info.item_compare_partial != NULL) { - item_compare_func_ptr = item_compare2_keeping_zero; - } else { - item_compare_func_ptr = item_compare_keeping_zero; - } - - int idx = 0; - for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) - ; li != NULL;) { - listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); - if (item_compare_func_ptr(&prev_li, &li) == 0) { - if (info.item_compare_func_err) { // -V547 - emsg(_("E882: Uniq compare function failed")); - break; - } - li = tv_list_item_remove(l, li); - } else { - idx++; - li = TV_LIST_ITEM_NEXT(l, li); - } - } - } - - xfree(ptrs); - } - -theend: - sortinfo = old_sortinfo; -} - -/// "sort"({list})" function -static void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - do_sort_uniq(argvars, rettv, true); -} - /// "stdioopen()" function static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9483,12 +8708,6 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// "uniq({list})" function -static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - do_sort_uniq(argvars, rettv, false); -} - /// "reltimefloat()" function static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL @@ -9549,7 +8768,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } str += len; - capcol -= len; + capcol -= (int)len; len = 0; } } @@ -9558,7 +8777,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) assert(len <= INT_MAX); tv_list_alloc_ret(rettv, 2); - tv_list_append_string(rettv->vval.v_list, word, len); + tv_list_append_string(rettv->vval.v_list, word, (ssize_t)len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" : attr == HLF_SPR ? "rare" @@ -9591,7 +8810,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = tv_get_number_chk(&argvars[1], &typeerr); + maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) { goto f_spellsuggest_return; } @@ -9763,7 +8982,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int what = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - base = tv_get_number(&argvars[1]); + base = (int)tv_get_number(&argvars[1]); if (base != 2 && base != 8 && base != 10 && base != 16) { emsg(_(e_invarg)); return; @@ -9874,7 +9093,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } charidx--; - byteidx += utf_ptr2len(str + byteidx); + byteidx += (size_t)utf_ptr2len(str + byteidx); } } @@ -9932,7 +9151,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) int (*func_mb_ptr2char_adv)(const char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = tv_get_number_chk(&argvars[1], NULL); + skipcc = (int)tv_get_number_chk(&argvars[1], NULL); } if (skipcc < 0 || skipcc > 1) { emsg(_(e_invarg)); @@ -9953,7 +9172,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) int col = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - col = tv_get_number(&argvars[1]); + col = (int)tv_get_number(&argvars[1]); } rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); @@ -9983,12 +9202,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) nchar--; } } else { - nbyte = nchar; + nbyte = (int)nchar; } } int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - int charlen = tv_get_number(&argvars[2]); + int charlen = (int)tv_get_number(&argvars[2]); while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; @@ -10000,7 +9219,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) charlen--; } } else { - len = slen - nbyte; // default: all bytes that are available. + len = (int)slen - nbyte; // default: all bytes that are available. } // Only return the overlap between the specified part and the actual @@ -10009,12 +9228,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) len += nbyte; nbyte = 0; } else if ((size_t)nbyte > slen) { - nbyte = slen; + nbyte = (int)slen; } if (len < 0) { len = 0; } else if (nbyte + len > (int)slen) { - len = slen - nbyte; + len = (int)slen - nbyte; } rettv->v_type = VAR_STRING; @@ -10036,7 +9255,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[2].v_type != VAR_UNKNOWN) { len = tv_get_number(&argvars[2]); } else { - len = slen - n; // Default len: all bytes that are available. + len = (varnumber_T)slen - n; // Default len: all bytes that are available. } // Only return the overlap between the specified part and the actual @@ -10045,19 +9264,19 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) len += n; n = 0; } else if (n > (varnumber_T)slen) { - n = slen; + n = (varnumber_T)slen; } if (len < 0) { len = 0; } else if (n + len > (varnumber_T)slen) { - len = slen - n; + len = (varnumber_T)slen - n; } if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) { int off; // length in characters - for (off = n; off < (int)slen && len > 0; len--) { + for (off = (int)n; off < (int)slen && len > 0; len--) { off += utfc_ptr2len(p + off); } len = off - n; @@ -10165,7 +9384,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) int retList = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - retList = tv_get_number_chk(&argvars[1], &error); + retList = (int)tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -10238,7 +9457,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; bool transerr = false; - const int trans = tv_get_number_chk(&argvars[2], &transerr); + const int trans = (int)tv_get_number_chk(&argvars[2], &transerr); int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -10287,8 +9506,12 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) p = highlight_has_attr(id, HL_ITALIC, modec); } break; - case 'n': // name - p = get_highlight_name_ext(NULL, id - 1, false); + case 'n': + if (TOLOWER_ASC(what[1]) == 'o') { // nocombine + p = highlight_has_attr(id, HL_NOCOMBINE, modec); + } else { // name + p = get_highlight_name_ext(NULL, id - 1, false); + } break; case 'r': // reverse p = highlight_has_attr(id, HL_INVERSE, modec); @@ -10335,7 +9558,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "synIDtrans(id)" function static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id = tv_get_number(&argvars[0]); + int id = (int)tv_get_number(&argvars[0]); if (id > 0) { id = syn_get_final_id(id); @@ -10662,10 +9885,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) const bool overlapped = false; const bool detach = false; ChannelStdinMode stdin_mode = kChannelStdinPipe; - uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); + uint16_t term_width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, rpc, overlapped, detach, stdin_mode, - cwd, term_width, curwin->w_height_inner, + cwd, term_width, (uint16_t)curwin->w_height_inner, env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; @@ -10699,7 +9922,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ(chan->id), false, false, &err); + INTEGER_OBJ((Integer)chan->id), false, false, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), INTEGER_OBJ(pid), false, false, &err); @@ -10740,8 +9963,8 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) if (!timer->paused && paused) { time_watcher_stop(&timer->tw); } else if (timer->paused && !paused) { - time_watcher_start(&timer->tw, timer_due_cb, timer->timeout, - timer->timeout); + time_watcher_start(&timer->tw, timer_due_cb, (uint64_t)timer->timeout, + (uint64_t)timer->timeout); } timer->paused = paused; } @@ -10766,7 +9989,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); if (di != NULL) { - repeat = tv_get_number(&di->di_tv); + repeat = (int)tv_get_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -10777,8 +10000,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!callback_from_typval(&callback, &argvars[1])) { return; } - rettv->vval.v_number = - timer_start(tv_get_number(&argvars[0]), repeat, &callback); + rettv->vval.v_number = (varnumber_T)timer_start(tv_get_number(&argvars[0]), repeat, &callback); } /// "timer_stop(timerid)" function @@ -10975,7 +10197,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - rettv->vval.v_string = (char *)vim_strnsave(head, tail - head); + rettv->vval.v_string = (char *)vim_strnsave(head, (size_t)(tail - head)); } /// "type(expr)" function @@ -11047,12 +10269,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); } -/// "values(dict)" function -static void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 1); -} - /// "virtcol(string)" function static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -11085,7 +10301,7 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u str[2]; rettv->v_type = VAR_STRING; - str[0] = curbuf->b_visual_mode_eval; + str[0] = (char_u)curbuf->b_visual_mode_eval; str[1] = NUL; rettv->vval.v_string = (char *)vim_strsave(str); @@ -11299,29 +10515,29 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { dictitem_T *di; if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { - curwin->w_cursor.lnum = tv_get_number(&di->di_tv); + curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { - curwin->w_cursor.col = tv_get_number(&di->di_tv); + curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { - curwin->w_cursor.coladd = tv_get_number(&di->di_tv); + curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { - curwin->w_curswant = tv_get_number(&di->di_tv); + curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv); curwin->w_set_curswant = false; } if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { - set_topline(curwin, tv_get_number(&di->di_tv)); + set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv)); } if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { - curwin->w_topfill = tv_get_number(&di->di_tv); + curwin->w_topfill = (int)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { - curwin->w_leftcol = tv_get_number(&di->di_tv); + curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { - curwin->w_skipcol = tv_get_number(&di->di_tv); + curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv); } check_cursor(); diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 5f8d81c989..583ee0e75e 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -4,8 +4,6 @@ #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" -typedef void (*FunPtr)(void); - /// Prototype of C function that implements VimL function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index e19cf411c0..fd57b45e86 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -19,6 +19,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" @@ -829,6 +830,454 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) return retval; } +/// "join()" function +void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + const char *const sep = (argvars[1].v_type == VAR_UNKNOWN + ? " " + : tv_get_string_chk(&argvars[1])); + + rettv->v_type = VAR_STRING; + + if (sep != NULL) { + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + tv_list_join(&ga, argvars[0].vval.v_list, sep); + ga_append(&ga, NUL); + rettv->vval.v_string = ga.ga_data; + } else { + rettv->vval.v_string = NULL; + } +} + +/// "list2str()" function +void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + garray_T ga; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_invarg)); + return; + } + + list_T *const l = argvars[0].vval.v_list; + if (l == NULL) { + return; // empty list results in empty string + } + + ga_init(&ga, 1, 80); + char buf[MB_MAXBYTES + 1]; + + TV_LIST_ITER_CONST(l, li, { + buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL; + ga_concat(&ga, (char *)buf); + }); + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; +} + +/// "remove({list})" function +void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + list_T *l; + bool error = false; + + if (var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { + return; + } + + long idx = tv_get_number_chk(&argvars[1], &error); + + listitem_T *item; + + if (error) { + // Type error: do nothing, errmsg already given. + } else if ((item = tv_list_find(l, (int)idx)) == NULL) { + semsg(_(e_listidx), (int64_t)idx); + } else { + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + tv_list_drop_items(l, item, item); + *rettv = *TV_LIST_ITEM_TV(item); + xfree(item); + } else { + listitem_T *item2; + // Remove range of items, return list with values. + long end = tv_get_number_chk(&argvars[2], &error); + if (error) { + // Type error: do nothing. + } else if ((item2 = tv_list_find(l, (int)end)) == NULL) { + semsg(_(e_listidx), (int64_t)end); + } else { + int cnt = 0; + + listitem_T *li; + for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + cnt++; + if (li == item2) { + break; + } + } + if (li == NULL) { // Didn't find "item2" after "item". + emsg(_(e_invrange)); + } else { + tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), + cnt); + } + } + } + } +} + +/// struct storing information about current sort +typedef struct { + int item_compare_ic; + bool item_compare_lc; + bool item_compare_numeric; + bool item_compare_numbers; + bool item_compare_float; + const char *item_compare_func; + partial_T *item_compare_partial; + dict_T *item_compare_selfdict; + bool item_compare_func_err; +} sortinfo_T; +static sortinfo_T *sortinfo = NULL; + +#define ITEM_COMPARE_FAIL 999 + +/// Compare functions for f_sort() and f_uniq() below. +static int item_compare(const void *s1, const void *s2, bool keep_zero) +{ + ListSortItem *const si1 = (ListSortItem *)s1; + ListSortItem *const si2 = (ListSortItem *)s2; + + typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); + typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); + + int res; + + if (sortinfo->item_compare_numbers) { + const varnumber_T v1 = tv_get_number(tv1); + const varnumber_T v2 = tv_get_number(tv2); + + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; + } + + if (sortinfo->item_compare_float) { + const float_T v1 = tv_get_float(tv1); + const float_T v2 = tv_get_float(tv2); + + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; + } + + char *tofree1 = NULL; + char *tofree2 = NULL; + char *p1; + char *p2; + + // encode_tv2string() puts quotes around a string and allocates memory. Don't + // do that for string variables. Use a single quote when comparing with + // a non-string to do what the docs promise. + if (tv1->v_type == VAR_STRING) { + if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { + p1 = "'"; + } else { + p1 = tv1->vval.v_string; + } + } else { + tofree1 = p1 = encode_tv2string(tv1, NULL); + } + if (tv2->v_type == VAR_STRING) { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { + p2 = "'"; + } else { + p2 = tv2->vval.v_string; + } + } else { + tofree2 = p2 = encode_tv2string(tv2, NULL); + } + if (p1 == NULL) { + p1 = ""; + } + if (p2 == NULL) { + p2 = ""; + } + if (!sortinfo->item_compare_numeric) { + if (sortinfo->item_compare_lc) { + res = strcoll(p1, p2); + } else { + res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); + } + } else { + double n1, n2; + n1 = strtod(p1, &p1); + n2 = strtod(p2, &p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + xfree(tofree1); + xfree(tofree2); + +item_compare_end: + // When the result would be zero, compare the item indexes. Makes the + // sort stable. + if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. + res = si1->idx > si2->idx ? 1 : -1; + } + return res; +} + +static int item_compare_keeping_zero(const void *s1, const void *s2) +{ + return item_compare(s1, s2, true); +} + +static int item_compare_not_keeping_zero(const void *s1, const void *s2) +{ + return item_compare(s1, s2, false); +} + +static int item_compare2(const void *s1, const void *s2, bool keep_zero) +{ + ListSortItem *si1, *si2; + int res; + typval_T rettv; + typval_T argv[3]; + const char *func_name; + partial_T *partial = sortinfo->item_compare_partial; + + // shortcut after failure in previous call; compare all items equal + if (sortinfo->item_compare_func_err) { + return 0; + } + + si1 = (ListSortItem *)s1; + si2 = (ListSortItem *)s2; + + if (partial == NULL) { + func_name = sortinfo->item_compare_func; + } else { + func_name = (const char *)partial_name(partial); + } + + // Copy the values. This is needed to be able to set v_lock to VAR_FIXED + // in the copy without changing the original list items. + tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); + tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); + + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); + tv_clear(&argv[0]); + tv_clear(&argv[1]); + + if (res == FAIL) { + res = ITEM_COMPARE_FAIL; + } else { + res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (res > 0) { + res = 1; + } else if (res < 0) { + res = -1; + } + } + if (sortinfo->item_compare_func_err) { + res = ITEM_COMPARE_FAIL; // return value has wrong type + } + tv_clear(&rettv); + + // When the result would be zero, compare the pointers themselves. Makes + // the sort stable. + if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. + res = si1->idx > si2->idx ? 1 : -1; + } + + return res; +} + +static int item_compare2_keeping_zero(const void *s1, const void *s2) +{ + return item_compare2(s1, s2, true); +} + +static int item_compare2_not_keeping_zero(const void *s1, const void *s2) +{ + return item_compare2(s1, s2, false); +} + +/// "sort({list})" function +static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) +{ + ListSortItem *ptrs; + long len; + long i; + + // Pointer to current info struct used in compare function. Save and restore + // the current one for nested calls. + sortinfo_T info; + sortinfo_T *old_sortinfo = sortinfo; + sortinfo = &info; + + const char *const arg_errmsg = (sort + ? N_("sort() argument") + : N_("uniq() argument")); + + if (argvars[0].v_type != VAR_LIST) { + semsg(_(e_listarg), sort ? "sort()" : "uniq()"); + } else { + list_T *const l = argvars[0].vval.v_list; + if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + goto theend; + } + tv_list_set_ret(rettv, l); + + len = tv_list_len(l); + if (len <= 1) { + goto theend; // short list sorts pretty quickly + } + + info.item_compare_ic = false; + info.item_compare_lc = false; + info.item_compare_numeric = false; + info.item_compare_numbers = false; + info.item_compare_float = false; + info.item_compare_func = NULL; + info.item_compare_partial = NULL; + info.item_compare_selfdict = NULL; + + if (argvars[1].v_type != VAR_UNKNOWN) { + // optional second argument: {func} + if (argvars[1].v_type == VAR_FUNC) { + info.item_compare_func = (const char *)argvars[1].vval.v_string; + } else if (argvars[1].v_type == VAR_PARTIAL) { + info.item_compare_partial = argvars[1].vval.v_partial; + } else { + bool error = false; + + i = tv_get_number_chk(&argvars[1], &error); + if (error) { + goto theend; // type error; errmsg already given + } + if (i == 1) { + info.item_compare_ic = true; + } else if (argvars[1].v_type != VAR_NUMBER) { + info.item_compare_func = tv_get_string(&argvars[1]); + } else if (i != 0) { + emsg(_(e_invarg)); + goto theend; + } + if (info.item_compare_func != NULL) { + if (*info.item_compare_func == NUL) { + // empty string means default sort + info.item_compare_func = NULL; + } else if (strcmp(info.item_compare_func, "n") == 0) { + info.item_compare_func = NULL; + info.item_compare_numeric = true; + } else if (strcmp(info.item_compare_func, "N") == 0) { + info.item_compare_func = NULL; + info.item_compare_numbers = true; + } else if (strcmp(info.item_compare_func, "f") == 0) { + info.item_compare_func = NULL; + info.item_compare_float = true; + } else if (strcmp(info.item_compare_func, "i") == 0) { + info.item_compare_func = NULL; + info.item_compare_ic = true; + } else if (strcmp(info.item_compare_func, "l") == 0) { + info.item_compare_func = NULL; + info.item_compare_lc = true; + } + } + } + + if (argvars[2].v_type != VAR_UNKNOWN) { + // optional third argument: {dict} + if (argvars[2].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + goto theend; + } + info.item_compare_selfdict = argvars[2].vval.v_dict; + } + } + + // Make an array with each entry pointing to an item in the List. + ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem))); + + if (sort) { + info.item_compare_func_err = false; + tv_list_item_sort(l, ptrs, + ((info.item_compare_func == NULL + && info.item_compare_partial == NULL) + ? item_compare_not_keeping_zero + : item_compare2_not_keeping_zero), + &info.item_compare_func_err); + if (info.item_compare_func_err) { + emsg(_("E702: Sort compare function failed")); + } + } else { + ListSorter item_compare_func_ptr; + + // f_uniq(): ptrs will be a stack of items to remove. + info.item_compare_func_err = false; + if (info.item_compare_func != NULL + || info.item_compare_partial != NULL) { + item_compare_func_ptr = item_compare2_keeping_zero; + } else { + item_compare_func_ptr = item_compare_keeping_zero; + } + + int idx = 0; + for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) + ; li != NULL;) { + listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); + if (item_compare_func_ptr(&prev_li, &li) == 0) { + if (info.item_compare_func_err) { // -V547 + emsg(_("E882: Uniq compare function failed")); + break; + } + li = tv_list_item_remove(l, li); + } else { + idx++; + li = TV_LIST_ITEM_NEXT(l, li); + } + } + } + + xfree(ptrs); + } + +theend: + sortinfo = old_sortinfo; +} + +/// "sort"({list})" function +void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_sort_uniq(argvars, rettv, true); +} + +/// "uniq({list})" function +void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_sort_uniq(argvars, rettv, false); +} + /// Check whether two lists are equal /// /// @param[in] l1 First list to compare. @@ -2199,6 +2648,66 @@ bool tv_blob_equal(const blob_T *const b1, const blob_T *const b2) return true; } +/// "remove({blob})" function +void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + blob_T *const b = argvars[0].vval.v_blob; + + if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { + return; + } + + bool error = false; + long idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + const int len = tv_blob_len(b); + + if (idx < 0) { + // count from the end + idx = len + idx; + } + if (idx < 0 || idx >= len) { + semsg(_(e_blobidx), (int64_t)idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + char_u *const p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T)(*(p + idx)); + memmove(p + idx, p + idx + 1, (size_t)(len - idx - 1)); + b->bv_ga.ga_len--; + } else { + // Remove range of items, return blob with values. + long end = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (end < 0) { + // count from the end + end = len + end; + } + if (end >= len || idx > end) { + semsg(_(e_blobidx), (int64_t)end); + return; + } + blob_T *const blob = tv_blob_alloc(); + blob->bv_ga.ga_len = (int)(end - idx + 1); + ga_grow(&blob->bv_ga, (int)(end - idx + 1)); + + char_u *const p = (char_u *)b->bv_ga.ga_data; + memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + tv_blob_set_ret(rettv, blob); + + if (len - end - 1 > 0) { + memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); + } + b->bv_ga.ga_len -= (int)(end - idx + 1); + } + } +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -2243,6 +2752,118 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) tv_dict_set_ret(ret_tv, d); } +/// Turn a dictionary into a list +/// +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. +static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) +{ + if (tv->v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + if (tv->vval.v_dict == NULL) { + return; + } + + tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); + + TV_DICT_ITER(tv->vval.v_dict, di, { + typval_T tv_item = { .v_lock = VAR_UNLOCKED }; + + switch (what) { + case kDictListKeys: + tv_item.v_type = VAR_STRING; + tv_item.vval.v_string = (char *)vim_strsave(di->di_key); + break; + case kDictListValues: + tv_copy(&di->di_tv, &tv_item); + break; + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(2); + tv_item.v_type = VAR_LIST; + tv_item.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrdup((const char *)di->di_key), + }); + + tv_list_append_tv(sub_l, &di->di_tv); + + break; + } + } + + tv_list_append_owned_tv(rettv->vval.v_list, tv_item); + }); +} + +/// "items(dict)" function +void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 2); +} + +/// "keys()" function +void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 0); +} + +/// "values(dict)" function +void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 1); +} + +/// "has_key()" function +void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + if (argvars[0].vval.v_dict == NULL) { + return; + } + + rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, + tv_get_string(&argvars[1]), + -1) != NULL; +} + +/// "remove({dict})" function +void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + dict_T *d; + if (argvars[2].v_type != VAR_UNKNOWN) { + semsg(_(e_toomanyarg), "remove()"); + } else if ((d = argvars[0].vval.v_dict) != NULL + && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { + const char *key = tv_get_string_chk(&argvars[1]); + if (key != NULL) { + dictitem_T *di = tv_dict_find(d, key, -1); + if (di == NULL) { + semsg(_(e_dictkey), key); + } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { + *rettv = di->di_tv; + di->di_tv = TV_INITIAL_VALUE; + tv_dict_item_remove(d, di); + if (tv_dict_is_watched(d)) { + tv_dict_watcher_notify(d, key, NULL, rettv); + } + } + } + } +} + /// Allocate an empty blob for a return value. /// /// Also sets reference count. diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index c2579944e4..a90148bf23 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -9,13 +9,16 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/getchar.h" #include "nvim/globals.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/os/input.h" #include "nvim/regexp.h" @@ -1265,7 +1268,7 @@ void free_all_functions(void) // Clean up the current_funccal chain and the funccal stack. while (current_funccal != NULL) { tv_clear(current_funccal->rettv); - cleanup_function_call(current_funccal); + cleanup_function_call(current_funccal); // -V595 if (current_funccal == NULL && funccal_stack != NULL) { restore_funccal(); } diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c new file mode 100644 index 0000000000..d01fff6b94 --- /dev/null +++ b/src/nvim/eval/vars.c @@ -0,0 +1,1822 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// eval/vars.c: functions for dealing with variables + +#include "nvim/ascii.h" +#include "nvim/autocmd.h" +#include "nvim/buffer.h" +#include "nvim/charset.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" +#include "nvim/ex_cmds.h" +#include "nvim/ex_docmd.h" +#include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/search.h" +#include "nvim/window.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/vars.c.generated.h" +#endif + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 // maximum nesting of lists and dicts + +static char *e_letunexp = N_("E18: Unexpected characters in :let"); +static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); + +/// Get a list of lines from a HERE document. The here document is a list of +/// lines surrounded by a marker. +/// cmd << {marker} +/// {line1} +/// {line2} +/// .... +/// {marker} +/// +/// The {marker} is a string. If the optional 'trim' word is supplied before the +/// marker, then the leading indentation before the lines (matching the +/// indentation in the 'cmd' line) is stripped. +/// +/// @return a List with {lines} or NULL. +static list_T *heredoc_get(exarg_T *eap, char *cmd) +{ + char *marker; + char *p; + int marker_indent_len = 0; + int text_indent_len = 0; + char *text_indent = NULL; + + if (eap->getline == NULL) { + emsg(_("E991: cannot use =<< here")); + return NULL; + } + + // Check for the optional 'trim' word before the marker + cmd = skipwhite(cmd); + if (STRNCMP(cmd, "trim", 4) == 0 + && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { + cmd = skipwhite(cmd + 4); + + // Trim the indentation from all the lines in the here document. + // The amount of indentation trimmed is the same as the indentation of + // the first line after the :let command line. To find the end marker + // the indent of the :let command line is trimmed. + p = *eap->cmdlinep; + while (ascii_iswhite(*p)) { + p++; + marker_indent_len++; + } + text_indent_len = -1; + } + + // The marker is the next word. + if (*cmd != NUL && *cmd != '"') { + marker = skipwhite(cmd); + p = (char *)skiptowhite((char_u *)marker); + if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { + emsg(_(e_trailing)); + return NULL; + } + *p = NUL; + if (islower(*marker)) { + emsg(_("E221: Marker cannot start with lower case letter")); + return NULL; + } + } else { + emsg(_("E172: Missing marker")); + return NULL; + } + + list_T *l = tv_list_alloc(0); + for (;;) { + int mi = 0; + int ti = 0; + + char *theline = eap->getline(NUL, eap->cookie, 0, false); + if (theline == NULL) { + semsg(_("E990: Missing end marker '%s'"), marker); + break; + } + + // with "trim": skip the indent matching the :let line to find the + // marker + if (marker_indent_len > 0 + && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { + mi = marker_indent_len; + } + if (STRCMP(marker, theline + mi) == 0) { + xfree(theline); + break; + } + if (text_indent_len == -1 && *theline != NUL) { + // set the text indent from the first line. + p = theline; + text_indent_len = 0; + while (ascii_iswhite(*p)) { + p++; + text_indent_len++; + } + text_indent = xstrnsave(theline, (size_t)text_indent_len); + } + // with "trim": skip the indent matching the first line + if (text_indent != NULL) { + for (ti = 0; ti < text_indent_len; ti++) { + if (theline[ti] != text_indent[ti]) { + break; + } + } + } + + tv_list_append_string(l, theline + ti, -1); + xfree(theline); + } + xfree(text_indent); + + return l; +} + +/// ":let" list all variable values +/// ":let var1 var2" list variable values +/// ":let var = expr" assignment command. +/// ":let var += expr" assignment command. +/// ":let var -= expr" assignment command. +/// ":let var *= expr" assignment command. +/// ":let var /= expr" assignment command. +/// ":let var %= expr" assignment command. +/// ":let var .= expr" assignment command. +/// ":let var ..= expr" assignment command. +/// ":let [var1, var2] = expr" unpack list. +/// ":let [name, ..., ; lastname] = expr" unpack list. +void ex_let(exarg_T *eap) +{ + ex_let_const(eap, false); +} + +/// ":cons[t] var = expr1" define constant +/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list +/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list +void ex_const(exarg_T *eap) +{ + ex_let_const(eap, true); +} + +static void ex_let_const(exarg_T *eap, const bool is_const) +{ + char *arg = eap->arg; + char *expr = NULL; + typval_T rettv; + int i; + int var_count = 0; + int semicolon = 0; + char op[2]; + char *argend; + int first = true; + + argend = (char *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { + return; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } + expr = skipwhite(argend); + if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL + && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { + // ":let" without "=": list variables + if (*arg == '[') { + emsg(_(e_invarg)); + } else if (!ends_excmd(*arg)) { + // ":let var1 var2" + arg = (char *)list_arg_vars(eap, (const char *)arg, &first); + } else if (!eap->skip) { + // ":let" + list_glob_vars(&first); + list_buf_vars(&first); + list_win_vars(&first); + list_tab_vars(&first); + list_script_vars(&first); + list_func_vars(&first); + list_vim_vars(&first); + } + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { + // HERE document + list_T *l = heredoc_get(eap, expr + 3); + if (l != NULL) { + tv_list_set_ret(&rettv, l); + if (!eap->skip) { + op[0] = '='; + op[1] = NUL; + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + } + tv_clear(&rettv); + } + } else { + op[0] = '='; + op[1] = NUL; + if (*expr != '=') { + if (vim_strchr("+-*/%.", *expr) != NULL) { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') { // ..= + expr++; + } + } + expr = skipwhite(expr + 2); + } else { + expr = skipwhite(expr + 1); + } + + if (eap->skip) { + emsg_skip++; + } + i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); + if (eap->skip) { + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; + } else if (i != FAIL) { + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + tv_clear(&rettv); + } + } +} + +/// Assign the typevalue "tv" to the variable or variables at "arg_start". +/// Handles both "var" with any type and "[var, var; var]" with a list type. +/// When "op" is not NULL it points to a string with characters that +/// must appear after the variable(s). Use "+", "-" or "." for add, subtract +/// or concatenate. +/// +/// @param copy copy values from "tv", don't move +/// @param semicolon from skip_var_list() +/// @param var_count from skip_var_list() +/// @param is_const lock variables for :const +/// +/// @return OK or FAIL; +int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int is_const, + char *op) +{ + char *arg = arg_start; + typval_T ltv; + + if (*arg != '[') { + // ":let var = expr" or ":for var in list" + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { + return FAIL; + } + return OK; + } + + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { + emsg(_(e_listreq)); + return FAIL; + } + list_T *const l = tv->vval.v_list; + + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { + emsg(_("E687: Less targets than List items")); + return FAIL; + } + if (var_count - semicolon > len) { + emsg(_("E688: More targets than List items")); + return FAIL; + } + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". + assert(l != NULL); + + listitem_T *item = tv_list_first(l); + size_t rest_len = (size_t)tv_list_len(l); + while (*arg != ']') { + arg = skipwhite(arg + 1); + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op); + if (arg == NULL) { + return FAIL; + } + rest_len--; + + item = TV_LIST_ITEM_NEXT(l, item); + arg = skipwhite(arg); + if (*arg == ';') { + // Put the rest of the list (may be empty) in the var after ';'. + // Create a new list for this. + list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len); + while (item != NULL) { + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); + } + + ltv.v_type = VAR_LIST; + ltv.v_lock = VAR_UNLOCKED; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); + + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, "]", op); + tv_clear(<v); + if (arg == NULL) { + return FAIL; + } + break; + } else if (*arg != ',' && *arg != ']') { + internal_error("ex_let_vars()"); + return FAIL; + } + } + + return OK; +} + +/// Skip over assignable variable "var" or list of variables "[var, var]". +/// Used for ":let varvar = expr" and ":for varvar in expr". +/// For "[var, var]" increment "*var_count" for each variable. +/// for "[var, var; var]" set "semicolon". +/// +/// @return NULL for an error. +const char *skip_var_list(const char *arg, int *var_count, int *semicolon) +{ + const char *p; + const char *s; + + if (*arg == '[') { + // "[var, var]": find the matching ']'. + p = arg; + for (;;) { + p = skipwhite(p + 1); // skip whites after '[', ';' or ',' + s = skip_var_one((char *)p); + if (s == p) { + semsg(_(e_invarg2), p); + return NULL; + } + (*var_count)++; + + p = skipwhite(s); + if (*p == ']') { + break; + } else if (*p == ';') { + if (*semicolon == 1) { + emsg(_("E452: Double ; in list of variables")); + return NULL; + } + *semicolon = 1; + } else if (*p != ',') { + semsg(_(e_invarg2), p); + return NULL; + } + } + return p + 1; + } else { + return skip_var_one((char *)arg); + } +} + +/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, +/// l[idx]. +static const char *skip_var_one(const char *arg) +{ + if (*arg == '@' && arg[1] != NUL) { + return arg + 1 + utfc_ptr2len(arg + 1); + } + return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, + NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); +} + +/// List variables for hashtab "ht" with prefix "prefix". +/// +/// @param empty if true also list NULL strings as empty strings. +void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) +{ + hashitem_T *hi; + dictitem_T *di; + int todo; + + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0 && !got_int; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + di = TV_DICT_HI2DI(hi); + char buf[IOSIZE]; + + // apply :filter /pat/ to variable name + xstrlcpy(buf, prefix, IOSIZE); + xstrlcat(buf, (char *)di->di_key, IOSIZE); + if (message_filtered((char_u *)buf)) { + continue; + } + + if (empty || di->di_tv.v_type != VAR_STRING + || di->di_tv.vval.v_string != NULL) { + list_one_var(di, prefix, first); + } + } + } +} + +/// List global variables. +static void list_glob_vars(int *first) +{ + list_hashtable_vars(&globvarht, "", true, first); +} + +/// List buffer variables. +static void list_buf_vars(int *first) +{ + list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); +} + +/// List window variables. +static void list_win_vars(int *first) +{ + list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); +} + +/// List tab page variables. +static void list_tab_vars(int *first) +{ + list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); +} + +/// List variables in "arg". +static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) +{ + int error = false; + int len; + const char *name; + const char *name_start; + typval_T tv; + + while (!ends_excmd(*arg) && !got_int) { + if (error || eap->skip) { + arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); + if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) { + emsg_severe = true; + emsg(_(e_trailing)); + break; + } + } else { + // get_name_len() takes care of expanding curly braces + name_start = name = arg; + char *tofree; + len = get_name_len(&arg, &tofree, true, true); + if (len <= 0) { + // This is mainly to keep test 49 working: when expanding + // curly braces fails overrule the exception error message. + if (len < 0 && !aborting()) { + emsg_severe = true; + semsg(_(e_invarg2), arg); + break; + } + error = true; + } else { + if (tofree != NULL) { + name = tofree; + } + if (get_var_tv(name, len, &tv, NULL, true, false) + == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) + const char *const arg_subsc = arg; + if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { + error = true; + } else { + if (arg == arg_subsc && len == 2 && name[1] == ':') { + switch (*name) { + case 'g': + list_glob_vars(first); break; + case 'b': + list_buf_vars(first); break; + case 'w': + list_win_vars(first); break; + case 't': + list_tab_vars(first); break; + case 'v': + list_vim_vars(first); break; + case 's': + list_script_vars(first); break; + case 'l': + list_func_vars(first); break; + default: + semsg(_("E738: Can't list variables for %s"), name); + } + } else { + char *const s = encode_tv2echo(&tv, NULL); + const char *const used_name = (arg == arg_subsc + ? name + : name_start); + assert(used_name != NULL); + const ptrdiff_t name_size = (used_name == tofree + ? (ptrdiff_t)strlen(used_name) + : (arg - used_name)); + list_one_var_a("", used_name, name_size, + tv.v_type, s == NULL ? "" : s, first); + xfree(s); + } + tv_clear(&tv); + } + } + } + + xfree(tofree); + } + + arg = (const char *)skipwhite(arg); + } + + return arg; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *arg_end = NULL; + int len; + int opt_flags; + char *tofree = NULL; + + // ":let $VAR = expr": Set environment variable. + if (*arg == '$') { + if (is_const) { + emsg(_("E996: Cannot lock an environment variable")); + return NULL; + } + // Find the end of the name. + arg++; + char *name = arg; + len = get_env_len((const char **)&arg); + if (len == 0) { + semsg(_(e_invarg2), name - 1); + } else { + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg)) == NULL) { + emsg(_(e_letunexp)); + } else if (!check_secure()) { + const char c1 = name[len]; + name[len] = NUL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = vim_getenv(name); + + if (s != NULL) { + tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; + xfree(s); + } + } + if (p != NULL) { + os_setenv(name, p, 1); + if (STRICMP(name, "HOME") == 0) { + init_homedir(); + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } + arg_end = arg; + } + name[len] = c1; + xfree(tofree); + } + } + // ":let &option = expr": Set option value. + // ":let &l:option = expr": Set local option value. + // ":let &g:option = expr": Set global option value. + } else if (*arg == '&') { + if (is_const) { + emsg(_("E996: Cannot lock an option")); + return NULL; + } + // Find the end of the name. + char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); + if (p == NULL + || (endchars != NULL + && vim_strchr(endchars, *skipwhite(p)) == NULL)) { + emsg(_(e_letunexp)); + } else { + varnumber_T n = 0; + getoption_T opt_type; + long numval; + char *stringval = NULL; + const char *s = NULL; + bool failed = false; + + const char c1 = *p; + *p = NUL; + + opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + if (opt_type == gov_bool + || opt_type == gov_number + || opt_type == gov_hidden_bool + || opt_type == gov_hidden_number) { + // number, possibly hidden + n = (long)tv_get_number(tv); + } + + // Avoid setting a string option to the text "v:false" or similar. + if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + s = tv_get_string_chk(tv); + } + + if (op != NULL && *op != '=') { + if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.') + || (opt_type == gov_string && *op != '.')) { + semsg(_(e_letwrong), op); + failed = true; // don't set the value + } else { + // number or bool + if (opt_type == gov_number || opt_type == gov_bool) { + switch (*op) { + case '+': + n = numval + n; break; + case '-': + n = numval - n; break; + case '*': + n = numval * n; break; + case '/': + n = num_divide(numval, n); break; + case '%': + n = num_modulus(numval, n); break; + } + s = NULL; + } else if (opt_type == gov_string && stringval != NULL && s != NULL) { + // string + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; + } + } + } + + if (!failed) { + if (opt_type != gov_string || s != NULL) { + set_option_value(arg, n, s, opt_flags); + arg_end = p; + } else { + emsg(_(e_stringreq)); + } + } + *p = c1; + xfree(stringval); + } + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + if (is_const) { + emsg(_("E996: Cannot lock a register")); + return NULL; + } + arg++; + + int regname = utf_ptr2char(arg); + int mblen = utf_ptr2len(arg); + + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) { + emsg(_(e_letunexp)); + } else { + char *s; + + char *ptofree = NULL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc); + if (s != NULL) { + ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); + p = (const char *)ptofree; + xfree(s); + } + } + if (p != NULL) { + write_reg_contents(*arg == '@' ? '"' : regname, + (const char_u *)p, (ssize_t)STRLEN(p), false); + arg_end = arg + mblen; + } + xfree(ptofree); + } + // ":let var = expr": Set internal variable. + // ":let {expr} = expr": Idem, name made with curly braces + } else if (eval_isnamec1(*arg) || *arg == '{') { + lval_T lv; + + char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); + if (p != NULL && lv.ll_name != NULL) { + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { + emsg(_(e_letunexp)); + } else { + set_var_lval(&lv, p, tv, copy, is_const, op); + arg_end = p; + } + } + clear_lval(&lv); + } else { + semsg(_(e_invarg2), arg); + } + + return arg_end; +} + +/// ":unlet[!] var1 ... " command. +void ex_unlet(exarg_T *eap) +{ + ex_unletlock(eap, eap->arg, 0, do_unlet_var); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// ":lockvar" and ":unlockvar" commands +void ex_lockvar(exarg_T *eap) +{ + char *arg = eap->arg; + int deep = 2; + + if (eap->forceit) { + deep = -1; + } else if (ascii_isdigit(*arg)) { + deep = getdigits_int(&arg, false, -1); + arg = skipwhite(arg); + } + + ex_unletlock(eap, arg, deep, do_lock_var); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Common parsing logic for :unlet, :lockvar and :unlockvar. +/// +/// Invokes `callback` afterwards if successful and `eap->skip == false`. +/// +/// @param[in] eap Ex command arguments for the command. +/// @param[in] argstart Start of the string argument for the command. +/// @param[in] deep Levels to (un)lock for :(un)lockvar, -1 to (un)lock +/// everything. +/// @param[in] callback Appropriate handler for the command. +static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_callback callback) + FUNC_ATTR_NONNULL_ALL +{ + char *arg = argstart; + char *name_end; + bool error = false; + lval_T lv; + + do { + if (*arg == '$') { + lv.ll_name = (const char *)arg; + lv.ll_tv = NULL; + arg++; + if (get_env_len((const char **)&arg) == 0) { + semsg(_(e_invarg2), arg - 1); + return; + } + if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) { + error = true; + } + name_end = arg; + } else { + // Parse the name and find the end. + name_end = get_lval(arg, NULL, &lv, true, eap->skip || error, + 0, FNE_CHECK_START); + if (lv.ll_name == NULL) { + error = true; // error, but continue parsing. + } + if (name_end == NULL + || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { + if (name_end != NULL) { + emsg_severe = true; + emsg(_(e_trailing)); + } + if (!(eap->skip || error)) { + clear_lval(&lv); + } + break; + } + + if (!error && !eap->skip && callback(&lv, name_end, eap, deep) == FAIL) { + error = true; + } + + if (!eap->skip) { + clear_lval(&lv); + } + } + arg = skipwhite(name_end); + } while (!ends_excmd(*arg)); + + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Unlet a variable indicated by `lp`. +/// +/// @param[in] lp The lvalue. +/// @param[in] name_end End of the string argument for the command. +/// @param[in] eap Ex command arguments for :unlet. +/// @param[in] deep Unused. +/// +/// @return OK on success, or FAIL on failure. +static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ATTR_UNUSED) + FUNC_ATTR_NONNULL_ALL +{ + int forceit = eap->forceit; + int ret = OK; + int cc; + + if (lp->ll_tv == NULL) { + cc = (char_u)(*name_end); + *name_end = NUL; + + // Environment variable, normal name or expanded name. + if (*lp->ll_name == '$') { + os_unsetenv(lp->ll_name + 1); + } else if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { + ret = FAIL; + } + *name_end = (char)cc; + } else if ((lp->ll_list != NULL + // ll_list is not NULL when lvalue is not in a list, NULL lists + // yield E689. + && var_check_lock(tv_list_locked(lp->ll_list), + lp->ll_name, + lp->ll_name_len)) + || (lp->ll_dict != NULL + && var_check_lock(lp->ll_dict->dv_lock, + lp->ll_name, + lp->ll_name_len))) { + return FAIL; + } else if (lp->ll_range) { + assert(lp->ll_list != NULL); + // Delete a range of List items. + listitem_T *const first_li = lp->ll_li; + listitem_T *last_li = first_li; + for (;;) { + listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + lp->ll_name, + lp->ll_name_len)) { + return false; + } + lp->ll_li = li; + lp->ll_n1++; + if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { + break; + } else { + last_li = lp->ll_li; + } + } + tv_list_remove_items(lp->ll_list, first_li, last_li); + } else { + if (lp->ll_list != NULL) { + // unlet a List item. + tv_list_item_remove(lp->ll_list, lp->ll_li); + } else { + // unlet a Dictionary item. + dict_T *d = lp->ll_dict; + assert(d != NULL); + dictitem_T *di = lp->ll_di; + bool watched = tv_dict_is_watched(d); + char *key = NULL; + typval_T oldtv; + + if (watched) { + tv_copy(&di->di_tv, &oldtv); + // need to save key because dictitem_remove will free it + key = xstrdup((char *)di->di_key); + } + + tv_dict_item_remove(d, di); + + if (watched) { + tv_dict_watcher_notify(d, key, NULL, &oldtv); + tv_clear(&oldtv); + xfree(key); + } + } + } + + return ret; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// unlet a variable +/// +/// @param[in] name Variable name to unlet. +/// @param[in] name_len Variable name length. +/// @param[in] forceit If true, do not complain if variable doesn’t exist. +/// +/// @return OK if it existed, FAIL otherwise. +int do_unlet(const char *const name, const size_t name_len, const bool forceit) + FUNC_ATTR_NONNULL_ALL +{ + const char *varname; + dict_T *dict; + hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); + + if (ht != NULL && *varname != NUL) { + dict_T *d = get_current_funccal_dict(ht); + if (d == NULL) { + if (ht == &globvarht) { + d = &globvardict; + } else if (is_compatht(ht)) { + d = &vimvardict; + } else { + dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); + d = di->di_tv.vval.v_dict; + } + if (d == NULL) { + internal_error("do_unlet()"); + return FAIL; + } + } + + hashitem_T *hi = hash_find(ht, varname); + if (HASHITEM_EMPTY(hi)) { + hi = find_hi_in_scoped_ht(name, &ht); + } + if (hi != NULL && !HASHITEM_EMPTY(hi)) { + dictitem_T *const di = TV_DICT_HI2DI(hi); + if (var_check_fixed(di->di_flags, name, TV_CSTRING) + || var_check_ro(di->di_flags, name, TV_CSTRING) + || var_check_lock(d->dv_lock, name, TV_CSTRING)) { + return FAIL; + } + + if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { + return FAIL; + } + + typval_T oldtv; + bool watched = tv_dict_is_watched(dict); + + if (watched) { + tv_copy(&di->di_tv, &oldtv); + } + + delete_var(ht, hi); + + if (watched) { + tv_dict_watcher_notify(dict, varname, NULL, &oldtv); + tv_clear(&oldtv); + } + return OK; + } + } + if (forceit) { + return OK; + } + semsg(_("E108: No such variable: \"%s\""), name); + return FAIL; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Lock or unlock variable indicated by `lp`. +/// +/// Locks if `eap->cmdidx == CMD_lockvar`, unlocks otherwise. +/// +/// @param[in] lp The lvalue. +/// @param[in] name_end Unused. +/// @param[in] eap Ex command arguments for :(un)lockvar. +/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. +/// +/// @return OK on success, or FAIL on failure. +static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap, int deep) + FUNC_ATTR_NONNULL_ARG(1, 3) +{ + bool lock = eap->cmdidx == CMD_lockvar; + int ret = OK; + + if (deep == 0) { // Nothing to do. + return OK; + } + + if (lp->ll_tv == NULL) { + if (*lp->ll_name == '$') { + semsg(_(e_lock_unlock), lp->ll_name); + ret = FAIL; + } else { + // Normal name or expanded name. + dictitem_T *const di = find_var(lp->ll_name, lp->ll_name_len, NULL, + true); + if (di == NULL) { + ret = FAIL; + } else if ((di->di_flags & DI_FLAGS_FIX) + && di->di_tv.v_type != VAR_DICT + && di->di_tv.v_type != VAR_LIST) { + // For historical reasons this error is not given for Lists and + // Dictionaries. E.g. b: dictionary may be locked/unlocked. + semsg(_(e_lock_unlock), lp->ll_name); + ret = FAIL; + } else { + if (lock) { + di->di_flags |= DI_FLAGS_LOCK; + } else { + di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK); + } + tv_item_lock(&di->di_tv, deep, lock, false); + } + } + } else if (lp->ll_range) { + listitem_T *li = lp->ll_li; + + // (un)lock a range of List items. + while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { + tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock, false); + li = TV_LIST_ITEM_NEXT(lp->ll_list, li); + lp->ll_n1++; + } + } else if (lp->ll_list != NULL) { + // (un)lock a List item. + tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock, false); + } else { + // (un)lock a Dictionary item. + tv_item_lock(&lp->ll_di->di_tv, deep, lock, false); + } + + return ret; +} + +/// Get the value of internal variable "name". +/// Return OK or FAIL. If OK is returned "rettv" must be cleared. +/// +/// @param len length of "name" +/// @param rettv NULL when only checking existence +/// @param dip non-NULL when typval's dict item is needed +/// @param verbose may give error message +/// @param no_autoload do not use script autoloading +int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose, + bool no_autoload) +{ + int ret = OK; + typval_T *tv = NULL; + dictitem_T *v; + + v = find_var(name, (size_t)len, NULL, no_autoload); + if (v != NULL) { + tv = &v->di_tv; + if (dip != NULL) { + *dip = v; + } + } + + if (tv == NULL) { + if (rettv != NULL && verbose) { + semsg(_("E121: Undefined variable: %.*s"), len, name); + } + ret = FAIL; + } else if (rettv != NULL) { + tv_copy(tv, rettv); + } + + return ret; +} + +/// @return the string value of a (global/local) variable or +/// NULL when it doesn't exist. +/// +/// @see tv_get_string() for how long the pointer remains valid. +char_u *get_var_value(const char *const name) +{ + dictitem_T *v; + + v = find_var(name, strlen(name), NULL, false); + if (v == NULL) { + return NULL; + } + return (char_u *)tv_get_string(&v->di_tv); +} + +/// Clean up a list of internal variables. +/// Frees all allocated variables and the value they contain. +/// Clears hashtab "ht", does not free it. +void vars_clear(hashtab_T *ht) +{ + vars_clear_ext(ht, true); +} + +/// Like vars_clear(), but only free the value if "free_val" is TRUE. +void vars_clear_ext(hashtab_T *ht, int free_val) +{ + int todo; + hashitem_T *hi; + dictitem_T *v; + + hash_lock(ht); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; hi++) { + 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. + v = TV_DICT_HI2DI(hi); + if (free_val) { + tv_clear(&v->di_tv); + } + if (v->di_flags & DI_FLAGS_ALLOC) { + xfree(v); + } + } + } + hash_clear(ht); + ht->ht_used = 0; +} + +/// Delete a variable from hashtab "ht" at item "hi". +/// Clear the variable value and free the dictitem. +void delete_var(hashtab_T *ht, hashitem_T *hi) +{ + dictitem_T *di = TV_DICT_HI2DI(hi); + + hash_remove(ht, hi); + tv_clear(&di->di_tv); + xfree(di); +} + +/// List the value of one internal variable. +static void list_one_var(dictitem_T *v, const char *prefix, int *first) +{ + char *const s = encode_tv2echo(&v->di_tv, NULL); + list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)STRLEN(v->di_key), + v->di_tv.v_type, (s == NULL ? "" : s), first); + xfree(s); +} + +/// @param[in] name_len Length of the name. May be -1, in this case strlen() +/// will be used. +/// @param[in,out] first When true clear rest of screen and set to false. +static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len, + const VarType type, const char *string, int *first) +{ + // don't use msg() or msg_attr() to avoid overwriting "v:statusmsg" + msg_start(); + msg_puts(prefix); + if (name != NULL) { // "a:" vars don't have a name stored + msg_puts_attr_len(name, name_len, 0); + } + msg_putchar(' '); + msg_advance(22); + if (type == VAR_NUMBER) { + msg_putchar('#'); + } else if (type == VAR_FUNC || type == VAR_PARTIAL) { + msg_putchar('*'); + } else if (type == VAR_LIST) { + msg_putchar('['); + if (*string == '[') { + string++; + } + } else if (type == VAR_DICT) { + msg_putchar('{'); + if (*string == '{') { + string++; + } + } else { + msg_putchar(' '); + } + + msg_outtrans((char *)string); + + if (type == VAR_FUNC || type == VAR_PARTIAL) { + msg_puts("()"); + } + if (*first) { + msg_clr_eos(); + *first = false; + } +} + +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +void set_var(const char *name, const size_t name_len, typval_T *const tv, const bool copy) + FUNC_ATTR_NONNULL_ALL +{ + set_var_const(name, name_len, tv, copy, false); +} + +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +/// @param[in] is_const True if value in tv is to be locked. +void set_var_const(const char *name, const size_t name_len, typval_T *const tv, const bool copy, + const bool is_const) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *v; + hashtab_T *ht; + dict_T *dict; + + const char *varname; + ht = find_var_ht_dict(name, name_len, &varname, &dict); + const bool watched = tv_dict_is_watched(dict); + + if (ht == NULL || *varname == NUL) { + semsg(_(e_illvar), name); + return; + } + v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); + + // Search in parent scope which is possible to reference from lambda + if (v == NULL) { + v = find_var_in_scoped_ht(name, name_len, true); + } + + if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { + return; + } + + typval_T oldtv = TV_INITIAL_VALUE; + if (v != NULL) { + if (is_const) { + emsg(_(e_cannot_mod)); + return; + } + + // existing variable, need to clear the value + if (var_check_ro(v->di_flags, name, name_len) + || var_check_lock(v->di_tv.v_lock, name, name_len)) { + return; + } + + // Handle setting internal v: variables separately where needed to + // prevent changing the type. + if (is_vimvarht(ht)) { + if (v->di_tv.v_type == VAR_STRING) { + XFREE_CLEAR(v->di_tv.vval.v_string); + if (copy || tv->v_type != VAR_STRING) { + const char *const val = tv_get_string(tv); + + // Careful: when assigning to v:errmsg and tv_get_string() + // causes an error message the variable will already be set. + if (v->di_tv.vval.v_string == NULL) { + v->di_tv.vval.v_string = xstrdup(val); + } + } else { + // Take over the string to avoid an extra alloc/free. + v->di_tv.vval.v_string = tv->vval.v_string; + tv->vval.v_string = NULL; + } + return; + } else if (v->di_tv.v_type == VAR_NUMBER) { + v->di_tv.vval.v_number = tv_get_number(tv); + if (strcmp(varname, "searchforward") == 0) { + set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); + } else if (strcmp(varname, "hlsearch") == 0) { + no_hlsearch = !v->di_tv.vval.v_number; + redraw_all_later(SOME_VALID); + } + return; + } else if (v->di_tv.v_type != tv->v_type) { + semsg(_("E963: setting %s to value with wrong type"), name); + return; + } + } + + if (watched) { + tv_copy(&v->di_tv, &oldtv); + } + tv_clear(&v->di_tv); + } else { // Add a new variable. + // Can't add "v:" or "a:" variable. + if (is_vimvarht(ht) || ht == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + return; + } + + // Make sure the variable name is valid. + if (!valid_varname(varname)) { + return; + } + + // Make sure dict is valid + assert(dict != NULL); + + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); + STRCPY(v->di_key, varname); + if (tv_dict_add(dict, v) == FAIL) { + xfree(v); + return; + } + v->di_flags = DI_FLAGS_ALLOC; + if (is_const) { + v->di_flags |= DI_FLAGS_LOCK; + } + } + + if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { + tv_copy(tv, &v->di_tv); + } else { + v->di_tv = *tv; + v->di_tv.v_lock = VAR_UNLOCKED; + tv_init(tv); + } + + if (watched) { + if (oldtv.v_type == VAR_UNKNOWN) { + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); + } else { + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_clear(&oldtv); + } + } + + if (is_const) { + // Like :lockvar! name: lock the value and what it contains, but only + // if the reference count is up to one. That locks only literal + // values. + tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true); + } +} + +/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX) +/// +/// Also gives an error message. +/// +/// @param[in] flags di_flags attribute value. +/// @param[in] name Variable name, for use in error message. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. +/// +/// @return True if variable is read-only: either always or in sandbox when +/// sandbox is enabled, false otherwise. +bool var_check_ro(const int flags, const char *name, size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + const char *error_message = NULL; + if (flags & DI_FLAGS_RO) { + error_message = _(e_readonlyvar); + } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) { + error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\""); + } + + if (error_message == NULL) { + return false; + } + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + + semsg(_(error_message), (int)name_len, name); + + return true; +} + +/// Check whether variable is fixed (DI_FLAGS_FIX) +/// +/// Also gives an error message. +/// +/// @param[in] flags di_flags attribute value. +/// @param[in] name Variable name, for use in error message. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. +/// +/// @return True if variable is fixed, false otherwise. +bool var_check_fixed(const int flags, const char *name, size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + if (flags & DI_FLAGS_FIX) { + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + semsg(_("E795: Cannot delete variable %.*s"), (int)name_len, name); + return true; + } + return false; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Check if name is a valid name to assign funcref to +/// +/// @param[in] name Possible function/funcref name. +/// @param[in] new_var True if it is a name for a variable. +/// +/// @return false in case of error, true in case of success. Also gives an +/// error message if appropriate. +bool var_check_func_name(const char *const name, const bool new_var) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Allow for w: b: s: and t:. + if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':') + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) { + semsg(_("E704: Funcref variable name must start with a capital: %s"), name); + return false; + } + // Don't allow hiding a function. When "v" is not NULL we might be + // assigning another function to the same var, the type is checked + // below. + if (new_var && function_exists(name, false)) { + semsg(_("E705: Variable name conflicts with existing function: %s"), name); + return false; + } + return true; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Check if a variable name is valid +/// +/// @param[in] varname Variable name to check. +/// +/// @return false when variable name is not valid, true when it is. Also gives +/// an error message if appropriate. +bool valid_varname(const char *varname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + for (const char *p = varname; *p != NUL; p++) { + if (!eval_isnamec1((int)(uint8_t)(*p)) + && (p == varname || !ascii_isdigit(*p)) + && *p != AUTOLOAD_CHAR) { + semsg(_(e_illvar), varname); + return false; + } + } + return true; +} + +/// Implements the logic to retrieve local variable and option values. +/// Used by "getwinvar()" "gettabvar()" "gettabwinvar()" "getbufvar()". +/// +/// @param deftv default value if not found +/// @param htname 't'ab, 'w'indow or 'b'uffer local +/// @param tp can be NULL +/// @param buf ignored if htname is not 'b' +static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv, int htname, + tabpage_T *tp, win_T *win, buf_T *buf) +{ + bool done = false; + const bool do_change_curbuf = buf != NULL && htname == 'b'; + + emsg_off++; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (varname != NULL && tp != NULL && win != NULL && (htname != 'b' || buf != NULL)) { + // 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. + // If we have a buffer reference avoid the switching, we're saving and + // restoring curbuf directly. + const bool need_switch_win = !(tp == curtab && win == curwin) && !do_change_curbuf; + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { + if (*varname == '&' && htname != 't') { + buf_T *const save_curbuf = curbuf; + + // Change curbuf so the option is read from the correct buffer. + if (do_change_curbuf) { + curbuf = buf; + } + + if (varname[1] == NUL) { + // get all window-local or buffer-local options in a dict + dict_T *opts = get_winbuf_options(htname == 'b'); + + if (opts != NULL) { + tv_dict_set_ret(rettv, opts); + done = true; + } + } else if (get_option_tv(&varname, rettv, true) == OK) { + // Local option + done = true; + } + + curbuf = save_curbuf; + } else if (*varname == NUL) { + const ScopeDictDictItem *v; + // Empty string: return a dict with all the local variables. + if (htname == 'b') { + v = &buf->b_bufvar; + } else if (htname == 'w') { + v = &win->w_winvar; + } else { + v = &tp->tp_winvar; + } + tv_copy(&v->di_tv, rettv); + done = true; + } else { + hashtab_T *ht; + + if (htname == 'b') { + ht = &buf->b_vars->dv_hashtab; + } else if (htname == 'w') { + ht = &win->w_vars->dv_hashtab; + } else { + ht = &tp->tp_vars->dv_hashtab; + } + + // Look up the variable. + const dictitem_T *const v = find_var_in_ht(ht, htname, varname, strlen(varname), false); + if (v != NULL) { + tv_copy(&v->di_tv, rettv); + done = true; + } + } + } + + if (need_switch_win) { + // restore previous notion of curwin + restore_win(&switchwin, true); + } + } + + if (!done && deftv->v_type != VAR_UNKNOWN) { + // use the default value + tv_copy(deftv, rettv); + } + + emsg_off--; +} + +/// getwinvar() and gettabwinvar() +/// +/// @param off 1 for gettabwinvar() +static void getwinvar(typval_T *argvars, typval_T *rettv, int off) +{ + tabpage_T *tp; + + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { + tp = curtab; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *const varname = tv_get_string_chk(&argvars[off + 1]); + + get_var_from(varname, rettv, &argvars[off + 2], 'w', tp, win, NULL); +} + +/// Set option "varname" to the value of "varp" for the current buffer/window. +static void set_option_from_tv(const char *varname, typval_T *varp) +{ + long numval = 0; + const char *strval; + bool error = false; + char nbuf[NUMBUFLEN]; + + if (varp->v_type == VAR_BOOL) { + if (is_string_option(varname)) { + emsg(_(e_stringreq)); + return; + } + numval = (long)varp->vval.v_number; + strval = "0"; // avoid using "false" + } else { + numval = (long)tv_get_number_chk(varp, &error); + strval = tv_get_string_buf_chk(varp, nbuf); + } + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } +} + +/// "setwinvar()" and "settabwinvar()" functions +static void setwinvar(typval_T *argvars, typval_T *rettv, int off) +{ + if (check_secure()) { + return; + } + + tabpage_T *tp = NULL; + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { + tp = curtab; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *varname = tv_get_string_chk(&argvars[off + 1]); + typval_T *varp = &argvars[off + 2]; + + if (win != NULL && varname != NULL && varp != NULL) { + bool need_switch_win = !(tp == curtab && win == curwin); + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { + if (*varname == '&') { + set_option_from_tv(varname + 1, varp); + } else { + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); + set_var(winvarname, varname_len + 2, varp, true); + xfree(winvarname); + } + } + if (need_switch_win) { + restore_win(&switchwin, true); + } + } +} + +bool var_exists(const char *var) + FUNC_ATTR_NONNULL_ALL +{ + char *tofree; + bool n = false; + + // get_name_len() takes care of expanding curly braces + const char *name = var; + const int len = get_name_len(&var, &tofree, true, false); + if (len > 0) { + typval_T tv; + + if (tofree != NULL) { + name = tofree; + } + n = get_var_tv(name, len, &tv, NULL, false, true) == OK; + if (n) { + // Handle d.key, l[idx], f(expr). + n = handle_subscript(&var, &tv, true, false, name, &name) == OK; + if (n) { + tv_clear(&tv); + } + } + } + if (*var != NUL) { + n = false; + } + + xfree(tofree); + return n; +} + +/// "gettabvar()" function +void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *const varname = tv_get_string_chk(&argvars[1]); + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + win_T *win = NULL; + + if (tp != NULL) { + win = tp == curtab || tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; + } + + get_var_from(varname, rettv, &argvars[2], 't', tp, win, NULL); +} + +/// "gettabwinvar()" function +void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getwinvar(argvars, rettv, 1); +} + +/// "getwinvar()" function +void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getwinvar(argvars, rettv, 0); +} + +/// "getbufvar()" function +void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *const varname = tv_get_string_chk(&argvars[1]); + buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); + + get_var_from(varname, rettv, &argvars[2], 'b', curtab, curwin, buf); +} + +/// "settabvar()" function +void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_number = 0; + + if (check_secure()) { + return; + } + + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + const char *const varname = tv_get_string_chk(&argvars[1]); + typval_T *const varp = &argvars[2]; + + if (varname != NULL && tp != NULL) { + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); + + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varname_len + 2, varp, true); + xfree(tabvarname); + + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); + } + } +} + +/// "settabwinvar()" function +void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + setwinvar(argvars, rettv, 1); +} + +/// "setwinvar()" function +void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + setwinvar(argvars, rettv, 0); +} + +/// "setbufvar()" function +void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (check_secure() + || !tv_check_str_or_nr(&argvars[0])) { + return; + } + const char *varname = tv_get_string_chk(&argvars[1]); + buf_T *const buf = tv_get_buf(&argvars[0], false); + typval_T *varp = &argvars[2]; + + if (buf != NULL && varname != NULL) { + if (*varname == '&') { + aco_save_T aco; + + // set curbuf to be our buf, temporarily + aucmd_prepbuf(&aco, buf); + + set_option_from_tv(varname + 1, varp); + + // reset notion of buffer + aucmd_restbuf(&aco); + } else { + const size_t varname_len = STRLEN(varname); + char *const bufvarname = xmalloc(varname_len + 3); + buf_T *const save_curbuf = curbuf; + curbuf = buf; + memcpy(bufvarname, "b:", 2); + memcpy(bufvarname + 2, varname, varname_len + 1); + set_var(bufvarname, varname_len + 2, varp, true); + xfree(bufvarname); + curbuf = save_curbuf; + } + } +} diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h new file mode 100644 index 0000000000..73efc4938a --- /dev/null +++ b/src/nvim/eval/vars.h @@ -0,0 +1,9 @@ +#ifndef NVIM_EVAL_VARS_H +#define NVIM_EVAL_VARS_H + +#include "nvim/ex_cmds_defs.h" // For exarg_T + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/vars.h.generated.h" +#endif +#endif // NVIM_EVAL_VARS_H diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 28e1893b31..23e7660606 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1583,22 +1583,39 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), } + + const char *const cmd_args = strchr(cmd, ' '); + len += (is_pwsh && cmd_args) + ? STRLEN(" -ArgumentList ") + 2 // two extra quotes + : 0; + char *const buf = xmalloc(len); -#if defined(UNIX) - // Put delimiters around the command (for concatenated commands) when - // redirecting input and/or output. if (is_pwsh) { xstrlcpy(buf, "Start-Process ", len); - xstrlcat(buf, cmd, len); + if (cmd_args == NULL) { + xstrlcat(buf, cmd, len); + } else { + xstrlcpy(buf + STRLEN(buf), cmd, (size_t)(cmd_args - cmd + 1)); + xstrlcat(buf, " -ArgumentList \"", len); + xstrlcat(buf, cmd_args + 1, len); // +1 to skip the leading space. + xstrlcat(buf, "\"", len); + } +#if defined(UNIX) + // Put delimiters around the command (for concatenated commands) when + // redirecting input and/or output. } else if (itmp != NULL || otmp != NULL) { char *fmt = is_fish_shell ? "begin; %s; end" : "(%s)"; vim_snprintf(buf, len, fmt, cmd); +#endif + // For shells that don't understand braces around commands, at least allow + // the use of commands in a pipe. } else { xstrlcpy(buf, cmd, len); } +#if defined(UNIX) if (itmp != NULL) { if (is_pwsh) { xstrlcat(buf, " -RedirectStandardInput ", len - 1); @@ -1608,14 +1625,6 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) xstrlcat(buf, itmp, len - 1); } #else - // For shells that don't understand braces around commands, at least allow - // the use of commands in a pipe. - if (is_pwsh) { - xstrlcpy(buf, "Start-Process ", len); - xstrlcat(buf, cmd, len); - } else { - xstrlcpy(buf, cmd, len); - } if (itmp != NULL) { // If there is a pipe, we have to put the '<' in front of it. // Don't do this when 'shellquote' is not empty, otherwise the @@ -2716,6 +2725,12 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum // Assume success now retval = OK; + // If the file name was changed, reset the not-edit flag so that ":write" + // works. + if (!other_file) { + curbuf->b_flags &= ~BF_NOTEDITED; + } + /* * Check if we are editing the w_arg_idx file in the argument list. */ @@ -3582,7 +3597,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T // check for a trailing count cmd = skipwhite(cmd); if (ascii_isdigit(*cmd)) { - i = getdigits_long((char_u **)&cmd, true, 0); + i = getdigits_long(&cmd, true, 0); if (i <= 0 && !eap->skip && subflags.do_error) { emsg(_(e_zerocount)); return 0; @@ -3635,7 +3650,6 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T assert(sub != NULL); - bool sub_needs_free = false; char *sub_copy = NULL; // If the substitute pattern starts with "\=" then it's an expression. @@ -3647,14 +3661,15 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T sub = xstrdup(sub); sub_copy = sub; } else { - char *source = sub; - sub = (char *)regtilde((char_u *)sub, p_magic, cmdpreview); - // When previewing, the new pattern allocated by regtilde() needs to be freed - // in this function because it will not be used or freed by regtilde() later. - sub_needs_free = cmdpreview && sub != source; + char *newsub = (char *)regtilde((char_u *)sub, p_magic, cmdpreview); + if (newsub != sub) { + // newsub was allocated, free it later. + sub_copy = newsub; + sub = newsub; + } } - bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages); + const bool cmdheight0 = !ui_has_messages(); if (cmdheight0) { // If cmdheight is 0, cmdheight must be set to 1 when we enter command line. set_option_value("ch", 1L, NULL, 0); @@ -4450,9 +4465,6 @@ skip: vim_regfree(regmatch.regprog); xfree(sub_copy); - if (sub_needs_free) { - xfree(sub); - } // Restore the flag values, they can be used for ":&&". subflags.do_all = save_do_all; @@ -4846,14 +4858,14 @@ void ex_help(exarg_T *eap) semsg(_("E149: Sorry, no help for %s"), arg); } if (n != FAIL) { - FreeWild(num_matches, (char_u **)matches); + FreeWild(num_matches, matches); } return; } // The first match (in the requested language) is the best match. tag = xstrdup(matches[i]); - FreeWild(num_matches, (char_u **)matches); + FreeWild(num_matches, matches); /* * Re-use an existing help window or open a new one. @@ -5042,7 +5054,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep int i; // Specific tags that either have a specific replacement or won't go - // throught the generic rules. + // through the generic rules. static char *(except_tbl[][2]) = { { "*", "star" }, { "g*", "gstar" }, @@ -5275,7 +5287,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep if (keep_lang) { flags |= TAG_KEEP_LANG; } - if (find_tags(IObuff, num_matches, (char_u ***)matches, flags, MAXCOL, NULL) == OK + if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK && *num_matches > 0) { // Sort the matches found on the heuristic number that is after the // tag name. @@ -5412,8 +5424,8 @@ void fix_help_buffer(void) // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. char *buff_list[1] = { (char *)NameBuff }; - if (gen_expand_wildcards(1, (char_u **)buff_list, &fcount, - (char_u ***)&fnames, EW_FILE|EW_SILENT) == OK + if (gen_expand_wildcards(1, buff_list, &fcount, + &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { // If foo.abx is found use it instead of foo.txt in // the same directory. @@ -5514,7 +5526,7 @@ void fix_help_buffer(void) } fclose(fd); } - FreeWild(fcount, (char_u **)fnames); + FreeWild(fcount, fnames); } } xfree(rt); @@ -5568,12 +5580,15 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. char *buff_list[1] = { (char *)NameBuff }; - if (gen_expand_wildcards(1, (char_u **)buff_list, &filecount, (char_u ***)&files, - EW_FILE|EW_SILENT) == FAIL - || filecount == 0) { + const int res = gen_expand_wildcards(1, buff_list, &filecount, &files, + EW_FILE|EW_SILENT); + if (res == FAIL || filecount == 0) { if (!got_int) { semsg(_("E151: No match: %s"), NameBuff); } + if (res != FAIL) { + FreeWild(filecount, files); + } return; } @@ -5593,7 +5608,7 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool if (!ignore_writeerr) { semsg(_("E152: Cannot open %s for writing"), NameBuff); } - FreeWild(filecount, (char_u **)files); + FreeWild(filecount, files); return; } @@ -5683,11 +5698,11 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool fclose(fd); } - FreeWild(filecount, (char_u **)files); + FreeWild(filecount, files); if (!got_int && ga.ga_data != NULL) { // Sort the tags. - sort_strings((char_u **)ga.ga_data, ga.ga_len); + sort_strings(ga.ga_data, ga.ga_len); // Check for duplicates. for (int i = 1; i < ga.ga_len; i++) { @@ -5762,7 +5777,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr) // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. char *buff_list[1] = { (char *)NameBuff }; - if (gen_expand_wildcards(1, (char_u **)buff_list, &filecount, (char_u ***)&files, + if (gen_expand_wildcards(1, buff_list, &filecount, &files, EW_FILE|EW_SILENT) == FAIL || filecount == 0) { semsg(_("E151: No match: %s"), NameBuff); @@ -5778,6 +5793,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr) if (len <= 4) { continue; } + if (STRICMP(files[i] + len - 4, ".txt") == 0) { // ".txt" -> language "en" lang[0] = 'e'; @@ -5828,7 +5844,7 @@ static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr) } ga_clear(&ga); - FreeWild(filecount, (char_u **)files); + FreeWild(filecount, files); } static void helptags_cb(char *fname, void *cookie) diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e57dc5d13f..730a2f1b69 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -24,6 +24,7 @@ #include "nvim/charset.h" #include "nvim/debugger.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_eval.h" @@ -856,7 +857,7 @@ static void get_arglist(garray_T *gap, char *str, int escaped) /// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'. /// /// @return FAIL or OK. -int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) +int get_arglist_exp(char_u *str, int *fcountp, char ***fnamesp, bool wig) { garray_T ga; int i; @@ -864,10 +865,10 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) get_arglist(&ga, (char *)str, true); if (wig) { - i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, + i = expand_wildcards(ga.ga_len, ga.ga_data, fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); } else { - i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, + i = gen_expand_wildcards(ga.ga_len, ga.ga_data, fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); } @@ -949,8 +950,8 @@ static int do_arglist(char *str, int what, int after, bool will_edit) } ga_clear(&new_ga); } else { - int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, - &exp_count, (char_u ***)&exp_files, + int i = expand_wildcards(new_ga.ga_len, new_ga.ga_data, + &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); ga_clear(&new_ga); if (i == FAIL || exp_count == 0) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c4d2821e79..a7d91a47d7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -21,6 +21,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/ex_cmds.h" @@ -1395,7 +1396,7 @@ static int parse_count(exarg_T *eap, char **errormsg, bool validate) if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg) && (!(eap->argt & EX_BUFNAME) || *(p = skipdigits(eap->arg + 1)) == NUL || ascii_iswhite(*p))) { - n = getdigits_long((char_u **)&eap->arg, false, -1); + n = getdigits_long(&eap->arg, false, -1); eap->arg = skipwhite(eap->arg); if (n <= 0 && (eap->argt & EX_ZEROR) == 0) { if (errormsg != NULL) { @@ -1623,7 +1624,7 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) // If filename expansion is enabled, expand filenames if (cmdinfo->magic.file) { - if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) { + if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL) { goto end; } } @@ -2289,7 +2290,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } if (ea.argt & EX_XFILE) { - if (expand_filename(&ea, (char_u **)cmdlinep, &errormsg) == FAIL) { + if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) { goto doend; } } @@ -2852,11 +2853,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) if (!mark_check(fm)) { goto theend; } + assert(fm != NULL); eap->line1 = fm->mark.lnum; fm = mark_get_visual(curbuf, '>'); if (!mark_check(fm)) { goto theend; } + assert(fm != NULL); eap->line2 = fm->mark.lnum; eap->addr_count++; } @@ -3049,6 +3052,7 @@ char *find_ex_command(exarg_T *eap, int *full) } else { eap->cmdidx = CMD_bang; } + assert(eap->cmdidx >= 0); for (; (int)eap->cmdidx < CMD_SIZE; eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) { @@ -3268,6 +3272,7 @@ int cmd_exists(const char *const name) // For ":2match" and ":3match" we need to skip the number. ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name); ea.cmdidx = (cmdidx_T)0; + ea.flags = 0; int full = false; p = find_ex_command(&ea, &full); if (p == NULL) { @@ -3301,6 +3306,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; ea.cmdidx = (cmdidx_T)0; + ea.flags = 0; char *p = find_ex_command(&ea, NULL); if (p == NULL || ea.cmdidx == CMD_SIZE) { return; @@ -4390,6 +4396,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int cmd = NULL; goto error; } + assert(fm != NULL); lnum = fm->mark.lnum; } } @@ -4482,7 +4489,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int default: if (ascii_isdigit(*cmd)) { // absolute line number - lnum = (linenr_T)getdigits((char_u **)&cmd, false, 0); + lnum = (linenr_T)getdigits(&cmd, false, 0); } } @@ -4731,78 +4738,46 @@ static char *skip_grep_pat(exarg_T *eap) /// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option /// in the command line, so that things like % get expanded. -char *replace_makeprg(exarg_T *eap, char *p, char **cmdlinep) +char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep) { - char *new_cmdline; - char *program; - char *pos; - char *ptr; - int len; - size_t i; + bool isgrep = eap->cmdidx == CMD_grep + || eap->cmdidx == CMD_lgrep + || eap->cmdidx == CMD_grepadd + || eap->cmdidx == CMD_lgrepadd; - /* - * Don't do it when ":vimgrep" is used for ":grep". - */ - if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake - || eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep - || eap->cmdidx == CMD_grepadd - || eap->cmdidx == CMD_lgrepadd) + // Don't do it when ":vimgrep" is used for ":grep". + if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake || isgrep) && !grep_internal(eap->cmdidx)) { - if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep - || eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd) { - if (*curbuf->b_p_gp == NUL) { - program = (char *)p_gp; - } else { - program = (char *)curbuf->b_p_gp; - } - } else { - if (*curbuf->b_p_mp == NUL) { - program = (char *)p_mp; - } else { - program = (char *)curbuf->b_p_mp; - } - } + const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? (char *)p_gp : (char *)curbuf->b_p_gp) + : (*curbuf->b_p_mp == NUL ? (char *)p_mp : (char *)curbuf->b_p_mp); - p = skipwhite(p); + arg = skipwhite(arg); - if ((pos = strstr(program, "$*")) != NULL) { - // replace $* by given arguments - i = 1; - while ((pos = strstr(pos + 2, "$*")) != NULL) { - i++; - } - len = (int)STRLEN(p); - new_cmdline = xmalloc(STRLEN(program) + i * (size_t)(len - 2) + 1); - ptr = new_cmdline; - while ((pos = strstr(program, "$*")) != NULL) { - i = (size_t)(pos - program); - memcpy(ptr, program, i); - STRCPY(ptr += i, p); - ptr += len; - program = pos + 2; - } - STRCPY(ptr, program); - } else { - new_cmdline = xmalloc(STRLEN(program) + STRLEN(p) + 2); + char *new_cmdline; + // Replace $* by given arguments + if ((new_cmdline = strrep(program, "$*", arg)) == NULL) { + // No $* in arg, build "<makeprg> <arg>" instead + new_cmdline = xmalloc(STRLEN(program) + STRLEN(arg) + 2); STRCPY(new_cmdline, program); STRCAT(new_cmdline, " "); - STRCAT(new_cmdline, p); + STRCAT(new_cmdline, arg); } - msg_make((char_u *)p); + + msg_make((char_u *)arg); // 'eap->cmd' is not set here, because it is not used at CMD_make xfree(*cmdlinep); *cmdlinep = new_cmdline; - p = new_cmdline; + arg = new_cmdline; } - return p; + return arg; } /// Expand file name in Ex command argument. /// When an error is detected, "errormsgp" is set to a non-NULL pointer. /// /// @return FAIL for failure, OK otherwise. -int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) +int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp) { int has_wildcards; // need to expand wildcards char *repl; @@ -4908,7 +4883,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) repl = l; } - p = repl_cmdline(eap, p, srclen, repl, (char **)cmdlinep); + p = repl_cmdline(eap, p, srclen, repl, cmdlinep); xfree(repl); } @@ -4935,7 +4910,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) p = NULL; } if (p != NULL) { - (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep); + (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); } } @@ -4962,7 +4937,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) if (p == NULL) { return FAIL; } - (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep); + (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); xfree(p); } } @@ -5246,7 +5221,7 @@ static int get_tabpage_arg(exarg_T *eap) } p_save = p; - tab_number = (int)getdigits((char_u **)&p, false, tab_number); + tab_number = (int)getdigits(&p, false, tab_number); if (relative == 0) { if (STRCMP(p, "$") == 0) { @@ -5942,7 +5917,7 @@ two_count: return FAIL; } - *def = getdigits_long((char_u **)&p, true, 0); + *def = getdigits_long(&p, true, 0); *argt |= EX_ZEROR; if (p != val + vallen || vallen == 0) { @@ -5968,7 +5943,7 @@ invalid_count: goto two_count; } - *def = getdigits_long((char_u **)&p, true, 0); + *def = getdigits_long(&p, true, 0); if (p != val + vallen) { goto invalid_count; @@ -6783,9 +6758,18 @@ char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) if (idx < buf->b_ucmds.ga_len) { return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; } + idx -= buf->b_ucmds.ga_len; if (idx < ucmds.ga_len) { - return (char *)USER_CMD(idx)->uc_name; + char *name = (char *)USER_CMD(idx)->uc_name; + + for (int i = 0; i < buf->b_ucmds.ga_len; i++) { + if (STRCMP(name, USER_CMD_GA(&buf->b_ucmds, i)->uc_name) == 0) { + // global command is overruled by buffer-local one + return ""; + } + } + return name; } return NULL; } @@ -7741,7 +7725,7 @@ static void ex_tabnext(exarg_T *eap) if (eap->arg && *eap->arg != NUL) { char *p = eap->arg; char *p_save = p; - tab_number = (int)getdigits((char_u **)&p, false, 0); + tab_number = (int)getdigits(&p, false, 0); if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL || tab_number == 0) { // No numbers as argument. @@ -8722,7 +8706,7 @@ static void ex_later(exarg_T *eap) if (*p == NUL) { count = 1; } else if (isdigit(*p)) { - count = getdigits_long((char_u **)&p, false, 0); + count = getdigits_long(&p, false, 0); switch (*p) { case 's': ++p; sec = true; break; @@ -9239,7 +9223,7 @@ static void ex_findpat(exarg_T *eap) n = 1; if (ascii_isdigit(*eap->arg)) { // get count - n = getdigits_long((char_u **)&eap->arg, false, 0); + n = getdigits_long(&eap->arg, false, 0); eap->arg = skipwhite(eap->arg); } if (*eap->arg == '/') { // Match regexp, not just whole words @@ -9997,7 +9981,7 @@ static void ex_setfiletype(exarg_T *eap) static void ex_digraphs(exarg_T *eap) { if (*eap->arg != NUL) { - putdigraph((char_u *)eap->arg); + putdigraph(eap->arg); } else { listdigraphs(eap->forceit); } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 7e0d3016bc..ddb25c53e4 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -1,7 +1,6 @@ #ifndef NVIM_EX_DOCMD_H #define NVIM_EX_DOCMD_H -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/globals.h" diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 4c26cfe500..6240ac6b37 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -24,6 +24,7 @@ #include "nvim/digraph.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" #include "nvim/ex_cmds.h" @@ -688,12 +689,22 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool /// @param init_ccline clear ccline first static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline) { - bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages); + const bool cmdheight0 = !ui_has_messages(); if (cmdheight0) { - // If cmdheight is 0, cmdheight must be set to 1 when we enter command line. + const long save_so = lastwin->w_p_so; + + // If cmdheight is 0, cmdheight must be set to 1 when we enter the + // command line. Set "made_cmdheight_nonzero" and reset 'scrolloff' to + // avoid scrolling the last window. + made_cmdheight_nonzero = true; + + lastwin->w_p_so = 0; set_option_value("ch", 1L, NULL, 0); update_screen(VALID); // redraw the screen NOW + + made_cmdheight_nonzero = false; + lastwin->w_p_so = save_so; } // can be invoked recursively, identify each level @@ -827,6 +838,36 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init setmouse(); ui_cursor_shape(); // may show different cursor shape + TryState tstate; + Error err = ERROR_INIT; + bool tl_ret = true; + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); + char firstcbuf[2]; + firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); + firstcbuf[1] = 0; + + if (has_event(EVENT_CMDLINEENTER)) { + // set v:event to a dictionary with information about the commandline + tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); + tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); + tv_dict_set_keys_readonly(dict); + try_enter(&tstate); + + apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf); + restore_v_event(dict, &save_v_event); + + tl_ret = try_leave(&tstate, &err); + if (!tl_ret && ERROR_SET(&err)) { + msg_putchar('\n'); + msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + api_clear_error(&err); + redrawcmd(); + } + tl_ret = true; + } + may_trigger_modechanged(); + init_history(); s->hiscnt = hislen; // set hiscnt to impossible history value s->histype = hist_char2type(s->firstc); @@ -849,6 +890,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init found_one = true; } } + + if (*p_tal != NUL) { + redraw_tabline = true; + found_one = true; + } + if (found_one) { redraw_statuslines(); } @@ -859,36 +906,6 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init s->state.check = command_line_check; s->state.execute = command_line_execute; - TryState tstate; - Error err = ERROR_INIT; - bool tl_ret = true; - save_v_event_T save_v_event; - dict_T *dict = get_v_event(&save_v_event); - char firstcbuf[2]; - firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); - firstcbuf[1] = 0; - - if (has_event(EVENT_CMDLINEENTER)) { - // set v:event to a dictionary with information about the commandline - tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); - tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); - tv_dict_set_keys_readonly(dict); - try_enter(&tstate); - - apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf); - restore_v_event(dict, &save_v_event); - - tl_ret = try_leave(&tstate, &err); - if (!tl_ret && ERROR_SET(&err)) { - msg_putchar('\n'); - msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); - api_clear_error(&err); - redrawcmd(); - } - tl_ret = true; - } - may_trigger_modechanged(); - state_enter(&s->state); if (has_event(EVENT_CMDLINELEAVE)) { @@ -958,6 +975,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init cmdpreview = save_cmdpreview; // restore preview state redraw_all_later(SOME_VALID); } + may_trigger_modechanged(); setmouse(); ui_cursor_shape(); // may show different cursor shape sb_text_end_cmdline(); @@ -983,11 +1001,14 @@ theend: } if (cmdheight0) { + made_cmdheight_nonzero = true; + // Restore cmdheight set_option_value("ch", 0L, NULL, 0); - // Redraw is needed for command line completion redraw_all_later(CLEAR); + + made_cmdheight_nonzero = false; } return p; @@ -1066,7 +1087,8 @@ static int command_line_execute(VimState *state, int key) // Don't ignore it for the input() function. if ((s->c == Ctrl_C) && s->firstc != '@' - && !s->break_ctrl_c + // do clear got_int in Ex mode to avoid infinite Ctrl-C loop + && (!s->break_ctrl_c || exmode_active) && !global_busy) { got_int = false; } @@ -3327,7 +3349,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) { Arena arena = ARENA_EMPTY; arena_start(&arena, &ui_ext_fixblk); - Array content = ARRAY_DICT_INIT; + Array content; if (cmdline_star) { content = arena_array(&arena, 1); size_t len = 0; @@ -3814,6 +3836,7 @@ void redrawcmd(void) redrawing_cmdline = true; + sb_text_restart_cmdline(); msg_start(); redrawcmdprompt(); @@ -4142,8 +4165,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode compl_selected = findex; cmdline_pum_display(false); } else if (p_wmnu) { - win_redr_status_matches(xp, xp->xp_numfiles, (char_u **)xp->xp_files, - findex, cmd_showtail); + win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail); } if (findex == -1) { return vim_strsave(orig_save); @@ -4163,7 +4185,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode // free old names if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) { - FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files); + FreeWild(xp->xp_numfiles, xp->xp_files); xp->xp_numfiles = -1; XFREE_CLEAR(orig_save); } @@ -4181,8 +4203,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode /* * Do the expansion. */ - if (ExpandFromContext(xp, str, &xp->xp_numfiles, (char_u ***)&xp->xp_files, - options) == FAIL) { + if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) { #ifdef FNAME_ILLEGAL /* Illegal file name has been silently skipped. But when there * are wildcards, the real problem is that there was no match, @@ -4198,7 +4219,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode } } else { // Escape the matches for use on the command line. - ExpandEscape(xp, str, xp->xp_numfiles, (char_u **)xp->xp_files, options); + ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options); /* * Check for matching suffixes in file names. @@ -4323,12 +4344,12 @@ void ExpandInit(expand_T *xp) void ExpandCleanup(expand_T *xp) { if (xp->xp_numfiles >= 0) { - FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files); + FreeWild(xp->xp_numfiles, xp->xp_files); xp->xp_numfiles = -1; } } -void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int options) +void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char **files, int options) { int i; char_u *p; @@ -4354,9 +4375,9 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o for (i = 0; i < numfiles; ++i) { // for ":set path=" we need to escape spaces twice if (xp->xp_backslash == XP_BS_THREE) { - p = vim_strsave_escaped(files[i], (char_u *)" "); + p = vim_strsave_escaped((char_u *)files[i], (char_u *)" "); xfree(files[i]); - files[i] = p; + files[i] = (char *)p; #if defined(BACKSLASH_IN_FILENAME) p = vim_strsave_escaped(files[i], (char_u *)" "); xfree(files[i]); @@ -4370,7 +4391,7 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o xp->xp_shell ? VSE_SHELL : vse_what); #endif xfree(files[i]); - files[i] = p; + files[i] = (char *)p; /* If 'str' starts with "\~", replace "~" at start of * files[i] with "\~". */ @@ -4390,10 +4411,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o * Insert a backslash before characters in a tag name that * would terminate the ":tag" command. */ - for (i = 0; i < numfiles; ++i) { - p = vim_strsave_escaped(files[i], (char_u *)"\\|\""); + for (i = 0; i < numfiles; i++) { + p = vim_strsave_escaped((char_u *)files[i], (char_u *)"\\|\""); xfree(files[i]); - files[i] = p; + files[i] = (char *)p; } } } @@ -4449,7 +4470,7 @@ char *vim_strsave_fnameescape(const char *const fname, const int what) // '>' and '+' are special at the start of some commands, e.g. ":edit" and // ":write". "cd -" has a special meaning. if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) { - escape_fname((char_u **)&p); + escape_fname(&p); } return p; @@ -4458,28 +4479,26 @@ char *vim_strsave_fnameescape(const char *const fname, const int what) /* * Put a backslash before the file name in "pp", which is in allocated memory. */ -static void escape_fname(char_u **pp) +static void escape_fname(char **pp) { char_u *p = xmalloc(STRLEN(*pp) + 2); p[0] = '\\'; STRCPY(p + 1, *pp); xfree(*pp); - *pp = p; + *pp = (char *)p; } -/* - * For each file name in files[num_files]: - * If 'orig_pat' starts with "~/", replace the home directory with "~". - */ -void tilde_replace(char_u *orig_pat, int num_files, char_u **files) +/// For each file name in files[num_files]: +/// If 'orig_pat' starts with "~/", replace the home directory with "~". +void tilde_replace(char_u *orig_pat, int num_files, char **files) { char *p; if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) { for (int i = 0; i < num_files; i++) { - p = home_replace_save(NULL, (char *)files[i]); + p = home_replace_save(NULL, files[i]); xfree(files[i]); - files[i] = (char_u *)p; + files[i] = p; } } } @@ -4500,7 +4519,7 @@ static int showmatches(expand_T *xp, int wildmenu) #define L_SHOWFILE(m) (showtail \ ? sm_gettail(files_found[m], false) : files_found[m]) int num_files; - char_u **files_found; + char **files_found; int i, j, k; int maxlen; int lines; @@ -4520,7 +4539,7 @@ static int showmatches(expand_T *xp, int wildmenu) } } else { num_files = xp->xp_numfiles; - files_found = (char_u **)xp->xp_files; + files_found = xp->xp_files; showtail = cmd_showtail; } @@ -4535,10 +4554,9 @@ static int showmatches(expand_T *xp, int wildmenu) compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); for (i = 0; i < num_files; i++) { - compl_match_array[i].pum_text = L_SHOWFILE(i); + compl_match_array[i].pum_text = (char_u *)L_SHOWFILE(i); } - char_u *endpos = (showtail - ? sm_gettail((char_u *)xp->xp_pattern, true) : (char_u *)xp->xp_pattern); + char_u *endpos = (char_u *)(showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern); if (ui_has(kUICmdline)) { compl_startcol = (int)(endpos - ccline.cmdbuff); } else { @@ -4570,10 +4588,10 @@ static int showmatches(expand_T *xp, int wildmenu) if (!showtail && (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_BUFFERS)) { - home_replace(NULL, (char *)files_found[i], (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, files_found[i], (char *)NameBuff, MAXPATHL, true); j = vim_strsize((char *)NameBuff); } else { - j = vim_strsize((char *)L_SHOWFILE(i)); + j = vim_strsize(L_SHOWFILE(i)); } if (j > maxlen) { maxlen = j; @@ -4606,8 +4624,8 @@ static int showmatches(expand_T *xp, int wildmenu) lastlen = 999; for (k = i; k < num_files; k += lines) { if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D)); - p = files_found[k] + STRLEN(files_found[k]) + 1; + msg_outtrans_attr((char_u *)files_found[k], HL_ATTR(HLF_D)); + p = (char_u *)files_found[k] + STRLEN(files_found[k]) + 1; msg_advance(maxlen + 1); msg_puts((const char *)p); msg_advance(maxlen + 3); @@ -4625,8 +4643,8 @@ static int showmatches(expand_T *xp, int wildmenu) // Expansion was done before and special characters // were escaped, need to halve backslashes. Also // $HOME has been replaced with ~/. - char_u *exp_path = expand_env_save_opt(files_found[k], true); - char_u *path = exp_path != NULL ? exp_path : files_found[k]; + char_u *exp_path = expand_env_save_opt((char_u *)files_found[k], true); + char_u *path = exp_path != NULL ? exp_path : (char_u *)files_found[k]; char_u *halved_slash = backslash_halve_save(path); j = os_isdir(halved_slash); xfree(exp_path); @@ -4635,17 +4653,17 @@ static int showmatches(expand_T *xp, int wildmenu) } } else { // Expansion was done here, file names are literal. - j = os_isdir(files_found[k]); + j = os_isdir((char_u *)files_found[k]); } if (showtail) { - p = L_SHOWFILE(k); + p = (char_u *)L_SHOWFILE(k); } else { - home_replace(NULL, (char *)files_found[k], (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, files_found[k], (char *)NameBuff, MAXPATHL, true); p = NameBuff; } } else { - j = FALSE; - p = L_SHOWFILE(k); + j = false; + p = (char_u *)L_SHOWFILE(k); } lastlen = msg_outtrans_attr(p, j ? attr : 0); } @@ -4674,17 +4692,15 @@ static int showmatches(expand_T *xp, int wildmenu) return EXPAND_OK; } -/* - * Private path_tail for showmatches() (and win_redr_status_matches()): - * Find tail of file name path, but ignore trailing "/". - */ -char_u *sm_gettail(char_u *s, bool eager) +/// Private path_tail for showmatches() (and win_redr_status_matches()): +/// Find tail of file name path, but ignore trailing "/". +char *sm_gettail(char *s, bool eager) { char_u *p; - char_u *t = s; - int had_sep = FALSE; + char_u *t = (char_u *)s; + int had_sep = false; - for (p = s; *p != NUL;) { + for (p = (char_u *)s; *p != NUL;) { if (vim_ispathsep(*p) #ifdef BACKSLASH_IN_FILENAME && !rem_backslash(p) @@ -4701,7 +4717,7 @@ char_u *sm_gettail(char_u *s, bool eager) } MB_PTR_ADV(p); } - return t; + return (char *)t; } /* @@ -4979,7 +4995,7 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline /// @param col position of cursor /// @param matchcount return: nr of matches /// @param matches return: array of pointers to matches -int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u ***matches) +int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***matches) { char_u *file_str = NULL; int options = WILD_ADD_SLASH|WILD_SILENT; @@ -5015,7 +5031,7 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u * // Cleanup matches for help tags: // Remove "@ab" if the top of 'helplang' is "ab" and the language of the first // tag matches it. Otherwise remove "@en" if "en" is the only language. -static void cleanup_help_tags(int num_file, char_u **file) +static void cleanup_help_tags(int num_file, char **file) { char_u buf[4]; char_u *p = buf; @@ -5069,7 +5085,7 @@ typedef char *(*ExpandFunc)(expand_T *, int); /// Do the expansion based on xp->xp_context and "pat". /// /// @param options WILD_ flags -static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ***file, int options) +static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***file, int options) { regmatch_T regmatch; int ret; @@ -5164,7 +5180,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** /* With an empty argument we would get all the help tags, which is * very slow. Get matches for "help" instead. */ if (find_help_tags(*pat == NUL ? "help" : (char *)pat, - num_file, (char ***)file, false) == OK) { + num_file, file, false) == OK) { cleanup_help_tags(*num_file, *file); return OK; } @@ -5181,10 +5197,10 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** return OK; } if (xp->xp_context == EXPAND_BUFFERS) { - return ExpandBufnames((char *)pat, num_file, (char ***)file, options); + return ExpandBufnames((char *)pat, num_file, file, options); } if (xp->xp_context == EXPAND_DIFF_BUFFERS) { - return ExpandBufnames((char *)pat, num_file, (char ***)file, options | BUF_DIFF_FILTER); + return ExpandBufnames((char *)pat, num_file, file, options | BUF_DIFF_FILTER); } if (xp->xp_context == EXPAND_TAGS || xp->xp_context == EXPAND_TAGS_LISTFILES) { @@ -5192,8 +5208,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** } if (xp->xp_context == EXPAND_COLORS) { char *directories[] = { "colors", NULL }; - return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, - directories); + return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories); } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = { "compiler", NULL }; @@ -5300,8 +5315,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** if (tab[i].ic) { regmatch.rm_ic = TRUE; } - ExpandGeneric(xp, ®match, num_file, file, tab[i].func, - tab[i].escaped); + ExpandGeneric(xp, ®match, num_file, file, tab[i].func, tab[i].escaped); ret = OK; break; } @@ -5321,7 +5335,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** /// program. Matching strings are copied into an array, which is returned. /// /// @param func returns a string from the list -static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file, +static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file, CompleteListItemGetter func, int escaped) { int i; @@ -5346,7 +5360,7 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha } assert(count < INT_MAX); *num_file = (int)count; - *file = (char_u **)xmalloc(count * sizeof(char_u *)); + *file = xmalloc(count * sizeof(char_u *)); // copy the matching names into allocated memory count = 0; @@ -5364,7 +5378,7 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha } else { str = vim_strsave(str); } - (*file)[count++] = str; + (*file)[count++] = (char *)str; if (func == get_menu_names) { // Test for separator added by get_menu_names(). str += STRLEN(str) - 1; @@ -5401,14 +5415,14 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha /// *file will either be set to NULL or point to /// allocated memory. /// @param flagsarg is a combination of EW_* flags. -static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int flagsarg) +static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int flagsarg) FUNC_ATTR_NONNULL_ALL { char_u *pat; int i; char_u *path = NULL; garray_T ga; - char_u *buf = xmalloc(MAXPATHL); + char *buf = xmalloc(MAXPATHL); size_t l; char_u *s, *e; int flags = flagsarg; @@ -5475,7 +5489,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int break; } STRLCPY(buf, s, l + 1); - add_pathsep((char *)buf); + add_pathsep(buf); l = STRLEN(buf); STRLCPY(buf + l, pat, MAXPATHL - l); @@ -5485,7 +5499,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int ga_grow(&ga, *num_file); { for (i = 0; i < *num_file; i++) { - char_u *name = (*file)[i]; + char_u *name = (char_u *)(*file)[i]; if (STRLEN(name) > l) { // Check if this name was already found. @@ -5524,7 +5538,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int /// Call "user_expand_func()" to invoke a user defined Vim script function and /// return the result (either a string, a List or NULL). static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file, - char_u ***file) + char ***file) FUNC_ATTR_NONNULL_ALL { char_u keep = 0; @@ -5565,10 +5579,8 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T return ret; } -/* - * Expand names with a function defined by the user. - */ -static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file) +/// Expand names with a function defined by the user. +static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file) { char_u *e; garray_T ga; @@ -5606,10 +5618,8 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, return OK; } -/* - * Expand names with a list returned by a function defined by the user. - */ -static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) +/// Expand names with a list returned by a function defined by the user. +static int ExpandUserList(expand_T *xp, int *num_file, char ***file) { list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, num_file, file); @@ -5635,7 +5645,7 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) return OK; } -static int ExpandUserLua(expand_T *xp, int *num_file, char_u ***file) +static int ExpandUserLua(expand_T *xp, int *num_file, char ***file) { typval_T rettv; nlua_call_user_expand_func(xp, &rettv); @@ -5673,7 +5683,7 @@ static int ExpandUserLua(expand_T *xp, int *num_file, char_u ***file) /// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim /// When "flags" has DIP_LUA: search also performed for .lua files /// "dirnames" is an array with one or more directory names. -static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char *dirnames[]) +static int ExpandRTDir(char_u *pat, int flags, int *num_file, char ***file, char *dirnames[]) { *num_file = 0; *file = NULL; @@ -5782,7 +5792,7 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, ch /// Expand loadplugin names: /// 'packpath'/pack/ * /opt/{pat} -static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file) +static int ExpandPackAddDir(char_u *pat, int *num_file, char ***file) { garray_T ga; @@ -5836,7 +5846,7 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) add_pathsep((char *)buf); STRCAT(buf, file); // NOLINT - char_u **p; + char **p; int num_p = 0; (void)ExpandFromContext(&xpc, buf, &num_p, &p, WILD_SILENT | expand_options); @@ -5847,7 +5857,7 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) ga_grow(ga, num_p); // take over the pointers and put them in "ga" for (int i = 0; i < num_p; i++) { - ((char_u **)ga->ga_data)[ga->ga_len] = p[i]; + ((char_u **)ga->ga_data)[ga->ga_len] = (char_u *)p[i]; ga->ga_len++; } xfree(p); @@ -6802,9 +6812,13 @@ static int open_cmdwin(void) // Avoid command-line window first character being concealed. curwin->w_p_cole = 0; + // First go back to the original window. wp = curwin; set_bufref(&bufref, curbuf); win_goto(old_curwin); + + // win_goto() may trigger an autocommand that already closes the + // cmdline window. if (win_valid(wp) && wp != curwin) { win_close(wp, true, false); } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 6ca6da9cd0..1d670afa6d 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -193,6 +193,9 @@ static int ses_do_win(win_T *wp) if (bt_help(wp->w_buffer)) { return ssop_flags & SSOP_HELP; } + if (bt_terminal(wp->w_buffer)) { + return ssop_flags & SSOP_TERMINAL; + } return true; } @@ -407,6 +410,8 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr if ((flagp == &ssop_flags) && alt != NULL && alt->b_fname != NULL && *alt->b_fname != NUL && alt->b_p_bl + // do not set balt if buffer is terminal and "terminal" is not set in options + && !(bt_terminal(alt) && !(ssop_flags & SSOP_TERMINAL)) && (fputs("balt ", fd) < 0 || ses_fname(fd, alt, flagp, true) == FAIL)) { return FAIL; @@ -616,6 +621,7 @@ static int makeopens(FILE *fd, char_u *dirnow) FOR_ALL_BUFFERS(buf) { if (!(only_save_windows && buf->b_nwindows == 0) && !(buf->b_help && !(ssop_flags & SSOP_HELP)) + && !(bt_terminal(buf) && !(ssop_flags & SSOP_TERMINAL)) && buf->b_fname != NULL && buf->b_p_bl) { if (fprintf(fd, "badd +%" PRId64 " ", diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index ca276b8a40..ebefd1157c 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -82,12 +82,11 @@ typedef struct ff_stack { char_u *ffs_fix_path; char_u *ffs_wc_path; - /* files/dirs found in the above directory, matched by the first wildcard - * of wc_part - */ - char_u **ffs_filearray; + // files/dirs found in the above directory, matched by the first wildcard + // of wc_part + char **ffs_filearray; int ffs_filearray_size; - char_u ffs_filearray_cur; // needed for partly handled dirs + int ffs_filearray_cur; // needed for partly handled dirs /* to store status of partly handled directories * 0: we work on this directory for the first time @@ -683,12 +682,12 @@ char_u *vim_findfile(void *search_ctx_arg) * to handle the expansion of '**' into an empty string. */ if (stackp->ffs_filearray == NULL) { - char_u *dirptrs[2]; + char *dirptrs[2]; /* we use filepath to build the path expand_wildcards() should * expand. */ - dirptrs[0] = file_path; + dirptrs[0] = (char *)file_path; dirptrs[1] = NULL; // if we have a start dir copy it in @@ -743,7 +742,7 @@ char_u *vim_findfile(void *search_ctx_arg) if (stackp->ffs_star_star_empty == 0) { // if not done before, expand '**' to empty stackp->ffs_star_star_empty = 1; - dirptrs[1] = stackp->ffs_fix_path; + dirptrs[1] = (char *)stackp->ffs_fix_path; } } @@ -773,9 +772,9 @@ char_u *vim_findfile(void *search_ctx_arg) * Expand wildcards like "*" and "$VAR". * If the path is a URL don't try this. */ - if (path_with_url((char *)dirptrs[0])) { + if (path_with_url(dirptrs[0])) { stackp->ffs_filearray = xmalloc(sizeof(char *)); - stackp->ffs_filearray[0] = vim_strsave(dirptrs[0]); + stackp->ffs_filearray[0] = xstrdup(dirptrs[0]); stackp->ffs_filearray_size = 1; } else { /* Add EW_NOTWILD because the expanded path may contain @@ -801,10 +800,9 @@ char_u *vim_findfile(void *search_ctx_arg) * We don't have further wildcards to expand, so we have to * check for the final file now. */ - for (int i = stackp->ffs_filearray_cur; - i < stackp->ffs_filearray_size; ++i) { - if (!path_with_url((char *)stackp->ffs_filearray[i]) - && !os_isdir(stackp->ffs_filearray[i])) { + for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) { + if (!path_with_url(stackp->ffs_filearray[i]) + && !os_isdir((char_u *)stackp->ffs_filearray[i])) { continue; // not a directory } // prepare the filename to be checked for existence below @@ -862,8 +860,8 @@ char_u *vim_findfile(void *search_ctx_arg) #endif // push dir to examine rest of subdirs later - assert(i < UCHAR_MAX - 1); - stackp->ffs_filearray_cur = (char_u)(i + 1); + assert(i < INT_MAX); + stackp->ffs_filearray_cur = i + 1; ff_push(search_ctx, stackp); if (!path_with_url((char *)file_path)) { @@ -897,17 +895,13 @@ char_u *vim_findfile(void *search_ctx_arg) } } } else { - /* - * still wildcards left, push the directories for further - * search - */ - for (int i = stackp->ffs_filearray_cur; - i < stackp->ffs_filearray_size; ++i) { - if (!os_isdir(stackp->ffs_filearray[i])) { + // still wildcards left, push the directories for further search + for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) { + if (!os_isdir((char_u *)stackp->ffs_filearray[i])) { continue; // not a directory } ff_push(search_ctx, - ff_create_stack_element(stackp->ffs_filearray[i], + ff_create_stack_element((char_u *)stackp->ffs_filearray[i], rest_of_wildcards, stackp->ffs_level - 1, 0)); } @@ -927,11 +921,11 @@ char_u *vim_findfile(void *search_ctx_arg) stackp->ffs_fix_path) == 0) { continue; // don't repush same directory } - if (!os_isdir(stackp->ffs_filearray[i])) { + if (!os_isdir((char_u *)stackp->ffs_filearray[i])) { continue; // not a directory } ff_push(search_ctx, - ff_create_stack_element(stackp->ffs_filearray[i], + ff_create_stack_element((char_u *)stackp->ffs_filearray[i], stackp->ffs_wc_path, stackp->ffs_level - 1, 1)); } } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 6782465ef1..b98984017b 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -710,7 +710,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, fenc_alloced = false; } else { fenc_next = (char *)p_fencs; // try items in 'fileencodings' - fenc = (char *)next_fenc((char_u **)&fenc_next, &fenc_alloced); + fenc = (char *)next_fenc(&fenc_next, &fenc_alloced); } /* @@ -804,7 +804,7 @@ retry: xfree(fenc); } if (fenc_next != NULL) { - fenc = (char *)next_fenc((char_u **)&fenc_next, &fenc_alloced); + fenc = (char *)next_fenc(&fenc_next, &fenc_alloced); } else { fenc = ""; fenc_alloced = false; @@ -2060,7 +2060,7 @@ void set_forced_fenc(exarg_T *eap) /// NULL. /// When *pp is not set to NULL, the result is in allocated memory and "alloced" /// is set to true. -static char_u *next_fenc(char_u **pp, bool *alloced) +static char_u *next_fenc(char **pp, bool *alloced) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { char_u *p; @@ -2071,13 +2071,13 @@ static char_u *next_fenc(char_u **pp, bool *alloced) *pp = NULL; return (char_u *)""; } - p = (char_u *)vim_strchr((char *)(*pp), ','); + p = (char_u *)vim_strchr((*pp), ','); if (p == NULL) { - r = enc_canonize(*pp); + r = enc_canonize((char_u *)(*pp)); *pp += STRLEN(*pp); } else { - r = vim_strnsave(*pp, (size_t)(p - *pp)); - *pp = p + 1; + r = vim_strnsave((char_u *)(*pp), (size_t)(p - (char_u *)(*pp))); + *pp = (char *)p + 1; p = enc_canonize(r); xfree(r); r = p; @@ -5270,7 +5270,6 @@ void forward_slash(char_u *fname) return; } for (p = fname; *p != NUL; p++) { - // The Big5 encoding can have '\' in the trail byte. if (*p == '\\') { *p = '/'; } @@ -5296,6 +5295,9 @@ static void vim_mktempdir(void) char user[40] = { 0 }; (void)os_get_username(user, sizeof(user)); + // Usernames may contain slashes! #19240 + memchrsub(user, '/', '_', sizeof(user)); + memchrsub(user, '\\', '_', sizeof(user)); // Make sure the umask doesn't remove the executable bit. // "repl" has been reported to use "0177". @@ -5398,7 +5400,7 @@ int readdir_core(garray_T *gap, const char *path, void *context, CheckItem check os_closedir(&dir); if (gap->ga_len > 0) { - sort_strings((char_u **)gap->ga_data, gap->ga_len); + sort_strings(gap->ga_data, gap->ga_len); } return OK; diff --git a/src/nvim/fold.h b/src/nvim/fold.h index 60ea4b322e..f34e6d43c3 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -18,7 +18,7 @@ typedef struct foldinfo { // other fields are invalid int fi_low_level; // lowest fold level that starts in the same // line - long fi_lines; + linenr_T fi_lines; } foldinfo_T; #define FOLDINFO_INIT { 0, 0, 0, 0 } diff --git a/src/nvim/garray.c b/src/nvim/garray.c index 0c76e1a919..7a3c14b1bb 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -116,7 +116,7 @@ void ga_grow(garray_T *gap, int n) /// @param gap void ga_remove_duplicate_strings(garray_T *gap) { - char_u **fnames = gap->ga_data; + char **fnames = gap->ga_data; // sort the growing array, which puts duplicates next to each other sort_strings(fnames, gap->ga_len); diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 4cf282770d..b167767f7a 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -91,7 +91,7 @@ local deprecated_aliases = require("api.dispatch_deprecated") for _,f in ipairs(shallowcopy(functions)) do local ismethod = false if startswith(f.name, "nvim_") then - if startswith(f.name, "nvim__") then + if startswith(f.name, "nvim__") or f.name == "nvim_error_event" then f.since = -1 elseif f.since == nil then print("Function "..f.name.." lacks since field.\n") @@ -149,7 +149,7 @@ local exported_attributes = {'name', 'return_type', 'method', 'since', 'deprecated_since'} local exported_functions = {} for _,f in ipairs(functions) do - if not startswith(f.name, "nvim__") then + if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event") then local f_exported = {} for _,attr in ipairs(exported_attributes) do f_exported[attr] = f[attr] diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 00372d4f3d..28ff0cbd59 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -21,6 +21,7 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/lua/executor.h" #include "nvim/main.h" @@ -29,6 +30,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -1583,16 +1585,18 @@ int vgetc(void) vgetc_char = c; } - // If mappings are enabled (i.e., not Ctrl-v) and the user directly typed - // something with a meta- or alt- modifier that was not mapped, interpret - // <M-x> as <Esc>x rather than as an unbound meta keypress. #8213 - // In Terminal mode, however, this is not desirable. #16220 - if (!no_mapping && KeyTyped && !(State & MODE_TERMINAL) - && (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) { + // If mappings are enabled (i.e., not i_CTRL-V) and the user directly typed something with + // MOD_MASK_ALT (<M-/<A- modifier) that was not mapped, interpret <M-x> as <Esc>x rather + // than as an unbound <M-x> keypress. #8213 + // In Terminal mode, however, this is not desirable. #16202 #16220 + // Also do not do this for mouse keys, as terminals encode mouse events as CSI sequences, and + // MOD_MASK_ALT has a meaning even for unmapped mouse keys. + if (!no_mapping && KeyTyped && mod_mask == MOD_MASK_ALT && !(State & MODE_TERMINAL) + && !is_mouse_key(c)) { mod_mask = 0; int len = ins_char_typebuf(c, 0); (void)ins_char_typebuf(ESC, 0); - ungetchars(len + 3); // The ALT/META modifier takes three more bytes + ungetchars(len + 3); // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes continue; } @@ -1733,7 +1737,7 @@ static bool at_ins_compl_key(void) || ((compl_cont_status & CONT_LOCAL) && (c == Ctrl_N || c == Ctrl_P)); } -/// Check if typebuf.tb_buf[] contains a modifer plus key that can be changed +/// Check if typebuf.tb_buf[] contains a modifier plus key that can be changed /// into just a key, apply that. /// Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off + "max_offset"]. /// @return the length of the replaced bytes, 0 if nothing changed, -1 for error. @@ -1807,7 +1811,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) int local_State = get_real_state(); bool is_plug_map = false; - // If typehead starts with <Plug> then remap, even for a "noremap" mapping. + // If typeahead starts with <Plug> then remap, even for a "noremap" mapping. if (typebuf.tb_len >= 3 && typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL && typebuf.tb_buf[typebuf.tb_off + 1] == KS_EXTRA diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 8d896aef31..a41836353a 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -163,10 +163,6 @@ EXTERN colnr_T dollar_vcol INIT(= -1); // by the match.) EXTERN int compl_length INIT(= 0); -// Set when character typed while looking for matches and it means we should -// 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 bool compl_busy INIT(= false); @@ -471,6 +467,9 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer #define FOR_ALL_BUFFERS_BACKWARDS(buf) \ for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev) +#define FOR_ALL_BUF_WININFO(buf, wip) \ + for ((wip) = (buf)->b_wininfo; (wip) != NULL; (wip) = (wip)->wi_next) // NOLINT + // Iterate through all the signs placed in a buffer #define FOR_ALL_SIGNS_IN_BUF(buf, sign) \ for ((sign) = (buf)->b_signlist; (sign) != NULL; (sign) = (sign)->se_next) // NOLINT @@ -945,6 +944,9 @@ EXTERN char e_loclist[] INIT(= N_("E776: No location list")); EXTERN char e_re_damg[] INIT(= N_("E43: Damaged match string")); EXTERN char e_re_corr[] INIT(= N_("E44: Corrupted regexp program")); EXTERN char e_readonly[] INIT(= N_("E45: 'readonly' option is set (add ! to override)")); +EXTERN char e_letwrong[] INIT(= N_("E734: Wrong variable type for %s=")); +EXTERN char e_illvar[] INIT(= N_("E461: Illegal variable name: %s")); +EXTERN char e_cannot_mod[] INIT(= N_("E995: Cannot modify existing variable")); EXTERN char e_readonlyvar[] INIT(= N_("E46: Cannot change read-only variable \"%.*s\"")); EXTERN char e_stringreq[] INIT(= N_("E928: String required")); EXTERN char e_dictreq[] INIT(= N_("E715: Dictionary required")); @@ -1082,4 +1084,7 @@ EXTERN char windowsVersion[20] INIT(= { 0 }); EXTERN int exit_need_delay INIT(= 0); +// Set when 'cmdheight' is changed from zero to one temporarily. +EXTERN bool made_cmdheight_nonzero INIT(= false); + #endif // NVIM_GLOBALS_H diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 1268f987e1..72e85c425d 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -230,16 +230,12 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col && *ptr != NUL) { c = *ptr; // check if this is the first byte of a multibyte - if (len > 0) { - mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr)); - } else { - mbyte_blen = utfc_ptr2len((char *)ptr); - } - if (len >= 0) { - u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)); - } else { - u8c = utfc_ptr2char(ptr, u8cc); - } + mbyte_blen = len > 0 + ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr)) + : utfc_ptr2len((char *)ptr); + u8c = len >= 0 + ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)) + : utfc_ptr2char(ptr, u8cc); mbyte_cells = utf_char2cells(u8c); if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. @@ -248,8 +244,9 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col nc = NUL; nc1 = NUL; } else { - nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc, - (int)((text + len) - ptr - mbyte_blen)); + nc = len >= 0 + ? utfc_ptr2char_len(ptr + mbyte_blen, pcc, (int)((text + len) - ptr - mbyte_blen)) + : utfc_ptr2char(ptr + mbyte_blen, pcc); nc1 = pcc[0]; } pc = prev_c; diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 1571340849..9252b8a371 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -128,4 +128,13 @@ typedef struct { const char *start; ///< Location where region starts. } StlClickRecord; +typedef struct { + int args[3]; + int icell; + int ncells; + int coloff; + int cur_attr; + int clear_width; +} GridLineEvent; + #endif // NVIM_GRID_DEFS_H diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index a4cf65e816..d7f7b8eb92 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -639,7 +639,7 @@ void ex_hardcopy(exarg_T *eap) char *errormsg = NULL; // Expand things like "%.ps". - if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) { + if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL) { if (errormsg != NULL) { emsg(errormsg); } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 0f20eb1905..71c7194479 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -183,10 +183,10 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault) bool valid_cache = it.version >= p->hl_valid; if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = INTEGER_OBJ((Integer)ns_id); - args.items[1] = STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id))); - args.items[2] = BOOLEAN_OBJ(link); + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, INTEGER_OBJ((Integer)ns_id)); + ADD_C(args, STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id)))); + ADD_C(args, BOOLEAN_OBJ(link)); // TODO(bfredl): preload the "global" attr dict? Error err = ERROR_INIT; @@ -719,93 +719,111 @@ Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err) return dic; } - return hlattrs2dict(syn_attr2entry((int)attr_id), rgb); + return hlattrs2dict(NULL, syn_attr2entry((int)attr_id), rgb); } /// Converts an HlAttrs into Dictionary /// +/// @param[out] hl optional pre-allocated dictionary for return value +/// if present, must be allocated with at least 16 elements! /// @param[in] aep data to convert /// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*' -Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) +Dictionary hlattrs2dict(Dictionary *hl_alloc, HlAttrs ae, bool use_rgb) { - Dictionary hl = ARRAY_DICT_INIT; int mask = use_rgb ? ae.rgb_ae_attr : ae.cterm_ae_attr; + Dictionary hl = ARRAY_DICT_INIT; + if (hl_alloc) { + hl = *hl_alloc; + } else { + kv_ensure_space(hl, 16); + } if (mask & HL_BOLD) { - PUT(hl, "bold", BOOLEAN_OBJ(true)); + PUT_C(hl, "bold", BOOLEAN_OBJ(true)); } if (mask & HL_STANDOUT) { - PUT(hl, "standout", BOOLEAN_OBJ(true)); + PUT_C(hl, "standout", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERLINE) { - PUT(hl, "underline", BOOLEAN_OBJ(true)); + PUT_C(hl, "underline", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERCURL) { - PUT(hl, "undercurl", BOOLEAN_OBJ(true)); + PUT_C(hl, "undercurl", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDOUBLE) { - PUT(hl, "underdouble", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdouble", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDOTTED) { - PUT(hl, "underdotted", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdotted", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDASHED) { - PUT(hl, "underdashed", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdashed", BOOLEAN_OBJ(true)); } if (mask & HL_ITALIC) { - PUT(hl, "italic", BOOLEAN_OBJ(true)); + PUT_C(hl, "italic", BOOLEAN_OBJ(true)); } if (mask & HL_INVERSE) { - PUT(hl, "reverse", BOOLEAN_OBJ(true)); + PUT_C(hl, "reverse", BOOLEAN_OBJ(true)); } if (mask & HL_STRIKETHROUGH) { - PUT(hl, "strikethrough", BOOLEAN_OBJ(true)); + PUT_C(hl, "strikethrough", BOOLEAN_OBJ(true)); + } + + if (mask & HL_NOCOMBINE) { + PUT_C(hl, "nocombine", BOOLEAN_OBJ(true)); } if (use_rgb) { if (mask & HL_FG_INDEXED) { - PUT(hl, "fg_indexed", BOOLEAN_OBJ(true)); + PUT_C(hl, "fg_indexed", BOOLEAN_OBJ(true)); } if (mask & HL_BG_INDEXED) { - PUT(hl, "bg_indexed", BOOLEAN_OBJ(true)); + PUT_C(hl, "bg_indexed", BOOLEAN_OBJ(true)); } if (ae.rgb_fg_color != -1) { - PUT(hl, "foreground", INTEGER_OBJ(ae.rgb_fg_color)); + PUT_C(hl, "foreground", INTEGER_OBJ(ae.rgb_fg_color)); } if (ae.rgb_bg_color != -1) { - PUT(hl, "background", INTEGER_OBJ(ae.rgb_bg_color)); + PUT_C(hl, "background", INTEGER_OBJ(ae.rgb_bg_color)); } if (ae.rgb_sp_color != -1) { - PUT(hl, "special", INTEGER_OBJ(ae.rgb_sp_color)); + PUT_C(hl, "special", INTEGER_OBJ(ae.rgb_sp_color)); } } else { if (ae.cterm_fg_color != 0) { - PUT(hl, "foreground", INTEGER_OBJ(ae.cterm_fg_color - 1)); + PUT_C(hl, "foreground", INTEGER_OBJ(ae.cterm_fg_color - 1)); } if (ae.cterm_bg_color != 0) { - PUT(hl, "background", INTEGER_OBJ(ae.cterm_bg_color - 1)); + PUT_C(hl, "background", INTEGER_OBJ(ae.cterm_bg_color - 1)); } } if (ae.hl_blend > -1) { - PUT(hl, "blend", INTEGER_OBJ(ae.hl_blend)); + PUT_C(hl, "blend", INTEGER_OBJ(ae.hl_blend)); } - return hl; + if (hl_alloc) { + *hl_alloc = hl; + return hl; + } else { + Dictionary allocated = copy_dictionary(hl); + kv_destroy(hl); + return allocated; + } } HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err) diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index d958b7b344..f6ec03fb14 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -9,6 +9,7 @@ #include "nvim/autocmd.h" #include "nvim/charset.h" #include "nvim/cursor_shape.h" +#include "nvim/eval/vars.h" #include "nvim/fold.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" @@ -955,8 +956,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) linep++; } size_t key_len = (size_t)(linep - key_start); - if (key_len > sizeof key - 1) { - semsg(_("E423: Illegal argument")); + if (key_len > sizeof(key) - 1) { + emsg(_("E423: Illegal argument")); error = true; break; } @@ -1003,8 +1004,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) break; } size_t arg_len = (size_t)(linep - arg_start); - if (arg_len > sizeof arg - 1) { - semsg(_("E423: Illegal argument")); + if (arg_len > sizeof(arg) - 1) { + emsg(_("E423: Illegal argument")); error = true; break; } @@ -1409,7 +1410,7 @@ Dictionary get_global_hl_defs(void) Dictionary attrs = ARRAY_DICT_INIT; HlGroup *h = &hl_table[i - 1]; if (h->sg_attr > 0) { - attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); + attrs = hlattrs2dict(NULL, syn_attr2entry(h->sg_attr), true); } else if (h->sg_link > 0) { const char *link = (const char *)hl_table[h->sg_link - 1].sg_name; PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 3c74b4bd8d..c5e030ce25 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -2002,7 +2002,7 @@ int get_c_indent(void) } // #defines and so on go at the left when included in 'cinkeys', - // exluding pragmas when customized in 'cinoptions' + // excluding pragmas when customized in 'cinoptions' if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) { const char_u *const directive = (char_u *)skipwhite((char *)theline + 1); if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0) { @@ -3569,7 +3569,7 @@ term_again: // Are we at the start of a cpp base class declaration or // constructor initialization? XXX n = 0; - if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') { + if (curbuf->b_ind_cpp_baseclass != 0) { n = cin_is_cpp_baseclass(&cache_cpp_baseclass); l = get_cursor_line_ptr(); } diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c new file mode 100644 index 0000000000..2fc8f1dadc --- /dev/null +++ b/src/nvim/insexpand.c @@ -0,0 +1,3967 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// insexpand.c: functions for Insert mode completion + +#include <assert.h> +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/change.h" +#include "nvim/charset.h" +#include "nvim/cursor.h" +#include "nvim/edit.h" +#include "nvim/eval.h" +#include "nvim/eval/typval.h" +#include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" +#include "nvim/fileio.h" +#include "nvim/getchar.h" +#include "nvim/indent.h" +#include "nvim/indent_c.h" +#include "nvim/insexpand.h" +#include "nvim/keycodes.h" +#include "nvim/mbyte.h" +#include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/move.h" +#include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/time.h" +#include "nvim/path.h" +#include "nvim/popupmnu.h" +#include "nvim/regexp.h" +#include "nvim/screen.h" +#include "nvim/search.h" +#include "nvim/spell.h" +#include "nvim/state.h" +#include "nvim/strings.h" +#include "nvim/tag.h" +#include "nvim/ui.h" +#include "nvim/undo.h" +#include "nvim/vim.h" +#include "nvim/window.h" + +// Definitions used for CTRL-X submode. +// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] +// and ctrl_x_mode_names[]. + +#define CTRL_X_WANT_IDENT 0x100 + +#define CTRL_X_NORMAL 0 ///< CTRL-N CTRL-P completion, default +#define CTRL_X_NOT_DEFINED_YET 1 +#define CTRL_X_SCROLL 2 +#define CTRL_X_WHOLE_LINE 3 +#define CTRL_X_FILES 4 +#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT) +#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT) +#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT) +#define CTRL_X_FINISHED 8 +#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT) +#define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT) +#define CTRL_X_CMDLINE 11 +#define CTRL_X_FUNCTION 12 +#define CTRL_X_OMNI 13 +#define CTRL_X_SPELL 14 +#define CTRL_X_LOCAL_MSG 15 ///< only used in "ctrl_x_msgs" +#define CTRL_X_EVAL 16 ///< for builtin function complete() +#define CTRL_X_CMDLINE_CTRL_X 17 ///< CTRL-X typed in CTRL_X_CMDLINE + +#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] + +/// Message for CTRL-X mode, index is ctrl_x_mode. +static char *ctrl_x_msgs[] = +{ + N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. + N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), + NULL, // CTRL_X_SCROLL: depends on state + N_(" Whole line completion (^L^N^P)"), + N_(" File name completion (^F^N^P)"), + N_(" Tag completion (^]^N^P)"), + N_(" Path pattern completion (^N^P)"), + N_(" Definition completion (^D^N^P)"), + NULL, // CTRL_X_FINISHED + N_(" Dictionary completion (^K^N^P)"), + N_(" Thesaurus completion (^T^N^P)"), + N_(" Command-line completion (^V^N^P)"), + N_(" User defined completion (^U^N^P)"), + N_(" Omni completion (^O^N^P)"), + N_(" Spelling suggestion (s^N^P)"), + N_(" Keyword Local completion (^N^P)"), + NULL, // CTRL_X_EVAL doesn't use msg. + N_(" Command-line completion (^V^N^P)"), +}; + +static char *ctrl_x_mode_names[] = { + "keyword", + "ctrl_x", + "scroll", + "whole_line", + "files", + "tags", + "path_patterns", + "path_defines", + "unknown", // CTRL_X_FINISHED + "dictionary", + "thesaurus", + "cmdline", + "function", + "omni", + "spell", + NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" + "eval", + "cmdline", +}; + +// Array indexes used for cp_text[]. +#define CPT_ABBR 0 ///< "abbr" +#define CPT_MENU 1 ///< "menu" +#define CPT_KIND 2 ///< "kind" +#define CPT_INFO 3 ///< "info" +#define CPT_COUNT 4 ///< Number of entries + +/// Structure used to store one match for insert completion. +typedef struct compl_S compl_T; +struct compl_S { + compl_T *cp_next; + compl_T *cp_prev; + char_u *cp_str; ///< matched text + char_u *(cp_text[CPT_COUNT]); ///< text for the menu + typval_T cp_user_data; + char_u *cp_fname; ///< file containing the match, allocated when + ///< cp_flags has CP_FREE_FNAME + int cp_flags; ///< CP_ values + int cp_number; ///< sequence number +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "insexpand.c.generated.h" +#endif + +/// values for cp_flags +typedef enum { + CP_ORIGINAL_TEXT = 1, ///< the original text when the expansion begun + CP_FREE_FNAME = 2, ///< cp_fname is allocated + CP_CONT_S_IPOS = 4, ///< use CONT_S_IPOS for compl_cont_status + CP_EQUAL = 8, ///< ins_compl_equal() always returns true + CP_ICASE = 16, ///< ins_compl_equal ignores case + CP_FAST = 32, ///< use fast_breakcheck instead of os_breakcheck +} cp_flags_T; + +static char e_hitend[] = N_("Hit end of paragraph"); +static char e_compldel[] = N_("E840: Completion function deleted text"); + +// All the current matches are stored in a list. +// "compl_first_match" points to the start of the list. +// "compl_curr_match" points to the currently selected entry. +// "compl_shown_match" is different from compl_curr_match during +// ins_compl_get_exp(). + +static compl_T *compl_first_match = NULL; +static compl_T *compl_curr_match = NULL; +static compl_T *compl_shown_match = NULL; +static compl_T *compl_old_match = NULL; + +/// After using a cursor key <Enter> selects a match in the popup menu, +/// otherwise it inserts a line break. +static bool compl_enter_selects = false; + +/// When "compl_leader" is not NULL only matches that start with this string +/// are used. +static char_u *compl_leader = NULL; + +static bool compl_get_longest = false; ///< put longest common string in compl_leader + +static bool compl_no_insert = false; ///< false: select & insert + ///< true: noinsert +static bool compl_no_select = false; ///< false: select & insert + ///< true: noselect + +/// Selected one of the matches. When false the match was edited or using the +/// longest common string. +static bool compl_used_match; + +/// didn't finish finding completions. +static bool compl_was_interrupted = false; + +// Set when character typed while looking for matches and it means we should +// stop looking for matches. +static bool compl_interrupted = false; + +static bool compl_restarting = false; ///< don't insert match + +///< When the first completion is done "compl_started" is set. When it's +///< false the word to be completed must be located. +static bool compl_started = false; + +///< Which Ctrl-X mode are we in? +static int ctrl_x_mode = CTRL_X_NORMAL; + +static int compl_matches = 0; +static char *compl_pattern = NULL; +static Direction compl_direction = FORWARD; +static Direction compl_shows_dir = FORWARD; +static int compl_pending = 0; ///< > 1 for postponed CTRL-N +static pos_T compl_startpos; +static colnr_T compl_col = 0; ///< column where the text starts + ///< that is being completed +static char_u *compl_orig_text = NULL; ///< text as it was before + ///< completion started +static int compl_cont_mode = 0; +static expand_T compl_xp; + +static bool compl_opt_refresh_always = false; + +static size_t spell_bad_len = 0; // length of located bad word + +static int pum_selected_item = -1; + +/// CTRL-X pressed in Insert mode. +void ins_ctrl_x(void) +{ + if (!ctrl_x_mode_cmdline()) { + // if the next ^X<> won't ADD nothing, then reset compl_cont_status + if (compl_cont_status & CONT_N_ADDS) { + compl_cont_status |= CONT_INTRPT; + } else { + compl_cont_status = 0; + } + // We're not sure which CTRL-X mode it will be yet + ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; + edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); + edit_submode_pre = NULL; + showmode(); + } else { + // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X + // CTRL-V look like CTRL-N + ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; + } + + may_trigger_modechanged(); +} + +// Functions to check the current CTRL-X mode. + +bool ctrl_x_mode_none(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == 0; +} + +bool ctrl_x_mode_normal(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_NORMAL; +} + +bool ctrl_x_mode_scroll(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_SCROLL; +} + +bool ctrl_x_mode_whole_line(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_WHOLE_LINE; +} + +bool ctrl_x_mode_files(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_FILES; +} + +bool ctrl_x_mode_tags(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_TAGS; +} + +bool ctrl_x_mode_path_patterns(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_PATH_PATTERNS; +} + +bool ctrl_x_mode_path_defines(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_PATH_DEFINES; +} + +bool ctrl_x_mode_dictionary(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_DICTIONARY; +} + +bool ctrl_x_mode_thesaurus(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_THESAURUS; +} + +bool ctrl_x_mode_cmdline(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X; +} + +bool ctrl_x_mode_function(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_FUNCTION; +} + +bool ctrl_x_mode_omni(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_OMNI; +} + +bool ctrl_x_mode_spell(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_SPELL; +} + +static bool ctrl_x_mode_eval(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_EVAL; +} + +bool ctrl_x_mode_line_or_eval(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; +} + +/// Whether other than default completion has been selected. +bool ctrl_x_mode_not_default(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode != CTRL_X_NORMAL; +} + +/// Whether CTRL-X was typed without a following character, +/// not including when in CTRL-X CTRL-V mode. +bool ctrl_x_mode_not_defined_yet(void) + FUNC_ATTR_PURE +{ + return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; +} + +/// Check that the "dict" or "tsr" option can be used. +/// +/// @param dict_opt check "dict" when true, "tsr" when false. +bool check_compl_option(bool dict_opt) +{ + if (dict_opt + ? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell) + : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL + && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL)) { + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + msg_attr((dict_opt + ? _("'dictionary' option is empty") + : _("'thesaurus' option is empty")), HL_ATTR(HLF_E)); + if (emsg_silent == 0) { + vim_beep(BO_COMPL); + setcursor(); + ui_flush(); + os_delay(2004L, false); + } + return false; + } + return true; +} + +/// 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 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; + } + + switch (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: + case CTRL_X_CMDLINE_CTRL_X: + return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E + || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB + || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P + || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V + || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O + || c == Ctrl_S || c == Ctrl_K || c == 's' + || c == Ctrl_Z; + case CTRL_X_SCROLL: + return c == Ctrl_Y || c == Ctrl_E; + case CTRL_X_WHOLE_LINE: + return c == Ctrl_L || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_FILES: + return c == Ctrl_F || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_DICTIONARY: + return c == Ctrl_K || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_THESAURUS: + return c == Ctrl_T || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_TAGS: + return c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_PATH_PATTERNS: + return c == Ctrl_P || c == Ctrl_N; + case CTRL_X_PATH_DEFINES: + return c == Ctrl_D || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_CMDLINE: + return c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N + || c == Ctrl_X; + case CTRL_X_FUNCTION: + return c == Ctrl_U || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_OMNI: + return c == Ctrl_O || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_SPELL: + return c == Ctrl_S || c == Ctrl_P || c == Ctrl_N; + case CTRL_X_EVAL: + return (c == Ctrl_P || c == Ctrl_N); + } + internal_error("vim_is_ctrl_x_key()"); + return false; +} + +/// 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 +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. + 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 + return vim_isfilec(c) && !vim_ispathsep(c); + + case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: + case CTRL_X_OMNI: + // 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. + return vim_isprintc(c); + } + return vim_iswordc(c); +} + +/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the +/// case of the originally typed text is used, and the case of the completed +/// text is inferred, ie this tries to work out what case you probably wanted +/// the rest of the word to be in -- webb +/// +/// @param[in] cont_s_ipos next ^X<> will set initial_pos +int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, Direction dir, + bool cont_s_ipos) + FUNC_ATTR_NONNULL_ARG(1) +{ + char_u *str = str_arg; + int i, c; + int actual_len; // Take multi-byte characters + int actual_compl_length; // into account. + int min_len; + bool has_lower = false; + bool was_letter = false; + int flags = 0; + + if (p_ic && curbuf->b_p_inf && len > 0) { + // Infer case of completed part. + + // Find actual length of completion. + { + const char_u *p = str; + actual_len = 0; + while (*p != NUL) { + MB_PTR_ADV(p); + actual_len++; + } + } + + // Find actual length of original text. + { + const char_u *p = compl_orig_text; + actual_compl_length = 0; + while (*p != NUL) { + MB_PTR_ADV(p); + actual_compl_length++; + } + } + + // "actual_len" may be smaller than "actual_compl_length" when using + // thesaurus, only use the minimum when comparing. + min_len = actual_len < actual_compl_length + ? actual_len : actual_compl_length; + + // Allocate wide character array for the completion and fill it. + int *const wca = xmalloc((size_t)actual_len * sizeof(*wca)); + { + const char_u *p = str; + for (i = 0; i < actual_len; i++) { + wca[i] = mb_ptr2char_adv(&p); + } + } + + // Rule 1: Were any chars converted to lower? + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + c = mb_ptr2char_adv(&p); + if (mb_islower(c)) { + has_lower = true; + if (mb_isupper(wca[i])) { + // Rule 1 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = mb_tolower(wca[i]); + } + break; + } + } + } + } + + // Rule 2: No lower case, 2nd consecutive letter converted to + // upper case. + if (!has_lower) { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + c = mb_ptr2char_adv(&p); + if (was_letter && mb_isupper(c) && mb_islower(wca[i])) { + // Rule 2 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = mb_toupper(wca[i]); + } + break; + } + was_letter = mb_islower(c) || mb_isupper(c); + } + } + + // Copy the original case of the part we typed. + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + c = mb_ptr2char_adv(&p); + if (mb_islower(c)) { + wca[i] = mb_tolower(wca[i]); + } else if (mb_isupper(c)) { + wca[i] = mb_toupper(wca[i]); + } + } + } + + // Generate encoding specific output from wide character array. + // Multi-byte characters can occupy up to five bytes more than + // ASCII characters, and we also need one byte for NUL, so stay + // six bytes away from the edge of IObuff. + { + char_u *p = IObuff; + i = 0; + while (i < actual_len && (p - IObuff + 6) < IOSIZE) { + p += utf_char2bytes(wca[i++], (char *)p); + } + *p = NUL; + } + + xfree(wca); + + str = IObuff; + } + if (cont_s_ipos) { + flags |= CP_CONT_S_IPOS; + } + if (icase) { + flags |= CP_ICASE; + } + + return ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false); +} + +/// Add a match to the list of matches +/// +/// @param[in] str Match to add. +/// @param[in] len Match length, -1 to use #STRLEN. +/// @param[in] fname File name match comes from. May be NULL. +/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, +/// must have exactly #CPT_COUNT items. +/// @param[in] cptext_allocated If true, will not copy cptext strings. +/// +/// @note Will free strings in case of error. +/// cptext itself will not be freed. +/// @param[in] cdir Completion direction. +/// @param[in] adup True if duplicate matches are to be accepted. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add(char_u *const str, int len, char_u *const fname, + char_u *const *const cptext, const bool cptext_allocated, + typval_T *user_data, const Direction cdir, int flags_arg, const bool adup) + FUNC_ATTR_NONNULL_ARG(1) +{ + compl_T *match; + const Direction dir = (cdir == kDirectionNotSet ? compl_direction : cdir); + int flags = flags_arg; + + if (flags & CP_FAST) { + fast_breakcheck(); + } else { + os_breakcheck(); + } +#define FREE_CPTEXT(cptext, cptext_allocated) \ + do { \ + if ((cptext) != NULL && (cptext_allocated)) { \ + for (size_t i = 0; i < CPT_COUNT; i++) { \ + xfree((cptext)[i]); \ + } \ + } \ + } while (0) + if (got_int) { + FREE_CPTEXT(cptext, cptext_allocated); + return FAIL; + } + if (len < 0) { + len = (int)STRLEN(str); + } + + // If the same match is already present, don't add it. + if (compl_first_match != NULL && !adup) { + match = compl_first_match; + do { + if (!(match->cp_flags & CP_ORIGINAL_TEXT) + && STRNCMP(match->cp_str, str, len) == 0 + && match->cp_str[len] == NUL) { + FREE_CPTEXT(cptext, cptext_allocated); + return NOTDONE; + } + match = match->cp_next; + } while (match != NULL && match != compl_first_match); + } + + // Remove any popup menu before changing the list of matches. + ins_compl_del_pum(); + + // Allocate a new match structure. + // Copy the values to the new match structure. + match = xcalloc(1, sizeof(compl_T)); + match->cp_number = -1; + if (flags & CP_ORIGINAL_TEXT) { + match->cp_number = 0; + } + match->cp_str = vim_strnsave(str, (size_t)len); + + // match-fname is: + // - compl_curr_match->cp_fname if it is a string equal to fname. + // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem. + // - NULL otherwise. --Acevedo + if (fname != NULL + && compl_curr_match != NULL + && compl_curr_match->cp_fname != NULL + && STRCMP(fname, compl_curr_match->cp_fname) == 0) { + match->cp_fname = compl_curr_match->cp_fname; + } else if (fname != NULL) { + match->cp_fname = vim_strsave(fname); + flags |= CP_FREE_FNAME; + } else { + match->cp_fname = NULL; + } + match->cp_flags = flags; + + if (cptext != NULL) { + int i; + + for (i = 0; i < CPT_COUNT; i++) { + if (cptext[i] == NULL) { + continue; + } + if (*cptext[i] != NUL) { + match->cp_text[i] = (cptext_allocated + ? cptext[i] + : (char_u *)xstrdup((char *)cptext[i])); + } else if (cptext_allocated) { + xfree(cptext[i]); + } + } + } + + if (user_data != NULL) { + match->cp_user_data = *user_data; + } + + // Link the new match structure in the list of matches. + if (compl_first_match == NULL) { + match->cp_next = match->cp_prev = NULL; + } else if (dir == FORWARD) { + match->cp_next = compl_curr_match->cp_next; + match->cp_prev = compl_curr_match; + } else { // BACKWARD + match->cp_next = compl_curr_match; + match->cp_prev = compl_curr_match->cp_prev; + } + if (match->cp_next) { + match->cp_next->cp_prev = match; + } + if (match->cp_prev) { + match->cp_prev->cp_next = match; + } else { // if there's nothing before, it is the first match + compl_first_match = match; + } + compl_curr_match = match; + + // Find the longest common string if still doing that. + if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0) { + ins_compl_longest_match(match); + } + + return OK; +} + +/// Check that "str[len]" matches with "match->cp_str", considering +/// "match->cp_flags". +/// +/// @param match completion match +/// @param str character string to check +/// @param len length 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_flags & CP_EQUAL) { + return true; + } + if (match->cp_flags & CP_ICASE) { + return STRNICMP(match->cp_str, str, len) == 0; + } + return STRNCMP(match->cp_str, str, len) == 0; +} + +/// Reduce the longest common string for match "match". +static void ins_compl_longest_match(compl_T *match) +{ + char_u *p, *s; + int c1, c2; + int had_match; + + if (compl_leader == NULL) { + // First match, use it as a whole. + compl_leader = vim_strsave(match->cp_str); + had_match = (curwin->w_cursor.col > compl_col); + ins_compl_delete(); + ins_bytes(compl_leader + get_compl_len()); + ins_redraw(false); + + // When the match isn't there (to avoid matching itself) remove it + // again after redrawing. + if (!had_match) { + ins_compl_delete(); + } + compl_used_match = false; + } else { + // Reduce the text if this match differs from compl_leader. + p = compl_leader; + s = match->cp_str; + while (*p != NUL) { + c1 = utf_ptr2char((char *)p); + c2 = utf_ptr2char((char *)s); + + if ((match->cp_flags & CP_ICASE) + ? (mb_tolower(c1) != mb_tolower(c2)) + : (c1 != c2)) { + break; + } + MB_PTR_ADV(p); + MB_PTR_ADV(s); + } + + if (*p != NUL) { + // Leader was shortened, need to change the inserted text. + *p = NUL; + had_match = (curwin->w_cursor.col > compl_col); + ins_compl_delete(); + ins_bytes(compl_leader + get_compl_len()); + ins_redraw(false); + + // When the match isn't there (to avoid matching itself) remove it + // again after redrawing. + if (!had_match) { + ins_compl_delete(); + } + } + + compl_used_match = false; + } +} + +/// Add an array of matches to the list of matches. +/// Frees matches[]. +static void ins_compl_add_matches(int num_matches, char **matches, int icase) + FUNC_ATTR_NONNULL_ALL +{ + int add_r = OK; + Direction dir = compl_direction; + + for (int i = 0; i < num_matches && add_r != FAIL; i++) { + if ((add_r = ins_compl_add((char_u *)matches[i], -1, NULL, NULL, false, NULL, dir, + CP_FAST | (icase ? CP_ICASE : 0), + false)) == OK) { + // If dir was BACKWARD then honor it just once. + dir = FORWARD; + } + } + FreeWild(num_matches, matches); +} + +/// Make the completion list cyclic. +/// Return the number of matches (excluding the original). +static int ins_compl_make_cyclic(void) +{ + compl_T *match; + int count = 0; + + if (compl_first_match != NULL) { + // Find the end of the list. + match = compl_first_match; + // there's always an entry for the compl_orig_text, it doesn't count. + while (match->cp_next != NULL && match->cp_next != compl_first_match) { + match = match->cp_next; + count++; + } + match->cp_next = compl_first_match; + compl_first_match->cp_prev = match; + } + return count; +} + +/// Return whether there currently is a shown match. +bool ins_compl_has_shown_match(void) +{ + return compl_shown_match == NULL || compl_shown_match != compl_shown_match->cp_next; +} + +/// Return whether the shown match is long enough. +bool ins_compl_long_shown_match(void) +{ + return compl_shown_match != NULL && compl_shown_match->cp_str != NULL + && (colnr_T)STRLEN(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col; +} + +/// 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; + } +} + +/// "compl_match_array" points the currently displayed list of entries in the +/// popup menu. It is NULL when there is no popup menu. +static pumitem_T *compl_match_array = NULL; +static int compl_match_arraysize; + +/// Remove any popup menu. +static void ins_compl_del_pum(void) +{ + if (compl_match_array != NULL) { + pum_undisplay(false); + XFREE_CLEAR(compl_match_array); + } +} + +/// Check if the popup menu should be displayed. +bool pum_wanted(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + // "completeopt" must contain "menu" or "menuone" + return vim_strchr((char *)p_cot, 'm') != NULL; +} + +/// 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 +{ + // 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 (comp == NULL + || ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) { + break; + } + comp = comp->cp_next; + } while (comp != compl_first_match); + + if (strstr((char *)p_cot, "menuone") != NULL) { + return i >= 1; + } + return i >= 2; +} + +/// Convert to complete item dict +static dict_T *ins_compl_dict_alloc(compl_T *match) +{ + // { word, abbr, menu, kind, info } + dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); + tv_dict_add_str(dict, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(dict, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(dict, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(dict, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(dict, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + if (match->cp_user_data.v_type == VAR_UNKNOWN) { + tv_dict_add_str(dict, S_LEN("user_data"), ""); + } else { + tv_dict_add_tv(dict, S_LEN("user_data"), &match->cp_user_data); + } + return dict; +} + +static void trigger_complete_changed_event(int cur) +{ + static bool recursive = false; + save_v_event_T save_v_event; + + if (recursive) { + return; + } + + dict_T *v_event = get_v_event(&save_v_event); + if (cur < 0) { + tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc()); + } else { + dict_T *item = ins_compl_dict_alloc(compl_curr_match); + tv_dict_add_dict(v_event, S_LEN("completed_item"), item); + } + pum_set_event_info(v_event); + tv_dict_set_keys_readonly(v_event); + + recursive = true; + textlock++; + apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, false, curbuf); + textlock--; + recursive = false; + + restore_v_event(v_event, &save_v_event); +} + +/// Show the popup menu for the list of matches. +/// Also adjusts "compl_shown_match" to an entry that is actually displayed. +void ins_compl_show_pum(void) +{ + compl_T *compl; + compl_T *shown_compl = NULL; + bool did_find_shown_match = false; + bool shown_match_ok = false; + int i; + int cur = -1; + colnr_T col; + int lead_len = 0; + bool array_changed = false; + + if (!pum_wanted() || !pum_enough_matches()) { + return; + } + + // Dirty hard-coded hack: remove any matchparen highlighting. + do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif"); + + // Update the screen before drawing the popup menu over it. + update_screen(0); + + if (compl_match_array == NULL) { + array_changed = true; + // Need to build the popup menu list. + compl_match_arraysize = 0; + compl = compl_first_match; + // If it's user complete function and refresh_always, + // do not use "compl_leader" as prefix filter. + if (ins_compl_need_restart()) { + XFREE_CLEAR(compl_leader); + } + if (compl_leader != NULL) { + lead_len = (int)STRLEN(compl_leader); + } + do { + if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 + && (compl_leader == NULL + || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { + compl_match_arraysize++; + } + compl = compl->cp_next; + } while (compl != NULL && compl != compl_first_match); + if (compl_match_arraysize == 0) { + return; + } + + assert(compl_match_arraysize >= 0); + compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); + // If the current match is the original text don't find the first + // match after it, don't highlight anything. + if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { + shown_match_ok = true; + } + + i = 0; + compl = compl_first_match; + do { + if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 + && (compl_leader == NULL + || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { + if (!shown_match_ok) { + if (compl == compl_shown_match || did_find_shown_match) { + // This item is the shown match or this is the + // first displayed item after the shown match. + compl_shown_match = compl; + did_find_shown_match = true; + shown_match_ok = true; + } else { + // Remember this displayed match for when the + // shown match is just below it. + shown_compl = compl; + } + cur = i; + } + + if (compl->cp_text[CPT_ABBR] != NULL) { + compl_match_array[i].pum_text = + compl->cp_text[CPT_ABBR]; + } else { + compl_match_array[i].pum_text = compl->cp_str; + } + compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; + compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; + if (compl->cp_text[CPT_MENU] != NULL) { + compl_match_array[i++].pum_extra = + compl->cp_text[CPT_MENU]; + } else { + compl_match_array[i++].pum_extra = compl->cp_fname; + } + } + + if (compl == compl_shown_match) { + did_find_shown_match = true; + + // When the original text is the shown match don't set + // compl_shown_match. + if (compl->cp_flags & CP_ORIGINAL_TEXT) { + shown_match_ok = true; + } + + if (!shown_match_ok && shown_compl != NULL) { + // The shown match isn't displayed, set it to the + // previously displayed match. + compl_shown_match = shown_compl; + shown_match_ok = true; + } + } + compl = compl->cp_next; + } while (compl != NULL && compl != compl_first_match); + + if (!shown_match_ok) { // no displayed match at all + cur = -1; + } + } else { + // popup menu already exists, only need to find the current item. + for (i = 0; i < compl_match_arraysize; i++) { + if (compl_match_array[i].pum_text == compl_shown_match->cp_str + || compl_match_array[i].pum_text + == compl_shown_match->cp_text[CPT_ABBR]) { + cur = i; + break; + } + } + } + + // 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_selected_item = cur; + pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); + curwin->w_cursor.col = col; + + if (has_event(EVENT_COMPLETECHANGED)) { + trigger_complete_changed_event(cur); + } +} + +#define DICT_FIRST (1) ///< use just first element in "dict" +#define DICT_EXACT (2) ///< "dict" is the exact name of a file + +/// Add any identifiers that match the given pattern in the list of dictionary +/// files "dict_start" to the list of completions. +/// +/// @param flags DICT_FIRST and/or DICT_EXACT +/// @param thesaurus Thesaurus completion +static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, int thesaurus) +{ + char *dict = (char *)dict_start; + char_u *ptr; + char *buf; + regmatch_T regmatch; + char **files; + int count; + int save_p_scs; + Direction dir = compl_direction; + + if (*dict == NUL) { + // When 'dictionary' is empty and spell checking is enabled use + // "spell". + if (!thesaurus && curwin->w_p_spell) { + dict = "spell"; + } else { + return; + } + } + + buf = xmalloc(LSIZE); + regmatch.regprog = NULL; // so that we can goto theend + + // If 'infercase' is set, don't use 'smartcase' here + save_p_scs = p_scs; + if (curbuf->b_p_inf) { + p_scs = false; + } + + // When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern + // to only match at the start of a line. Otherwise just match the + // pattern. Also need to double backslashes. + if (ctrl_x_mode_line_or_eval()) { + char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); + + size_t len = STRLEN(pat_esc) + 10; + ptr = xmalloc(len); + vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); + regmatch.regprog = vim_regcomp((char *)ptr, RE_MAGIC); + xfree(pat_esc); + xfree(ptr); + } else { + regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); + if (regmatch.regprog == NULL) { + goto theend; + } + } + + // ignore case depends on 'ignorecase', 'smartcase' and "pat" + regmatch.rm_ic = ignorecase(pat); + while (*dict != NUL && !got_int && !compl_interrupted) { + // copy one dictionary file name into buf + if (flags == DICT_EXACT) { + count = 1; + files = &dict; + } else { + // Expand wildcards in the dictionary name, but do not allow + // backticks (for security, the 'dict' option may have been set in + // a modeline). + copy_option_part(&dict, buf, LSIZE, ","); + if (!thesaurus && STRCMP(buf, "spell") == 0) { + count = -1; + } else if (vim_strchr(buf, '`') != NULL + || expand_wildcards(1, &buf, &count, &files, + EW_FILE|EW_SILENT) != OK) { + count = 0; + } + } + + if (count == -1) { + // Complete from active spelling. Skip "\<" in the pattern, we + // don't use it as a RE. + if (pat[0] == '\\' && pat[1] == '<') { + ptr = pat + 2; + } else { + ptr = pat; + } + spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); + } else if (count > 0) { // avoid warning for using "files" uninit + ins_compl_files(count, files, thesaurus, flags, + ®match, (char_u *)buf, &dir); + if (flags != DICT_EXACT) { + FreeWild(count, files); + } + } + if (flags != 0) { + break; + } + } + +theend: + p_scs = save_p_scs; + vim_regfree(regmatch.regprog); + xfree(buf); +} + +static void ins_compl_files(int count, char **files, int thesaurus, int flags, regmatch_T *regmatch, + char_u *buf, Direction *dir) + FUNC_ATTR_NONNULL_ARG(2, 7) +{ + char_u *ptr; + int i; + FILE *fp; + int add_r; + + for (i = 0; i < count && !got_int && !compl_interrupted; i++) { + fp = os_fopen(files[i], "r"); // open dictionary file + if (flags != DICT_EXACT) { + msg_hist_off = true; // reset in msg_trunc_attr() + vim_snprintf((char *)IObuff, IOSIZE, + _("Scanning dictionary: %s"), files[i]); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } + + if (fp == NULL) { + continue; + } + + // Read dictionary file line by line. + // Check each line for a match. + while (!got_int && !compl_interrupted + && !vim_fgets(buf, LSIZE, fp)) { + ptr = buf; + while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) { + ptr = regmatch->startp[0]; + if (ctrl_x_mode_line_or_eval()) { + ptr = find_line_end(ptr); + } else { + ptr = find_word_end(ptr); + } + add_r = ins_compl_add_infercase(regmatch->startp[0], + (int)(ptr - regmatch->startp[0]), + p_ic, (char_u *)files[i], *dir, false); + if (thesaurus) { + char_u *wstart; + + // Add the other matches on the line + ptr = buf; + while (!got_int) { + // Find start of the next word. Skip white + // space and punctuation. + ptr = find_word_start(ptr); + if (*ptr == NUL || *ptr == NL) { + break; + } + wstart = ptr; + + // Find end of the word. + // Japanese words may have characters in + // different classes, only separate words + // with single-byte non-word characters. + while (*ptr != NUL) { + const int l = utfc_ptr2len((char *)ptr); + + if (l < 2 && !vim_iswordc(*ptr)) { + break; + } + ptr += l; + } + + // Add the word. Skip the regexp match. + if (wstart != regmatch->startp[0]) { + add_r = ins_compl_add_infercase(wstart, (int)(ptr - wstart), + p_ic, (char_u *)files[i], *dir, false); + } + } + } + if (add_r == OK) { + // if dir was BACKWARD then honor it just once + *dir = FORWARD; + } else if (add_r == FAIL) { + break; + } + // avoid expensive call to vim_regexec() when at end + // of line + if (*ptr == '\n' || got_int) { + break; + } + } + line_breakcheck(); + ins_compl_check_keys(50, false); + } + fclose(fp); + } +} + +/// Find the start of the next word. +/// Returns a pointer to the first char of the word. Also stops at a NUL. +char_u *find_word_start(char_u *ptr) + FUNC_ATTR_PURE +{ + while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) { + ptr += utfc_ptr2len((char *)ptr); + } + return ptr; +} + +/// Find the end of the word. Assumes it starts inside a word. +/// Returns a pointer to just after the word. +char_u *find_word_end(char_u *ptr) + FUNC_ATTR_PURE +{ + const int start_class = mb_get_class(ptr); + if (start_class > 1) { + while (*ptr != NUL) { + ptr += utfc_ptr2len((char *)ptr); + if (mb_get_class(ptr) != start_class) { + break; + } + } + } + return ptr; +} + +/// Find the end of the line, omitting CR and NL at the end. +/// Returns a pointer to just after the line. +static char_u *find_line_end(char_u *ptr) +{ + char_u *s; + + s = ptr + STRLEN(ptr); + while (s > ptr && (s[-1] == CAR || s[-1] == NL)) { + s--; + } + return s; +} + +/// Free the list of completions +static void ins_compl_free(void) +{ + compl_T *match; + + XFREE_CLEAR(compl_pattern); + XFREE_CLEAR(compl_leader); + + if (compl_first_match == NULL) { + return; + } + + ins_compl_del_pum(); + pum_clear(); + + compl_curr_match = compl_first_match; + do { + match = compl_curr_match; + compl_curr_match = compl_curr_match->cp_next; + xfree(match->cp_str); + // several entries may use the same fname, free it just once. + if (match->cp_flags & CP_FREE_FNAME) { + xfree(match->cp_fname); + } + for (int i = 0; i < CPT_COUNT; i++) { + xfree(match->cp_text[i]); + } + tv_clear(&match->cp_user_data); + xfree(match); + } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); + compl_first_match = compl_curr_match = NULL; + compl_shown_match = NULL; + compl_old_match = NULL; +} + +void ins_compl_clear(void) +{ + compl_cont_status = 0; + compl_started = false; + compl_matches = 0; + XFREE_CLEAR(compl_pattern); + XFREE_CLEAR(compl_leader); + edit_submode_extra = NULL; + XFREE_CLEAR(compl_orig_text); + compl_enter_selects = false; + // clear v:completed_item + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); +} + +/// Check that Insert completion is active. +bool ins_compl_active(void) + FUNC_ATTR_PURE +{ + return compl_started; +} + +/// Selected one of the matches. When false the match was edited or using the +/// longest common string. +bool ins_compl_used_match(void) +{ + return compl_used_match; +} + +/// Initialize get longest common string. +void ins_compl_init_get_longest(void) +{ + compl_get_longest = false; +} + +/// Returns true when insert completion is interrupted. +bool ins_compl_interrupted(void) +{ + return compl_interrupted; +} + +/// Returns true if the <Enter> key selects a match in the completion popup +/// menu. +bool ins_compl_enter_selects(void) +{ + return compl_enter_selects; +} + +/// Return the column where the text starts that is being completed +colnr_T ins_compl_col(void) +{ + return compl_col; +} + +/// Delete one character before the cursor and show the subset of the matches +/// that match the word that is now before the cursor. +/// Returns the character to be used, NUL if the work is done and another char +/// to be got from the user. +int ins_compl_bs(void) +{ + char_u *line = get_cursor_line_ptr(); + char_u *p = line + curwin->w_cursor.col; + MB_PTR_BACK(line, p); + ptrdiff_t p_off = p - line; + + // Stop completion when the whole word was deleted. For Omni completion + // allow the word to be deleted, we won't match everything. + // Respect the 'backspace' option. + if ((int)(p - line) - (int)compl_col < 0 + || ((int)(p - line) - (int)compl_col == 0 && !ctrl_x_mode_omni()) + || ctrl_x_mode_eval() + || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col + - compl_length < 0)) { + return K_BS; + } + + // Deleted more than what was used to find matches or didn't finish + // finding all matches: need to look for matches all over again. + if (curwin->w_cursor.col <= compl_col + compl_length + || ins_compl_need_restart()) { + ins_compl_restart(); + } + + // ins_compl_restart() calls update_screen(0) which may invalidate the pointer + // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic + line = get_cursor_line_ptr(); + + xfree(compl_leader); + compl_leader = vim_strnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col)); + ins_compl_new_leader(); + if (compl_shown_match != NULL) { + // Make sure current match is not a hidden item. + compl_curr_match = compl_shown_match; + } + + return NUL; +} + +/// 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 compl_was_interrupted + || ((ctrl_x_mode_function() || ctrl_x_mode_omni()) + && compl_opt_refresh_always); +} + +/// Called after changing "compl_leader". +/// Show the popup menu with a different set of matches. +/// May also search for matches again if the previous search was interrupted. +static void ins_compl_new_leader(void) +{ + ins_compl_del_pum(); + ins_compl_delete(); + ins_bytes(compl_leader + get_compl_len()); + compl_used_match = false; + + 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. + // 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_enter_selects = !compl_used_match; + + // 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. + // 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; + } +} + +/// Return the length of the completion, from the completion start column to +/// the cursor column. Making sure it never goes below zero. +static int get_compl_len(void) +{ + int off = (int)curwin->w_cursor.col - (int)compl_col; + + if (off < 0) { + return 0; + } + return off; +} + +/// Append one character to the match leader. May reduce the number of +/// matches. +void ins_compl_addleader(int c) +{ + int cc; + + if (stop_arrow() == FAIL) { + return; + } + if ((cc = utf_char2len(c)) > 1) { + char buf[MB_MAXBYTES + 1]; + + utf_char2bytes(c, (char *)buf); + buf[cc] = NUL; + ins_char_bytes((char_u *)buf, (size_t)cc); + } else { + ins_char(c); + } + + // If we didn't complete finding matches we must search again. + if (ins_compl_need_restart()) { + ins_compl_restart(); + } + + xfree(compl_leader); + compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, + (size_t)(curwin->w_cursor.col - compl_col)); + ins_compl_new_leader(); +} + +/// Setup for finding completions again without leaving CTRL-X mode. Used when +/// BS or a key was typed while still searching for matches. +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; + compl_cont_status = 0; + compl_cont_mode = 0; +} + +/// Set the first match, the original text. +static void ins_compl_set_original_text(char_u *str) + FUNC_ATTR_NONNULL_ALL +{ + // Replace the original text entry. + // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be + // at the last item for backward completion + if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check + xfree(compl_first_match->cp_str); + compl_first_match->cp_str = vim_strsave(str); + } else if (compl_first_match->cp_prev != NULL + && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) { + xfree(compl_first_match->cp_prev->cp_str); + compl_first_match->cp_prev->cp_str = vim_strsave(str); + } +} + +/// Append one character to the match leader. May reduce the number of +/// matches. +void ins_compl_addfrommatch(void) +{ + char_u *p; + int len = (int)curwin->w_cursor.col - (int)compl_col; + int c; + compl_T *cp; + assert(compl_shown_match != NULL); + p = compl_shown_match->cp_str; + if ((int)STRLEN(p) <= len) { // the match is too short + // When still at the original match use the first entry that matches + // the leader. + if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { + p = NULL; + for (cp = compl_shown_match->cp_next; cp != NULL + && cp != compl_first_match; cp = cp->cp_next) { + if (compl_leader == NULL + || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) { + p = cp->cp_str; + break; + } + } + if (p == NULL || (int)STRLEN(p) <= len) { + return; + } + } else { + return; + } + } + p += len; + c = utf_ptr2char((char *)p); + ins_compl_addleader(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; +bool ins_compl_prep(int c) +{ + char_u *ptr; + bool retval = false; + const int prev_mode = ctrl_x_mode; + + // 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. + if (c != Ctrl_R && vim_is_ctrl_x_key(c)) { + edit_submode_extra = NULL; + } + + // 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_EVENT + || c == K_COMMAND || c == K_LUA) { + return retval; + } + + if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X) { + if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c) + || !vim_is_ctrl_x_key(c)) { + // Not starting another completion mode. + ctrl_x_mode = CTRL_X_CMDLINE; + + // CTRL-X CTRL-Z should stop completion without inserting anything + if (c == Ctrl_Z) { + retval = true; + } + } else { + ctrl_x_mode = CTRL_X_CMDLINE; + + // Other CTRL-X keys first stop completion, then start another + // completion mode. + ins_compl_prep(' '); + ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; + } + } + + // Set "compl_get_longest" when finding the first matches. + if (ctrl_x_mode_not_defined_yet() + || (ctrl_x_mode_normal() && !compl_started)) { + compl_get_longest = (strstr((char *)p_cot, "longest") != NULL); + compl_used_match = true; + } + + if (ctrl_x_mode_not_defined_yet()) { + // We have just typed CTRL-X and aren't quite sure which CTRL-X mode + // it will be yet. Now we decide. + switch (c) { + case Ctrl_E: + case Ctrl_Y: + ctrl_x_mode = CTRL_X_SCROLL; + if (!(State & REPLACE_FLAG)) { + edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); + } else { + edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); + } + edit_submode_pre = NULL; + showmode(); + break; + case Ctrl_L: + ctrl_x_mode = CTRL_X_WHOLE_LINE; + break; + case Ctrl_F: + ctrl_x_mode = CTRL_X_FILES; + break; + case Ctrl_K: + ctrl_x_mode = CTRL_X_DICTIONARY; + break; + case Ctrl_R: + // Simply allow ^R to happen without affecting ^X mode + break; + case Ctrl_T: + ctrl_x_mode = CTRL_X_THESAURUS; + break; + case Ctrl_U: + ctrl_x_mode = CTRL_X_FUNCTION; + break; + case Ctrl_O: + ctrl_x_mode = CTRL_X_OMNI; + break; + case 's': + case Ctrl_S: + ctrl_x_mode = CTRL_X_SPELL; + emsg_off++; // Avoid getting the E756 error twice. + spell_back_to_badword(); + emsg_off--; + break; + case Ctrl_RSB: + ctrl_x_mode = CTRL_X_TAGS; + break; + case Ctrl_I: + case K_S_TAB: + ctrl_x_mode = CTRL_X_PATH_PATTERNS; + break; + case Ctrl_D: + ctrl_x_mode = CTRL_X_PATH_DEFINES; + break; + case Ctrl_V: + case Ctrl_Q: + ctrl_x_mode = CTRL_X_CMDLINE; + break; + case Ctrl_Z: + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + retval = true; + break; + case Ctrl_P: + case Ctrl_N: + // ^X^P means LOCAL expansion if nothing interrupted (eg we + // just started ^X mode, or there were enough ^X's to cancel + // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) + // do normal expansion when interrupting a different mode (say + // ^X^F^X^P or ^P^X^X^P, see below) + // nothing changes if interrupting mode 0, (eg, the flag + // doesn't change when going to ADDING mode -- Acevedo + if (!(compl_cont_status & CONT_INTRPT)) { + compl_cont_status |= CONT_LOCAL; + } else if (compl_cont_mode != 0) { + compl_cont_status &= ~CONT_LOCAL; + } + FALLTHROUGH; + default: + // If we have typed at least 2 ^X's... for modes != 0, we set + // compl_cont_status = 0 (eg, as if we had just started ^X + // mode). + // For mode 0, we set "compl_cont_mode" to an impossible + // value, in both cases ^X^X can be used to restart the same + // mode (avoiding ADDING mode). + // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start + // 'complete' and local ^P expansions respectively. + // In mode 0 an extra ^X is needed since ^X^P goes to ADDING + // mode -- Acevedo + if (c == Ctrl_X) { + if (compl_cont_mode != 0) { + compl_cont_status = 0; + } else { + compl_cont_mode = CTRL_X_NOT_DEFINED_YET; + } + } + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + break; + } + } else if (ctrl_x_mode_not_default()) { + // We're already in CTRL-X mode, do we stay in it? + if (!vim_is_ctrl_x_key(c)) { + if (ctrl_x_mode_scroll()) { + ctrl_x_mode = CTRL_X_NORMAL; + } else { + ctrl_x_mode = CTRL_X_FINISHED; + } + edit_submode = NULL; + } + showmode(); + } + + if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) { + // Show error message from attempted keyword completion (probably + // 'Pattern not found') until another key is hit, then go back to + // showing what mode we are in. + showmode(); + if ((ctrl_x_mode_normal() + && c != Ctrl_N + && c != Ctrl_P + && c != Ctrl_R + && !ins_compl_pum_key(c)) + || ctrl_x_mode == CTRL_X_FINISHED) { + // Get here when we have finished typing a sequence of ^N and + // ^P or other completion characters in CTRL-X mode. Free up + // memory that was used, and make sure we can redo the insert. + if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) { + // If any of the original typed text has been changed, eg when + // ignorecase is set, we must add back-spaces to the redo + // buffer. We add as few as necessary to delete just the part + // of the original text that has changed. + // When using the longest match, edited the match or used + // CTRL-E then don't use the current match. + if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { + ptr = compl_curr_match->cp_str; + } else { + ptr = NULL; + } + ins_compl_fixRedoBufForLeader(ptr); + } + + bool want_cindent = (can_cindent_get() && 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) { + do_c_expr_indent(); + want_cindent = false; // don't do it again + } + } else { + int prev_col = curwin->w_cursor.col; + + // put the cursor on the last char, for 'tw' formatting + if (prev_col > 0) { + dec_cursor(); + } + + if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) { + insertchar(NUL, 0, -1); + } + + if (prev_col > 0 + && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) { + 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 ((c == Ctrl_Y || (compl_enter_selects + && (c == CAR || c == K_KENTER || c == NL))) + && pum_visible()) { + retval = true; + } + + // CTRL-E means completion is Ended, go back to the typed text. + // but only do this, if the Popup is still visible + if (c == Ctrl_E) { + ins_compl_delete(); + if (compl_leader != NULL) { + ins_bytes(compl_leader + get_compl_len()); + } else if (compl_first_match != NULL) { + ins_bytes(compl_orig_text + get_compl_len()); + } + retval = true; + } + + auto_format(false, true); + + // Trigger the CompleteDonePre event to give scripts a chance to + // act upon the completion before clearing the info, and restore + // ctrl_x_mode, so that complete_info() can be used. + ctrl_x_mode = prev_mode; + ins_apply_autocmds(EVENT_COMPLETEDONEPRE); + + ins_compl_free(); + compl_started = false; + compl_matches = 0; + if (!shortmess(SHM_COMPLETIONMENU)) { + msg_clr_cmdline(); // necessary for "noshowmode" + } + ctrl_x_mode = CTRL_X_NORMAL; + compl_enter_selects = false; + if (edit_submode != NULL) { + edit_submode = NULL; + 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'. + if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) { + do_c_expr_indent(); + } + // Trigger the CompleteDone event to give scripts a chance to act + // upon the end of completion. + ins_apply_autocmds(EVENT_COMPLETEDONE); + } + } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { + // Trigger the CompleteDone event to give scripts a chance to act + // upon the (possibly failed) completion. + ins_apply_autocmds(EVENT_COMPLETEDONE); + } + + may_trigger_modechanged(); + + // reset continue_* if we left expansion-mode, if we stay they'll be + // (re)set properly in ins_complete() + if (!vim_is_ctrl_x_key(c)) { + compl_cont_status = 0; + compl_cont_mode = 0; + } + + return retval; +} + +/// Fix the redo buffer for the completion leader replacing some of the typed +/// text. This inserts backspaces and appends the changed text. +/// "ptr" is the known leader text or NUL. +static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) +{ + int len; + char_u *p; + char_u *ptr = ptr_arg; + + if (ptr == NULL) { + if (compl_leader != NULL) { + ptr = compl_leader; + } else { + return; // nothing to do + } + } + if (compl_orig_text != NULL) { + p = compl_orig_text; + for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {} + if (len > 0) { + len -= utf_head_off(p, p + len); + } + for (p += len; *p != NUL; MB_PTR_ADV(p)) { + AppendCharToRedobuff(K_BS); + } + } else { + len = 0; + } + AppendToRedobuffLit((char *)ptr + len, -1); +} + +/// Loops through the list of windows, loaded-buffers or non-loaded-buffers +/// (depending on flag) starting from buf and looking for a non-scanned +/// buffer (other than curbuf). curbuf is special, if it is called with +/// buf=curbuf then it has to be the first call for a given flag/expansion. +/// +/// Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo +static buf_T *ins_compl_next_buf(buf_T *buf, int flag) +{ + static win_T *wp = NULL; + + if (flag == 'w') { // just windows + if (buf == curbuf || wp == NULL) { // first call for this flag/expansion + wp = curwin; + } + assert(wp); + while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin + && wp->w_buffer->b_scanned) {} + buf = wp->w_buffer; + } else { + // 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' + // (unlisted buffers) + // When completing whole lines skip unloaded buffers. + while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf + && ((flag == 'U' + ? buf->b_p_bl + : (!buf->b_p_bl + || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) + || buf->b_scanned)) {} + } + return buf; +} + +/// Get the user-defined completion function name for completion 'type' +static char_u *get_complete_funcname(int type) +{ + switch (type) { + case CTRL_X_FUNCTION: + return curbuf->b_p_cfu; + case CTRL_X_OMNI: + return curbuf->b_p_ofu; + case CTRL_X_THESAURUS: + return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu; + default: + return (char_u *)""; + } +} + +/// Execute user defined complete function 'completefunc' or 'omnifunc', and +/// get matches in "matches". +/// +/// @param type CTRL_X_OMNI or CTRL_X_FUNCTION +static void expand_by_function(int type, char_u *base) +{ + list_T *matchlist = NULL; + dict_T *matchdict = NULL; + char_u *funcname; + pos_T pos; + typval_T rettv; + const int save_State = State; + + assert(curbuf != NULL); + funcname = get_complete_funcname(type); + if (*funcname == NUL) { + return; + } + + // Call 'completefunc' to obtain the list of matches. + typval_T args[3]; + args[0].v_type = VAR_NUMBER; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_UNKNOWN; + args[0].vval.v_number = 0; + args[1].vval.v_string = base != NULL ? (char *)base : ""; + + pos = curwin->w_cursor; + // Lock the text to avoid weird things from happening. Also disallow + // switching to another window, it should not be needed and may end up in + // Insert mode in another buffer. + textlock++; + + // Call a function, which returns a list or dict. + if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) { + switch (rettv.v_type) { + case VAR_LIST: + matchlist = rettv.vval.v_list; + break; + case VAR_DICT: + matchdict = rettv.vval.v_dict; + break; + case VAR_SPECIAL: + FALLTHROUGH; + default: + // TODO(brammool): Give error message? + tv_clear(&rettv); + break; + } + } + textlock--; + + curwin->w_cursor = pos; // restore the cursor position + validate_cursor(); + if (!equalpos(curwin->w_cursor, pos)) { + emsg(_(e_compldel)); + goto theend; + } + + if (matchlist != NULL) { + ins_compl_add_list(matchlist); + } else if (matchdict != NULL) { + ins_compl_add_dict(matchdict); + } + +theend: + // Restore State, it might have been changed. + State = save_State; + + if (matchdict != NULL) { + tv_dict_unref(matchdict); + } + if (matchlist != NULL) { + tv_list_unref(matchlist); + } +} + +/// Add a match to the list of matches from VimL object +/// +/// @param[in] tv Object to get matches from. +/// @param[in] dir Completion direction. +/// @param[in] fast use fast_breakcheck() instead of os_breakcheck(). +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) + FUNC_ATTR_NONNULL_ALL +{ + const char *word; + bool dup = false; + bool empty = false; + int flags = fast ? CP_FAST : 0; + char *(cptext[CPT_COUNT]); + typval_T user_data; + + user_data.v_type = VAR_UNKNOWN; + if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { + word = tv_dict_get_string(tv->vval.v_dict, "word", false); + cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); + cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); + cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); + cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); + tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data); + + if (tv_dict_get_number(tv->vval.v_dict, "icase")) { + flags |= CP_ICASE; + } + dup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); + empty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); + if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL + && tv_dict_get_number(tv->vval.v_dict, "equal")) { + flags |= CP_EQUAL; + } + } else { + word = tv_get_string_chk(tv); + memset(cptext, 0, sizeof(cptext)); + } + if (word == NULL || (!empty && *word == NUL)) { + for (size_t i = 0; i < CPT_COUNT; i++) { + xfree(cptext[i]); + } + return FAIL; + } + return ins_compl_add((char_u *)word, -1, NULL, + (char_u **)cptext, true, &user_data, dir, flags, dup); +} + +/// Add completions from a list. +static void ins_compl_add_list(list_T *const list) +{ + Direction dir = compl_direction; + + // Go through the List with matches and add each of them. + TV_LIST_ITER(list, li, { + if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir, true) == OK) { + // If dir was BACKWARD then honor it just once. + dir = FORWARD; + } else if (did_emsg) { + break; + } + }); +} + +/// Add completions from a dict. +static void ins_compl_add_dict(dict_T *dict) +{ + dictitem_T *di_refresh; + dictitem_T *di_words; + + // Check for optional "refresh" item. + compl_opt_refresh_always = false; + di_refresh = tv_dict_find(dict, S_LEN("refresh")); + if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { + const char *v = (const char *)di_refresh->di_tv.vval.v_string; + + if (v != NULL && strcmp(v, "always") == 0) { + compl_opt_refresh_always = true; + } + } + + // Add completions from a "words" list. + di_words = tv_dict_find(dict, S_LEN("words")); + if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { + ins_compl_add_list(di_words->di_tv.vval.v_list); + } +} + +/// Start completion for the complete() function. +/// +/// @param startcol where the matched text starts (1 is first column). +/// @param list the list of matches. +static void set_completion(colnr_T startcol, list_T *list) +{ + int flags = CP_ORIGINAL_TEXT; + + // If already doing completions stop it. + if (ctrl_x_mode_not_default()) { + ins_compl_prep(' '); + } + ins_compl_clear(); + ins_compl_free(); + + compl_direction = FORWARD; + if (startcol > curwin->w_cursor.col) { + startcol = curwin->w_cursor.col; + } + compl_col = startcol; + compl_length = (int)curwin->w_cursor.col - (int)startcol; + // compl_pattern doesn't need to be set + compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, + (size_t)compl_length); + if (p_ic) { + flags |= CP_ICASE; + } + if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, + flags | CP_FAST, false) != OK) { + return; + } + + ctrl_x_mode = CTRL_X_EVAL; + + ins_compl_add_list(list); + compl_matches = ins_compl_make_cyclic(); + compl_started = true; + compl_used_match = true; + compl_cont_status = 0; + int save_w_wrow = curwin->w_wrow; + int save_w_leftcol = curwin->w_leftcol; + + compl_curr_match = compl_first_match; + if (compl_no_insert || compl_no_select) { + ins_complete(K_DOWN, false); + if (compl_no_select) { + ins_complete(K_UP, false); + } + } else { + ins_complete(Ctrl_N, false); + } + compl_enter_selects = compl_no_insert; + + // Lazily show the popup menu, unless we got interrupted. + if (!compl_interrupted) { + show_pum(save_w_wrow, save_w_leftcol); + } + + may_trigger_modechanged(); + ui_flush(); +} + +/// "complete()" function +void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if ((State & MODE_INSERT) == 0) { + emsg(_("E785: complete() can only be used in Insert mode")); + return; + } + + // Check for undo allowed here, because if something was already inserted + // the line was already saved for undo and this check isn't done. + if (!undo_allowed(curbuf)) { + return; + } + + if (argvars[1].v_type != VAR_LIST) { + emsg(_(e_invarg)); + } else { + const colnr_T startcol = (colnr_T)tv_get_number_chk(&argvars[0], NULL); + if (startcol > 0) { + set_completion(startcol - 1, argvars[1].vval.v_list); + } + } +} + +/// "complete_add()" function +void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false); +} + +/// "complete_check()" function +void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + int saved = RedrawingDisabled; + + RedrawingDisabled = 0; + ins_compl_check_keys(0, true); + rettv->vval.v_number = ins_compl_interrupted(); + RedrawingDisabled = saved; +} + +/// Return Insert completion mode name string +static char_u *ins_compl_mode(void) +{ + if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started) { + return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; + } + return (char_u *)""; +} + +static void ins_compl_update_sequence_numbers(void) +{ + int number = 0; + compl_T *match; + + if (compl_direction == FORWARD) { + // search backwards for the first valid (!= -1) number. + // This should normally succeed already at the first loop + // cycle, so it's fast! + for (match = compl_curr_match->cp_prev; + match != NULL && match != compl_first_match; + match = match->cp_prev) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go up and assign all numbers which are not assigned yet + for (match = match->cp_next; + match != NULL && match->cp_number == -1; + match = match->cp_next) { + match->cp_number = ++number; + } + } + } else { // BACKWARD + assert(compl_direction == BACKWARD); + // search forwards (upwards) for the first valid (!= -1) + // number. This should normally succeed already at the + // first loop cycle, so it's fast! + for (match = compl_curr_match->cp_next; + match != NULL && match != compl_first_match; + match = match->cp_next) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go down and assign all numbers which are not + // assigned yet + for (match = match->cp_prev; + match && match->cp_number == -1; + match = match->cp_prev) { + match->cp_number = ++number; + } + } + } +} + +/// Get complete information +static void get_complete_info(list_T *what_list, dict_T *retdict) +{ +#define CI_WHAT_MODE 0x01 +#define CI_WHAT_PUM_VISIBLE 0x02 +#define CI_WHAT_ITEMS 0x04 +#define CI_WHAT_SELECTED 0x08 +#define CI_WHAT_INSERTED 0x10 +#define CI_WHAT_ALL 0xff + int what_flag; + + if (what_list == NULL) { + what_flag = CI_WHAT_ALL; + } else { + what_flag = 0; + for (listitem_T *item = tv_list_first(what_list) + ; item != NULL + ; item = TV_LIST_ITEM_NEXT(what_list, item)) { + const char *what = tv_get_string(TV_LIST_ITEM_TV(item)); + + if (STRCMP(what, "mode") == 0) { + what_flag |= CI_WHAT_MODE; + } else if (STRCMP(what, "pum_visible") == 0) { + what_flag |= CI_WHAT_PUM_VISIBLE; + } else if (STRCMP(what, "items") == 0) { + what_flag |= CI_WHAT_ITEMS; + } else if (STRCMP(what, "selected") == 0) { + what_flag |= CI_WHAT_SELECTED; + } else if (STRCMP(what, "inserted") == 0) { + what_flag |= CI_WHAT_INSERTED; + } + } + } + + int ret = OK; + if (what_flag & CI_WHAT_MODE) { + ret = tv_dict_add_str(retdict, S_LEN("mode"), + (char *)ins_compl_mode()); + } + + if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) { + ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible()); + } + + if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { + list_T *li = tv_list_alloc(get_compl_len()); + + ret = tv_dict_add_list(retdict, S_LEN("items"), li); + if (ret == OK && compl_first_match != NULL) { + compl_T *match = compl_first_match; + do { + if (!(match->cp_flags & CP_ORIGINAL_TEXT)) { + dict_T *di = tv_dict_alloc(); + + tv_list_append_dict(li, di); + tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + if (match->cp_user_data.v_type == VAR_UNKNOWN) { + tv_dict_add_str(di, S_LEN("user_data"), ""); + } else { + tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); + } + } + match = match->cp_next; + } while (match != NULL && match != compl_first_match); + } + } + + if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { + if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) { + ins_compl_update_sequence_numbers(); + } + ret = tv_dict_add_nr(retdict, S_LEN("selected"), + (compl_curr_match != NULL) + ? compl_curr_match->cp_number - 1 : -1); + } + + (void)ret; + // TODO(vim): + // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) +} + +/// "complete_info()" function +void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + + list_T *what_list = NULL; + + if (argvars[0].v_type != VAR_UNKNOWN) { + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + what_list = argvars[0].vval.v_list; + } + get_complete_info(what_list, rettv->vval.v_dict); +} + +/// Returns true when using a user-defined function for thesaurus completion. +static bool thesaurus_func_complete(int type) +{ + return type == CTRL_X_THESAURUS + && (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL); +} + +/// Get the next expansion(s), using "compl_pattern". +/// The search starts at position "ini" in curbuf and in the direction +/// compl_direction. +/// When "compl_started" is false start at that position, otherwise continue +/// where we stopped searching before. +/// This may return before finding all the matches. +/// Return the total number of matches or -1 if still unknown -- Acevedo +static int ins_compl_get_exp(pos_T *ini) +{ + static pos_T first_match_pos; + static pos_T last_match_pos; + static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' + static bool found_all = false; // Found all matches of a + // certain type. + static buf_T *ins_buf = NULL; // buffer being scanned + + pos_T *pos; + char **matches; + int save_p_scs; + bool save_p_ws; + int save_p_ic; + int i; + int num_matches; + int len; + int found_new_match; + int type = ctrl_x_mode; + char_u *ptr; + char_u *dict = NULL; + int dict_f = 0; + bool set_match_pos; + pos_T prev_pos = { 0, 0, 0 }; + + assert(curbuf != NULL); + + if (!compl_started) { + FOR_ALL_BUFFERS(buf) { + buf->b_scanned = false; + } + found_all = false; + ins_buf = curbuf; + e_cpt = (compl_cont_status & CONT_LOCAL) + ? (char_u *)"." : curbuf->b_p_cpt; + last_match_pos = first_match_pos = *ini; + } else if (ins_buf != curbuf && !buf_valid(ins_buf)) { + ins_buf = curbuf; // In case the buffer was wiped out. + } + assert(ins_buf != NULL); + + compl_old_match = compl_curr_match; // remember the last current match + pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; + + // For ^N/^P loop over all the flags/windows/buffers in 'complete' + for (;;) { + found_new_match = FAIL; + set_match_pos = false; + + // For ^N/^P pick a new entry from e_cpt if compl_started is off, + // or if found_all says this entry is done. For ^X^L only use the + // entries from 'complete' that look in loaded buffers. + if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval()) + && (!compl_started || found_all)) { + found_all = false; + while (*e_cpt == ',' || *e_cpt == ' ') { + e_cpt++; + } + if (*e_cpt == '.' && !curbuf->b_scanned) { + ins_buf = curbuf; + first_match_pos = *ini; + // Move the cursor back one character so that ^N can match the + // word immediately after the cursor. + if (ctrl_x_mode_normal() && dec(&first_match_pos) < 0) { + // Move the cursor to after the last character in the + // buffer, so that word at start of buffer is found + // correctly. + first_match_pos.lnum = ins_buf->b_ml.ml_line_count; + first_match_pos.col = (colnr_T)STRLEN(ml_get(first_match_pos.lnum)); + } + last_match_pos = first_match_pos; + type = 0; + + // Remember the first match so that the loop stops when we + // wrap and come back there a second time. + set_match_pos = true; + } else if (vim_strchr("buwU", *e_cpt) != NULL + && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { + // Scan a buffer, but not the current one. + if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer + compl_started = true; + first_match_pos.col = last_match_pos.col = 0; + first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; + last_match_pos.lnum = 0; + type = 0; + } else { // unloaded buffer, scan like dictionary + found_all = true; + if (ins_buf->b_fname == NULL) { + continue; + } + type = CTRL_X_DICTIONARY; + dict = (char_u *)ins_buf->b_fname; + dict_f = DICT_EXACT; + } + msg_hist_off = true; // reset in msg_trunc_attr() + vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), + ins_buf->b_fname == NULL + ? buf_spname(ins_buf) + : ins_buf->b_sfname == NULL + ? ins_buf->b_fname + : ins_buf->b_sfname); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } else if (*e_cpt == NUL) { + break; + } else { + if (ctrl_x_mode_line_or_eval()) { + type = -1; + } else if (*e_cpt == 'k' || *e_cpt == 's') { + if (*e_cpt == 'k') { + type = CTRL_X_DICTIONARY; + } else { + type = CTRL_X_THESAURUS; + } + if (*++e_cpt != ',' && *e_cpt != NUL) { + dict = e_cpt; + dict_f = DICT_FIRST; + } + } else if (*e_cpt == 'i') { + type = CTRL_X_PATH_PATTERNS; + } else if (*e_cpt == 'd') { + type = CTRL_X_PATH_DEFINES; + } else if (*e_cpt == ']' || *e_cpt == 't') { + msg_hist_off = true; // reset in msg_trunc_attr() + type = CTRL_X_TAGS; + vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); + (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); + } else { + type = -1; + } + + // in any case e_cpt is advanced to the next entry + (void)copy_option_part((char **)&e_cpt, (char *)IObuff, IOSIZE, ","); + + found_all = true; + if (type == -1) { + continue; + } + } + } + + // If complete() was called then compl_pattern has been reset. + // The following won't work then, bail out. + if (compl_pattern == NULL) { + break; + } + + switch (type) { + case -1: + break; + case CTRL_X_PATH_PATTERNS: + case CTRL_X_PATH_DEFINES: + find_pattern_in_path((char_u *)compl_pattern, compl_direction, + STRLEN(compl_pattern), false, false, + ((type == CTRL_X_PATH_DEFINES + && !(compl_cont_status & CONT_SOL)) + ? FIND_DEFINE + : FIND_ANY), + 1L, ACTION_EXPAND, 1, MAXLNUM); + break; + + case CTRL_X_DICTIONARY: + case CTRL_X_THESAURUS: + if (thesaurus_func_complete(type)) { + expand_by_function(type, (char_u *)compl_pattern); + } else { + ins_compl_dictionaries(dict != NULL ? dict + : (type == CTRL_X_THESAURUS + ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) + : (*curbuf->b_p_dict == + NUL ? p_dict : curbuf->b_p_dict)), + (char_u *)compl_pattern, + dict != NULL ? dict_f : 0, type == CTRL_X_THESAURUS); + } + dict = NULL; + break; + + case CTRL_X_TAGS: + // set p_ic according to p_ic, p_scs and pat for find_tags(). + save_p_ic = p_ic; + p_ic = ignorecase((char_u *)compl_pattern); + + // Find up to TAG_MANY matches. Avoids that an enormous number + // of matches is found when compl_pattern is empty + g_tag_at_cursor = true; + if (find_tags((char_u *)compl_pattern, &num_matches, &matches, + TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP + | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0), + TAG_MANY, (char_u *)curbuf->b_ffname) == OK && num_matches > 0) { + ins_compl_add_matches(num_matches, matches, p_ic); + } + g_tag_at_cursor = false; + p_ic = save_p_ic; + break; + + case CTRL_X_FILES: + if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, + EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { + // May change home directory back to "~". + tilde_replace((char_u *)compl_pattern, num_matches, matches); +#ifdef BACKSLASH_IN_FILENAME + if (curbuf->b_p_csl[0] != NUL) { + for (int i = 0; i < num_matches; i++) { + char_u *ptr = matches[i]; + while (*ptr != NUL) { + if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') { + *ptr = '/'; + } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') { + *ptr = '\\'; + } + ptr += utfc_ptr2len(ptr); + } + } + } +#endif + ins_compl_add_matches(num_matches, matches, p_fic || p_wic); + } + break; + + case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: + if (expand_cmdline(&compl_xp, (char_u *)compl_pattern, + (int)STRLEN(compl_pattern), + &num_matches, &matches) == EXPAND_OK) { + ins_compl_add_matches(num_matches, matches, false); + } + break; + + case CTRL_X_FUNCTION: + case CTRL_X_OMNI: + expand_by_function(type, (char_u *)compl_pattern); + break; + + case CTRL_X_SPELL: + num_matches = expand_spelling(first_match_pos.lnum, + (char_u *)compl_pattern, &matches); + if (num_matches > 0) { + ins_compl_add_matches(num_matches, matches, p_ic); + } + break; + + default: // normal ^P/^N and ^X^L + // If 'infercase' is set, don't use 'smartcase' here + save_p_scs = p_scs; + assert(ins_buf); + if (ins_buf->b_p_inf) { + p_scs = false; + } + + // Buffers other than curbuf are scanned from the beginning or the + // end but never from the middle, thus setting nowrapscan in this + // buffers is a good idea, on the other hand, we always set + // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb + save_p_ws = p_ws; + if (ins_buf != curbuf) { + p_ws = false; + } else if (*e_cpt == '.') { + p_ws = true; + } + bool looped_around = false; + for (;;) { + bool cont_s_ipos = false; + + msg_silent++; // Don't want messages for wrapscan. + // ctrl_x_mode_line_or_eval() || word-wise search that + // has added a word that was at the beginning of the line. + if (ctrl_x_mode_line_or_eval() + || (compl_cont_status & CONT_SOL)) { + found_new_match = search_for_exact_line(ins_buf, pos, + compl_direction, + (char_u *)compl_pattern); + } else { + found_new_match = searchit(NULL, ins_buf, pos, NULL, + compl_direction, + (char_u *)compl_pattern, 1L, + SEARCH_KEEP + SEARCH_NFMSG, + RE_LAST, NULL); + } + msg_silent--; + if (!compl_started || set_match_pos) { + // set "compl_started" even on fail + compl_started = true; + first_match_pos = *pos; + last_match_pos = *pos; + set_match_pos = false; + } else if (first_match_pos.lnum == last_match_pos.lnum + && first_match_pos.col == last_match_pos.col) { + found_new_match = FAIL; + } else if ((compl_direction == FORWARD) + && (prev_pos.lnum > pos->lnum + || (prev_pos.lnum == pos->lnum + && prev_pos.col >= pos->col))) { + if (looped_around) { + found_new_match = FAIL; + } else { + looped_around = true; + } + } else if ((compl_direction != FORWARD) + && (prev_pos.lnum < pos->lnum + || (prev_pos.lnum == pos->lnum + && prev_pos.col <= pos->col))) { + if (looped_around) { + found_new_match = FAIL; + } else { + looped_around = true; + } + } + prev_pos = *pos; + if (found_new_match == FAIL) { + if (ins_buf == curbuf) { + found_all = true; + } + break; + } + + // when ADDING, the text before the cursor matches, skip it + if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf + && ini->lnum == pos->lnum + && ini->col == pos->col) { + continue; + } + ptr = ml_get_buf(ins_buf, pos->lnum, false) + pos->col; + if (ctrl_x_mode_line_or_eval()) { + if (compl_cont_status & CONT_ADDING) { + if (pos->lnum >= ins_buf->b_ml.ml_line_count) { + continue; + } + ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); + if (!p_paste) { + ptr = (char_u *)skipwhite((char *)ptr); + } + } + len = (int)STRLEN(ptr); + } else { + char_u *tmp_ptr = ptr; + + if (compl_cont_status & CONT_ADDING) { + tmp_ptr += compl_length; + // Skip if already inside a word. + if (vim_iswordp(tmp_ptr)) { + continue; + } + // Find start of next word. + tmp_ptr = find_word_start(tmp_ptr); + } + // Find end of this word. + tmp_ptr = find_word_end(tmp_ptr); + len = (int)(tmp_ptr - ptr); + + if ((compl_cont_status & CONT_ADDING) + && len == compl_length) { + if (pos->lnum < ins_buf->b_ml.ml_line_count) { + // Try next line, if any. the new word will be "join" as if the + // normal command "J" was used. IOSIZE is always greater than + // compl_length, so the next STRNCPY always works -- Acevedo + STRNCPY(IObuff, ptr, len); // NOLINT(runtime/printf) + ptr = ml_get_buf(ins_buf, pos->lnum + 1, false); + tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr); + // Find start of next word. + tmp_ptr = find_word_start(tmp_ptr); + // Find end of next word. + tmp_ptr = find_word_end(tmp_ptr); + if (tmp_ptr > ptr) { + if (*ptr != ')' && IObuff[len - 1] != TAB) { + if (IObuff[len - 1] != ' ') { + IObuff[len++] = ' '; + } + // IObuf =~ "\k.* ", thus len >= 2 + if (p_js + && (IObuff[len - 2] == '.' + || IObuff[len - 2] == '?' + || IObuff[len - 2] == '!')) { + IObuff[len++] = ' '; + } + } + // copy as much as possible of the new word + if (tmp_ptr - ptr >= IOSIZE - len) { + tmp_ptr = ptr + IOSIZE - len - 1; + } + STRLCPY(IObuff + len, ptr, IOSIZE - len); + len += (int)(tmp_ptr - ptr); + cont_s_ipos = true; + } + IObuff[len] = NUL; + ptr = IObuff; + } + if (len == compl_length) { + continue; + } + } + } + if (ins_compl_add_infercase(ptr, len, p_ic, + ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname, + 0, cont_s_ipos) != NOTDONE) { + found_new_match = OK; + break; + } + } + p_scs = save_p_scs; + p_ws = save_p_ws; + } + + // check if compl_curr_match has changed, (e.g. other type of + // expansion added something) + if (type != 0 && compl_curr_match != compl_old_match) { + found_new_match = OK; + } + + // break the loop for specialized modes (use 'complete' just for the + // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match + if ((ctrl_x_mode_not_default() + && !ctrl_x_mode_line_or_eval()) + || found_new_match != FAIL) { + if (got_int) { + break; + } + // Fill the popup menu as soon as possible. + if (type != -1) { + ins_compl_check_keys(0, false); + } + + if ((ctrl_x_mode_not_default() + && !ctrl_x_mode_line_or_eval()) + || compl_interrupted) { + break; + } + compl_started = true; + } else { + // Mark a buffer scanned when it has been scanned completely + if (type == 0 || type == CTRL_X_PATH_PATTERNS) { + assert(ins_buf); + ins_buf->b_scanned = true; + } + + compl_started = false; + } + } + compl_started = true; + + if ((ctrl_x_mode_normal() + || ctrl_x_mode_line_or_eval()) + && *e_cpt == NUL) { // Got to end of 'complete' + found_new_match = FAIL; + } + + i = -1; // total of matches, unknown + if (found_new_match == FAIL + || (ctrl_x_mode_not_default() + && !ctrl_x_mode_line_or_eval())) { + i = ins_compl_make_cyclic(); + } + + if (compl_old_match != NULL) { + // If several matches were added (FORWARD) or the search failed and has + // just been made cyclic then we have to move compl_curr_match to the + // next or previous entry (if any) -- Acevedo + compl_curr_match = compl_direction == FORWARD + ? compl_old_match->cp_next + : compl_old_match->cp_prev; + if (compl_curr_match == NULL) { + compl_curr_match = compl_old_match; + } + } + may_trigger_modechanged(); + + return i; +} + +/// Delete the old text being completed. +void ins_compl_delete(void) +{ + int col; + + // In insert mode: Delete the typed part. + // In replace mode: Put the old characters back, if any. + col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); + if ((int)curwin->w_cursor.col > col) { + if (stop_arrow() == FAIL) { + return; + } + backspace_until_column(col); + } + + // TODO(vim): is this sufficient for redrawing? Redrawing everything + // causes flicker, thus we can't do that. + changed_cline_bef_curs(); + // clear v:completed_item + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); +} + +/// Insert the new text being completed. +/// "in_compl_func" is true when called from complete_check(). +void ins_compl_insert(bool in_compl_func) +{ + ins_bytes(compl_shown_match->cp_str + get_compl_len()); + compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT); + + dict_T *dict = ins_compl_dict_alloc(compl_shown_match); + set_vim_var_dict(VV_COMPLETED_ITEM, dict); + if (!in_compl_func) { + compl_curr_match = compl_shown_match; + } +} + +/// Fill in the next completion in the current direction. +/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to +/// get more completions. If it is false, then we just do nothing when there +/// are no more completions in a given direction. The latter case is used when +/// we are still in the middle of finding completions, to allow browsing +/// through the ones found so far. +/// @return the total number of matches, or -1 if still unknown -- webb. +/// +/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use +/// compl_shown_match here. +/// +/// Note that this function may be called recursively once only. First with +/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn +/// calls this function with "allow_get_expansion" false. +/// +/// @param count Repeat completion this many times; should be at least 1 +/// @param insert_match Insert the newly selected match +/// @param in_compl_func Called from complete_check() +static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match, + bool in_compl_func) +{ + int num_matches = -1; + int todo = count; + compl_T *found_compl = NULL; + bool found_end = false; + const bool started = compl_started; + + // When user complete function return -1 for findstart which is next + // time of 'always', compl_shown_match become NULL. + if (compl_shown_match == NULL) { + return -1; + } + + if (compl_leader != NULL + && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) { + // Set "compl_shown_match" to the actually shown match, it may differ + // when "compl_leader" is used to omit some of the matches. + while (!ins_compl_equal(compl_shown_match, + compl_leader, STRLEN(compl_leader)) + && compl_shown_match->cp_next != NULL + && compl_shown_match->cp_next != compl_first_match) { + compl_shown_match = compl_shown_match->cp_next; + } + + // If we didn't find it searching forward, and compl_shows_dir is + // backward, find the last match. + if (compl_shows_dir == BACKWARD + && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) + && (compl_shown_match->cp_next == NULL + || compl_shown_match->cp_next == compl_first_match)) { + while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader)) + && compl_shown_match->cp_prev != NULL + && compl_shown_match->cp_prev != compl_first_match) { + compl_shown_match = compl_shown_match->cp_prev; + } + } + } + + if (allow_get_expansion && insert_match + && (!(compl_get_longest || compl_restarting) || compl_used_match)) { + // Delete old text to be replaced + ins_compl_delete(); + } + + // When finding the longest common text we stick at the original text, + // don't let CTRL-N or CTRL-P move to the first match. + bool advance = count != 1 || !allow_get_expansion || !compl_get_longest; + + // When restarting the search don't insert the first match either. + if (compl_restarting) { + advance = false; + compl_restarting = false; + } + + // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap + // around. + while (--todo >= 0) { + if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { + compl_shown_match = compl_shown_match->cp_next; + found_end = (compl_first_match != NULL + && (compl_shown_match->cp_next == compl_first_match + || compl_shown_match == compl_first_match)); + } else if (compl_shows_dir == BACKWARD + && compl_shown_match->cp_prev != NULL) { + found_end = (compl_shown_match == compl_first_match); + compl_shown_match = compl_shown_match->cp_prev; + found_end |= (compl_shown_match == compl_first_match); + } else { + if (!allow_get_expansion) { + if (advance) { + if (compl_shows_dir == BACKWARD) { + compl_pending -= todo + 1; + } else { + compl_pending += todo + 1; + } + } + return -1; + } + + if (!compl_no_select && advance) { + if (compl_shows_dir == BACKWARD) { + compl_pending--; + } else { + compl_pending++; + } + } + + // Find matches. + num_matches = ins_compl_get_exp(&compl_startpos); + + // handle any pending completions + while (compl_pending != 0 && compl_direction == compl_shows_dir + && advance) { + if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { + compl_shown_match = compl_shown_match->cp_next; + compl_pending--; + } + if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) { + compl_shown_match = compl_shown_match->cp_prev; + compl_pending++; + } else { + break; + } + } + found_end = false; + } + if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0 + && compl_leader != NULL + && !ins_compl_equal(compl_shown_match, + compl_leader, STRLEN(compl_leader))) { + todo++; + } else { + // Remember a matching item. + found_compl = compl_shown_match; + } + + // Stop at the end of the list when we found a usable match. + if (found_end) { + if (found_compl != NULL) { + compl_shown_match = found_compl; + break; + } + todo = 1; // use first usable match after wrapping around + } + } + + // Insert the text of the new completion, or the compl_leader. + if (compl_no_insert && !started) { + ins_bytes(compl_orig_text + get_compl_len()); + compl_used_match = false; + } else if (insert_match) { + if (!compl_get_longest || compl_used_match) { + ins_compl_insert(in_compl_func); + } else { + ins_bytes(compl_leader + get_compl_len()); + } + } else { + compl_used_match = false; + } + + if (!allow_get_expansion) { + // redraw to show the user what was inserted + update_screen(0); + + // display the updated popup menu + ins_compl_show_pum(); + + // Delete old text to be replaced, since we're still searching and + // don't want to match ourselves! + ins_compl_delete(); + } + + // Enter will select a match when the match wasn't inserted and the popup + // menu is visible. + if (compl_no_insert && !started) { + compl_enter_selects = true; + } else { + compl_enter_selects = !insert_match && compl_match_array != NULL; + } + + // Show the file name for the match (if any) + // Truncate the file name to avoid a wait for return. + if (compl_shown_match->cp_fname != NULL) { + char *lead = _("match in file"); + int space = sc_col - vim_strsize(lead) - 2; + char *s; + char *e; + + if (space > 0) { + // We need the tail that fits. With double-byte encoding going + // back from the end is very slow, thus go from the start and keep + // the text that fits in "space" between "s" and "e". + for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) { + space -= ptr2cells(e); + while (space < 0) { + space += ptr2cells(s); + MB_PTR_ADV(s); + } + } + msg_hist_off = true; + vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, + (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s); + msg((char *)IObuff); + msg_hist_off = false; + redraw_cmdline = false; // don't overwrite! + } + } + + return num_matches; +} + +void pum_ext_select_item(int item, bool insert, bool finish) +{ + if (!pum_visible() || item < -1 || item >= compl_match_arraysize) { + return; + } + pum_want.active = true; + pum_want.item = item; + pum_want.insert = insert; + pum_want.finish = finish; +} + +/// Call this while finding completions, to check whether the user has hit a key +/// that should change the currently displayed completion, or exit completion +/// mode. Also, when compl_pending is not zero, show a completion as soon as +/// possible. -- webb +/// +/// @param frequency specifies out of how many calls we actually check. +/// @param in_compl_func true when called from complete_check(), don't set +/// compl_curr_match. +void ins_compl_check_keys(int frequency, bool in_compl_func) +{ + static int count = 0; + + // Don't check when reading keys from a script, :normal or feedkeys(). + // That would break the test scripts. But do check for keys when called + // from complete_check(). + if (!in_compl_func && (using_script() || ex_normal_busy)) { + return; + } + + // Only do this at regular intervals + if (++count < frequency) { + return; + } + count = 0; + + // Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key() + // can't do its work correctly. + int c = vpeekc_any(); + if (c != NUL) { + if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) { + c = safe_vgetc(); // Eat the character + compl_shows_dir = ins_compl_key2dir(c); + (void)ins_compl_next(false, ins_compl_key2count(c), + c != K_UP && c != K_DOWN, in_compl_func); + } else { + // Need to get the character to have KeyTyped set. We'll put it + // back with vungetc() below. But skip K_IGNORE. + c = safe_vgetc(); + if (c != K_IGNORE) { + // Don't interrupt completion when the character wasn't typed, + // e.g., when doing @q to replay keys. + if (c != Ctrl_R && KeyTyped) { + compl_interrupted = true; + } + + vungetc(c); + } + } + } + if (compl_pending != 0 && !got_int && !compl_no_insert) { + int todo = compl_pending > 0 ? compl_pending : -compl_pending; + + compl_pending = 0; + (void)ins_compl_next(false, todo, true, in_compl_func); + } +} + +/// Decide the direction of Insert mode complete from the key typed. +/// Returns BACKWARD or FORWARD. +static int ins_compl_key2dir(int c) +{ + if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { + return pum_want.item < pum_selected_item ? BACKWARD : FORWARD; + } + if (c == Ctrl_P || c == Ctrl_L + || c == K_PAGEUP || c == K_KPAGEUP + || c == K_S_UP || c == K_UP) { + return BACKWARD; + } + return FORWARD; +} + +/// 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); +} + +/// Decide the number of completions to move forward. +/// Returns 1 for most keys, height of the popup menu for page-up/down keys. +static int ins_compl_key2count(int c) +{ + int h; + + if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { + int offset = pum_want.item - pum_selected_item; + return abs(offset); + } + + if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) { + h = pum_get_height(); + if (h > 3) { + h -= 2; // keep some context + } + return h; + } + return 1; +} + +/// 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: + case K_DOWN: + case K_PAGEDOWN: + case K_KPAGEDOWN: + case K_S_DOWN: + case K_PAGEUP: + case K_KPAGEUP: + case K_S_UP: + return false; + case K_EVENT: + case K_COMMAND: + case K_LUA: + return pum_want.active && pum_want.insert; + } + return true; +} + +/// Get the pattern, column and length for normal completion (CTRL-N CTRL-P +/// completion) +/// Sets the global variables: compl_col, compl_length and compl_pattern. +/// Uses the global variables: compl_cont_status and ctrl_x_mode +static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col) +{ + if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines()) { + if (!(compl_cont_status & CONT_ADDING)) { + while (--startcol >= 0 && vim_isIDc(line[startcol])) {} + compl_col += ++startcol; + compl_length = curs_col - startcol; + } + if (p_ic) { + compl_pattern = (char *)str_foldcase(line + compl_col, compl_length, NULL, 0); + } else { + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + } + } else if (compl_cont_status & CONT_ADDING) { + char_u *prefix = (char_u *)"\\<"; + + // we need up to 2 extra chars for the prefix + compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, + compl_length) + 2); + if (!vim_iswordp(line + compl_col) + || (compl_col > 0 && (vim_iswordp(mb_prevptr(line, line + compl_col))))) { + prefix = (char_u *)""; + } + STRCPY(compl_pattern, prefix); + (void)quote_meta((char_u *)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 + compl_pattern = (char *)vim_strsave((char_u *)"\\<\\k\\k"); + compl_col += curs_col; + compl_length = 0; + } else { + // Search the point of change class of multibyte character + // or not a word single byte character backward. + startcol -= utf_head_off(line, line + startcol); + int base_class = mb_get_class(line + startcol); + while (--startcol >= 0) { + int head_off = utf_head_off(line, line + startcol); + if (base_class != mb_get_class(line + startcol - head_off)) { + break; + } + startcol -= head_off; + } + compl_col += ++startcol; + compl_length = (int)curs_col - startcol; + if (compl_length == 1) { + // Only match word with at least two chars -- webb + // there's no need to call quote_meta, + // xmalloc(7) is enough -- Acevedo + compl_pattern = xmalloc(7); + STRCPY(compl_pattern, "\\<"); + (void)quote_meta((char_u *)compl_pattern + 2, line + compl_col, 1); + STRCAT(compl_pattern, "\\k"); + } else { + compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, + compl_length) + 2); + STRCPY(compl_pattern, "\\<"); + (void)quote_meta((char_u *)compl_pattern + 2, line + compl_col, compl_length); + } + } + + return OK; +} + +/// Get the pattern, column and length for whole line completion or for the +/// complete() function. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +static int get_wholeline_compl_info(char_u *line, colnr_T curs_col) +{ + compl_col = (colnr_T)getwhitecols(line); + compl_length = (int)curs_col - (int)compl_col; + if (compl_length < 0) { // cursor in indent: empty pattern + compl_length = 0; + } + if (p_ic) { + compl_pattern = (char *)str_foldcase(line + compl_col, compl_length, NULL, 0); + } else { + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + } + + return OK; +} + +/// Get the pattern, column and length for filename completion. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +static int get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col) +{ + // Go back to just before the first filename character. + if (startcol > 0) { + char_u *p = line + startcol; + + MB_PTR_BACK(line, p); + while (p > line && vim_isfilec(utf_ptr2char((char *)p))) { + MB_PTR_BACK(line, p); + } + if (p == line && vim_isfilec(utf_ptr2char((char *)p))) { + startcol = 0; + } else { + startcol = (int)(p - line) + 1; + } + } + + compl_col += startcol; + compl_length = (int)curs_col - startcol; + compl_pattern = (char *)addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES); + + return OK; +} + +/// Get the pattern, column and length for command-line completion. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +static int get_cmdline_compl_info(char_u *line, colnr_T curs_col) +{ + compl_pattern = (char *)vim_strnsave(line, (size_t)curs_col); + set_cmd_context(&compl_xp, (char_u *)compl_pattern, (int)STRLEN(compl_pattern), curs_col, false); + if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL + || compl_xp.xp_context == EXPAND_NOTHING) { + // No completion possible, use an empty pattern to get a + // "pattern not found" message. + compl_col = curs_col; + } else { + compl_col = (int)(compl_xp.xp_pattern - compl_pattern); + } + compl_length = curs_col - compl_col; + + return OK; +} + +/// Get the pattern, column and length for user defined completion ('omnifunc', +/// 'completefunc' and 'thesaurusfunc') +/// Sets the global variables: compl_col, compl_length and compl_pattern. +/// Uses the global variable: spell_bad_len +static int get_userdefined_compl_info(colnr_T curs_col) +{ + // Call user defined function 'completefunc' with "a:findstart" + // set to 1 to obtain the length of text to use for completion. + const int save_State = State; + + // Call 'completefunc' or 'omnifunc' and get pattern length as a string + char_u *funcname = get_complete_funcname(ctrl_x_mode); + if (*funcname == NUL) { + semsg(_(e_notset), ctrl_x_mode_function() ? "completefunc" : "omnifunc"); + return FAIL; + } + + typval_T args[3]; + args[0].v_type = VAR_NUMBER; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_UNKNOWN; + args[0].vval.v_number = 1; + args[1].vval.v_string = ""; + + pos_T pos = curwin->w_cursor; + textlock++; + colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args); + textlock--; + + State = save_State; + curwin->w_cursor = pos; // restore the cursor position + validate_cursor(); + if (!equalpos(curwin->w_cursor, pos)) { + emsg(_(e_compldel)); + return FAIL; + } + + // Return value -2 means the user complete function wants to cancel the + // complete without an error, do the same if the function did not execute + // successfully. + if (col == -2 || aborting()) { + return FAIL; + } + // Return value -3 does the same as -2 and leaves CTRL-X mode. + if (col == -3) { + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + if (!shortmess(SHM_COMPLETIONMENU)) { + msg_clr_cmdline(); + } + return FAIL; + } + + // Reset extended parameters of completion, when start new + // completion. + compl_opt_refresh_always = false; + + if (col < 0) { + col = curs_col; + } + compl_col = col; + if (compl_col > curs_col) { + compl_col = curs_col; + } + + // Setup variables for completion. Need to obtain "line" again, + // it may have become invalid. + char_u *line = ml_get(curwin->w_cursor.lnum); + compl_length = curs_col - compl_col; + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + + return OK; +} + +/// Get the pattern, column and length for spell completion. +/// Sets the global variables: compl_col, compl_length and compl_pattern. +/// Uses the global variable: spell_bad_len +static int get_spell_compl_info(int startcol, colnr_T curs_col) +{ + if (spell_bad_len > 0) { + assert(spell_bad_len <= INT_MAX); + compl_col = curs_col - (int)spell_bad_len; + } else { + compl_col = spell_word_start(startcol); + } + if (compl_col >= (colnr_T)startcol) { + compl_length = 0; + compl_col = curs_col; + } else { + spell_expand_check_cap(compl_col); + compl_length = (int)curs_col - compl_col; + } + // Need to obtain "line" again, it may have become invalid. + char_u *line = ml_get(curwin->w_cursor.lnum); + compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length); + + return OK; +} + +/// Get the completion pattern, column and length. +static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *line_invalid) +{ + if (ctrl_x_mode_normal() + || ((ctrl_x_mode & CTRL_X_WANT_IDENT) + && !thesaurus_func_complete(ctrl_x_mode))) { + return get_normal_compl_info(line, startcol, curs_col); + } else if (ctrl_x_mode_line_or_eval()) { + return get_wholeline_compl_info(line, curs_col); + } else if (ctrl_x_mode_files()) { + return get_filename_compl_info(line, startcol, curs_col); + } else if (ctrl_x_mode == CTRL_X_CMDLINE) { + return get_cmdline_compl_info(line, curs_col); + } else if (ctrl_x_mode_function() || ctrl_x_mode_omni() + || thesaurus_func_complete(ctrl_x_mode)) { + if (get_userdefined_compl_info(curs_col) == FAIL) { + return FAIL; + } + *line_invalid = true; // 'line' may have become invalid + } else if (ctrl_x_mode_spell()) { + if (get_spell_compl_info(startcol, curs_col) == FAIL) { + return FAIL; + } + *line_invalid = true; // 'line' may have become invalid + } else { + internal_error("ins_complete()"); + return FAIL; + } + + return OK; +} + +/// Do Insert mode completion. +/// Called when character "c" was typed, which has a meaning for completion. +/// Returns OK if completion was done, FAIL if something failed. +int ins_complete(int c, bool enable_pum) +{ + char_u *line; + int startcol = 0; // column where searched text starts + colnr_T curs_col; // cursor column + int n; + int save_w_wrow; + int save_w_leftcol; + int insert_match; + const bool save_did_ai = did_ai; + int flags = CP_ORIGINAL_TEXT; + bool line_invalid = false; + + compl_direction = ins_compl_key2dir(c); + insert_match = ins_compl_use_match(c); + + if (!compl_started) { + // First time we hit ^N or ^P (in a row, I mean) + + did_ai = false; + did_si = false; + can_si = false; + can_si_back = false; + if (stop_arrow() == FAIL) { + return FAIL; + } + + line = ml_get(curwin->w_cursor.lnum); + curs_col = curwin->w_cursor.col; + compl_pending = 0; + + // If this same ctrl_x_mode has been interrupted use the text from + // "compl_startpos" to the cursor as a pattern to add a new word + // instead of expand the one before the cursor, in word-wise if + // "compl_startpos" is not in the same line as the cursor then fix it + // (the line has been split because it was longer than 'tw'). if SOL + // is set then skip the previous pattern, a word at the beginning of + // the line has been inserted, we'll look for that -- Acevedo. + if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT + && compl_cont_mode == ctrl_x_mode) { + // it is a continued search + compl_cont_status &= ~CONT_INTRPT; // remove INTRPT + if (ctrl_x_mode_normal() + || ctrl_x_mode_path_patterns() + || ctrl_x_mode_path_defines()) { + if (compl_startpos.lnum != curwin->w_cursor.lnum) { + // line (probably) wrapped, set compl_startpos to the + // first non_blank in the line, if it is not a wordchar + // include it to get a better pattern, but then we don't + // want the "\\<" prefix, check it below. + compl_col = (colnr_T)getwhitecols(line); + compl_startpos.col = compl_col; + compl_startpos.lnum = curwin->w_cursor.lnum; + compl_cont_status &= ~CONT_SOL; // clear SOL if present + } else { + // S_IPOS was set when we inserted a word that was at the + // beginning of the line, which means that we'll go to SOL + // mode but first we need to redefine compl_startpos + if (compl_cont_status & CONT_S_IPOS) { + compl_cont_status |= CONT_SOL; + compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line + + compl_length + + compl_startpos.col) - line); + } + compl_col = compl_startpos.col; + } + compl_length = curwin->w_cursor.col - (int)compl_col; + // IObuff is used to add a "word from the next line" would we + // have enough space? just being paranoid +#define MIN_SPACE 75 + if (compl_length > (IOSIZE - MIN_SPACE)) { + compl_cont_status &= ~CONT_SOL; + compl_length = (IOSIZE - MIN_SPACE); + compl_col = curwin->w_cursor.col - compl_length; + } + compl_cont_status |= CONT_ADDING | CONT_N_ADDS; + if (compl_length < 1) { + compl_cont_status &= CONT_LOCAL; + } + } else if (ctrl_x_mode_line_or_eval()) { + compl_cont_status = CONT_ADDING | CONT_N_ADDS; + } else { + compl_cont_status = 0; + } + } else { + compl_cont_status &= CONT_LOCAL; + } + + if (!(compl_cont_status & CONT_ADDING)) { // normal expansion + compl_cont_mode = ctrl_x_mode; + if (ctrl_x_mode_not_default()) { + // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL + compl_cont_status = 0; + } + compl_cont_status |= CONT_N_ADDS; + compl_startpos = curwin->w_cursor; + startcol = (int)curs_col; + compl_col = 0; + } + + // Work out completion pattern and original text -- webb + if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) { + if (ctrl_x_mode_function() || ctrl_x_mode_omni() + || thesaurus_func_complete(ctrl_x_mode)) { + // restore did_ai, so that adding comment leader works + did_ai = save_did_ai; + } + return FAIL; + } + // If "line" was changed while getting completion info get it again. + if (line_invalid) { + line = ml_get(curwin->w_cursor.lnum); + } + + if (compl_cont_status & CONT_ADDING) { + edit_submode_pre = (char_u *)_(" Adding"); + if (ctrl_x_mode_line_or_eval()) { + // Insert a new line, keep indentation but ignore 'comments'. + char_u *old = curbuf->b_p_com; + + curbuf->b_p_com = (char_u *)""; + compl_startpos.lnum = curwin->w_cursor.lnum; + compl_startpos.col = compl_col; + ins_eol('\r'); + curbuf->b_p_com = old; + compl_length = 0; + compl_col = curwin->w_cursor.col; + } + } else { + edit_submode_pre = NULL; + compl_startpos.col = compl_col; + } + + if (compl_cont_status & CONT_LOCAL) { + edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); + } else { + edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); + } + + // If any of the original typed text has been changed we need to fix + // the redo buffer. + ins_compl_fixRedoBufForLeader(NULL); + + // Always add completion for the original text. + xfree(compl_orig_text); + compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length); + if (p_ic) { + flags |= CP_ICASE; + } + if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, + flags, false) != OK) { + XFREE_CLEAR(compl_pattern); + XFREE_CLEAR(compl_orig_text); + return FAIL; + } + + // showmode might reset the internal line pointers, so it must + // be called before line = ml_get(), or when this address is no + // longer needed. -- Acevedo. + edit_submode_extra = (char_u *)_("-- Searching..."); + edit_submode_highl = HLF_COUNT; + showmode(); + edit_submode_extra = NULL; + ui_flush(); + } else if (insert_match && stop_arrow() == FAIL) { + return FAIL; + } + + compl_shown_match = compl_curr_match; + compl_shows_dir = compl_direction; + + // Find next match (and following matches). + save_w_wrow = curwin->w_wrow; + save_w_leftcol = curwin->w_leftcol; + n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false); + + if (n > 1) { // all matches have been found + compl_matches = n; + } + compl_curr_match = compl_shown_match; + compl_direction = compl_shows_dir; + + // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert + // mode. + if (got_int && !global_busy) { + (void)vgetc(); + got_int = false; + } + + // we found no match if the list has only the "compl_orig_text"-entry + if (compl_first_match == compl_first_match->cp_next) { + edit_submode_extra = (compl_cont_status & CONT_ADDING) + && compl_length > 1 + ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); + edit_submode_highl = HLF_E; + // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, + // because we couldn't expand anything at first place, but if we used + // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word + // (such as M in M'exico) if not tried already. -- Acevedo + if (compl_length > 1 + || (compl_cont_status & CONT_ADDING) + || (ctrl_x_mode_not_default() + && !ctrl_x_mode_path_patterns() + && !ctrl_x_mode_path_defines())) { + compl_cont_status &= ~CONT_N_ADDS; + } + } + + if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) { + compl_cont_status |= CONT_S_IPOS; + } else { + compl_cont_status &= ~CONT_S_IPOS; + } + + if (edit_submode_extra == NULL) { + if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) { + edit_submode_extra = (char_u *)_("Back at original"); + edit_submode_highl = HLF_W; + } else if (compl_cont_status & CONT_S_IPOS) { + edit_submode_extra = (char_u *)_("Word from other line"); + edit_submode_highl = HLF_COUNT; + } else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) { + edit_submode_extra = (char_u *)_("The only match"); + edit_submode_highl = HLF_COUNT; + compl_curr_match->cp_number = 1; + } else { + // Update completion sequence number when needed. + if (compl_curr_match->cp_number == -1) { + ins_compl_update_sequence_numbers(); + } + + // The match should always have a sequence number now, this is + // just a safety check. + if (compl_curr_match->cp_number != -1) { + // Space for 10 text chars. + 2x10-digit no.s = 31. + // Translations may need more than twice that. + static char_u match_ref[81]; + + if (compl_matches > 0) { + vim_snprintf((char *)match_ref, sizeof(match_ref), + _("match %d of %d"), + compl_curr_match->cp_number, compl_matches); + } else { + vim_snprintf((char *)match_ref, sizeof(match_ref), + _("match %d"), + compl_curr_match->cp_number); + } + edit_submode_extra = match_ref; + edit_submode_highl = HLF_R; + if (dollar_vcol >= 0) { + curs_columns(curwin, false); + } + } + } + } + + // Show a message about what (completion) mode we're in. + showmode(); + if (!shortmess(SHM_COMPLETIONMENU)) { + if (edit_submode_extra != NULL) { + if (!p_smd) { + msg_hist_off = true; + msg_attr((const char *)edit_submode_extra, + (edit_submode_highl < HLF_COUNT + ? HL_ATTR(edit_submode_highl) : 0)); + msg_hist_off = false; + } + } else { + msg_clr_cmdline(); // necessary for "noshowmode" + } + } + + // Show the popup menu, unless we got interrupted. + if (enable_pum && !compl_interrupted) { + show_pum(save_w_wrow, save_w_leftcol); + } + compl_was_interrupted = compl_interrupted; + compl_interrupted = false; + + return OK; +} + +static void show_pum(int prev_w_wrow, int prev_w_leftcol) +{ + // RedrawingDisabled may be set when invoked through complete(). + int n = RedrawingDisabled; + RedrawingDisabled = 0; + + // If the cursor moved or the display scrolled we need to remove the pum + // first. + setcursor(); + if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol) { + ins_compl_del_pum(); + } + + ins_compl_show_pum(); + setcursor(); + RedrawingDisabled = n; +} + +// Looks in the first "len" chars. of "src" for search-metachars. +// If dest is not NULL the chars. are copied there quoting (with +// a backslash) the metachars, and dest would be NUL terminated. +// Returns the length (needed) of dest +static unsigned quote_meta(char_u *dest, char_u *src, int len) +{ + unsigned m = (unsigned)len + 1; // one extra for the NUL + + for (; --len >= 0; src++) { + switch (*src) { + case '.': + case '*': + case '[': + if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus()) { + break; + } + FALLTHROUGH; + case '~': + if (!p_magic) { // quote these only if magic is set + break; + } + FALLTHROUGH; + case '\\': + if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus()) { + break; + } + FALLTHROUGH; + case '^': // currently it's not needed. + case '$': + m++; + if (dest != NULL) { + *dest++ = '\\'; + } + break; + } + if (dest != NULL) { + *dest++ = *src; + } + // Copy remaining bytes of a multibyte character. + const int mb_len = utfc_ptr2len((char *)src) - 1; + if (mb_len > 0 && len >= mb_len) { + for (int i = 0; i < mb_len; i++) { + len--; + src++; + if (dest != NULL) { + *dest++ = *src; + } + } + } + } + if (dest != NULL) { + *dest = NUL; + } + + return m; +} + +#if defined(EXITFREE) +void free_insexpand_stuff(void) +{ + XFREE_CLEAR(compl_orig_text); +} +#endif + +/// Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly +/// spelled word, if there is one. +static void spell_back_to_badword(void) +{ + pos_T tpos = curwin->w_cursor; + spell_bad_len = spell_move_to(curwin, BACKWARD, true, true, NULL); + if (curwin->w_cursor.col != tpos.col) { + start_arrow(&tpos); + } +} diff --git a/src/nvim/insexpand.h b/src/nvim/insexpand.h new file mode 100644 index 0000000000..8e183455ca --- /dev/null +++ b/src/nvim/insexpand.h @@ -0,0 +1,17 @@ +#ifndef NVIM_INSEXPAND_H +#define NVIM_INSEXPAND_H + +#include "nvim/vim.h" + +/// state for pum_ext_select_item. +EXTERN struct { + bool active; + int item; + bool insert; + bool finish; +} pum_want; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "insexpand.h.generated.h" +#endif +#endif // NVIM_INSEXPAND_H diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c index cd3c7316bf..9899f10788 100644 --- a/src/nvim/keycodes.c +++ b/src/nvim/keycodes.c @@ -8,7 +8,7 @@ #include "nvim/ascii.h" #include "nvim/charset.h" #include "nvim/edit.h" -#include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/keycodes.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -392,7 +392,7 @@ static struct mousetable { { (int)KE_X2DRAG, MOUSE_X2, false, true }, { (int)KE_X2RELEASE, MOUSE_X2, false, false }, // DRAG without CLICK - { (int)K_MOUSEMOVE, MOUSE_RELEASE, false, true }, + { (int)KE_MOUSEMOVE, MOUSE_RELEASE, false, true }, // RELEASE without CLICK { (int)KE_IGNORE, MOUSE_RELEASE, false, false }, { 0, 0, 0, 0 }, diff --git a/src/nvim/keycodes.h b/src/nvim/keycodes.h index 67ec092f60..943c127325 100644 --- a/src/nvim/keycodes.h +++ b/src/nvim/keycodes.h @@ -7,10 +7,8 @@ // // Any special key code sequences are replaced by these codes. -// -// For MS-DOS some keys produce codes larger than 0xff. They are split into two -// chars, the first one is K_NUL. -// +/// For MS-DOS some keys produce codes larger than 0xff. They are split into two +/// chars, the first one is K_NUL. #define K_NUL (0xce) // for MS-DOS: special key follows /// K_SPECIAL is the first byte of a special key code and is always followed by @@ -59,13 +57,13 @@ #define KS_SELECT 245 #define K_SELECT_STRING (char_u *)"\200\365X" -// Used a termcap entry that produces a normal character. +/// Used a termcap entry that produces a normal character. #define KS_KEY 242 -// Used for click in a tab pages label. +/// Used for click in a tab pages label. #define KS_TABLINE 240 -// Used for menu in a tab pages line. +/// Used for menu in a tab pages line. #define KS_TABMENU 239 /// Filler used after KS_SPECIAL and others @@ -89,18 +87,19 @@ #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. -// The numbers are fixed to make sure that recorded key sequences remain valid. -// Add new entries at the end, not halfway. -// -// K_SPECIAL KS_EXTRA KE_xxx -// -// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). +/// Codes for keys that do not have a termcap name. +/// The numbers are fixed to make sure that recorded key sequences remain valid. +/// Add new entries at the end, not halfway. +/// +/// K_SPECIAL KS_EXTRA KE_xxx +/// +/// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). enum key_extra { KE_S_UP = 4, // shift-up - KE_S_DOWN = 5, // shift-down + KE_S_DOWN = 5, // shift-down - KE_S_F1 = 6, // shifted function keys + // shifted function keys + KE_S_F1 = 6, KE_S_F2 = 7, KE_S_F3 = 8, KE_S_F4 = 9, @@ -141,7 +140,7 @@ enum key_extra { KE_S_F36 = 41, KE_S_F37 = 42, - KE_MOUSE = 43, // mouse event start + KE_MOUSE = 43, // mouse event start // Symbols for pseudo keys which are translated from the real key symbols // above. @@ -153,14 +152,14 @@ enum key_extra { KE_MIDDLERELEASE = 49, // Middle mouse button release KE_RIGHTMOUSE = 50, // Right mouse button click KE_RIGHTDRAG = 51, // Drag with right mouse button down - KE_RIGHTRELEASE = 52, // Right mouse button release + KE_RIGHTRELEASE = 52, // Right mouse button release - KE_IGNORE = 53, // Ignored mouse drag/release + KE_IGNORE = 53, // Ignored mouse drag/release KE_TAB = 54, // unshifted TAB key - KE_S_TAB_OLD = 55, // shifted TAB key (no longer used) + KE_S_TAB_OLD = 55, // shifted TAB key (no longer used) - // , KE_SNIFF_UNUSED = 56 // obsolete + // KE_SNIFF_UNUSED = 56, // obsolete KE_XF1 = 57, // extra vt100 function keys for xterm KE_XF2 = 58, KE_XF3 = 59, @@ -175,7 +174,7 @@ enum key_extra { KE_XRIGHT = 68, KE_LEFTMOUSE_NM = 69, // non-mappable Left mouse button click - KE_LEFTRELEASE_NM = 70, // non-mappable left mouse button release + KE_LEFTRELEASE_NM = 70, // non-mappable left mouse button release KE_S_XF1 = 71, // vt100 shifted function keys for xterm KE_S_XF2 = 72, @@ -188,20 +187,20 @@ enum key_extra { KE_MOUSEDOWN = 75, // scroll wheel pseudo-button Down KE_MOUSEUP = 76, // scroll wheel pseudo-button Up KE_MOUSELEFT = 77, // scroll wheel pseudo-button Left - KE_MOUSERIGHT = 78, // scroll wheel pseudo-button Right + KE_MOUSERIGHT = 78, // scroll wheel pseudo-button Right KE_KINS = 79, // keypad Insert key - KE_KDEL = 80, // keypad Delete key + KE_KDEL = 80, // keypad Delete key // KE_CSI = 81, // Nvim doesn't need escaping CSI KE_SNR = 82, // <SNR> KE_PLUG = 83, // <Plug> - KE_CMDWIN = 84, // open command-line window from Command-line Mode + KE_CMDWIN = 84, // open command-line window from Command-line Mode KE_C_LEFT = 85, // control-left KE_C_RIGHT = 86, // control-right KE_C_HOME = 87, // control-home - KE_C_END = 88, // control-end + KE_C_END = 88, // control-end KE_X1MOUSE = 89, // X1/X2 mouse-buttons KE_X1DRAG = 90, @@ -210,16 +209,16 @@ enum key_extra { KE_X2DRAG = 93, KE_X2RELEASE = 94, - KE_DROP = 95, // DnD data is available - // , KE_CURSORHOLD = 96 // CursorHold event - KE_NOP = 97, // no-op: does nothing - // , KE_FOCUSGAINED = 98 // focus gained - // , KE_FOCUSLOST = 99 // focus lost - KE_MOUSEMOVE = 100, // mouse moved with no button down - // , KE_CANCEL = 101 // return from vgetc + KE_DROP = 95, // DnD data is available + // KE_CURSORHOLD = 96, // CursorHold event + KE_NOP = 97, // no-op: does nothing + // KE_FOCUSGAINED = 98, // focus gained + // KE_FOCUSLOST = 99, // focus lost + KE_MOUSEMOVE = 100, // mouse moved with no button down + // KE_CANCEL = 101, // return from vgetc() KE_EVENT = 102, // event - KE_LUA = 103, // lua special key - KE_COMMAND = 104, // <Cmd> special key + KE_LUA = 103, // Lua special key + KE_COMMAND = 104, // <Cmd> special key }; // the three byte codes are replaced with the following int when using vgetc() @@ -259,7 +258,8 @@ enum key_extra { #define K_XLEFT TERMCAP2KEY(KS_EXTRA, KE_XLEFT) #define K_XRIGHT TERMCAP2KEY(KS_EXTRA, KE_XRIGHT) -#define K_F1 TERMCAP2KEY('k', '1') // function keys +// function keys +#define K_F1 TERMCAP2KEY('k', '1') #define K_F2 TERMCAP2KEY('k', '2') #define K_F3 TERMCAP2KEY('k', '3') #define K_F4 TERMCAP2KEY('k', '4') @@ -490,13 +490,13 @@ enum key_extra { /// Current longest is <M-C-S-T-D-A-4-ScrollWheelRight> (length includes '<' and '>'). #define MAX_KEY_NAME_LEN 32 -// 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. +/// 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 @@ -504,7 +504,7 @@ enum key_extra { ? 0 \ : FLAG_CPO_BSLASH) -// Flags for replace_termcodes() +/// Flags for replace_termcodes() enum { REPTERM_FROM_PART = 1, REPTERM_DO_LT = 2, @@ -512,7 +512,7 @@ enum { REPTERM_NO_SIMPLIFY = 8, }; -// Flags for find_special_key() +/// Flags for find_special_key() enum { FSK_KEYCODE = 0x01, ///< prefer key code, e.g. K_DEL in place of DEL FSK_KEEP_X_KEY = 0x02, ///< don’t translate xHome to Home key diff --git a/src/nvim/log.c b/src/nvim/log.c index 57c7c4758b..99b17a612b 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -310,7 +310,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, const char *parent = path_tail(os_getenv(ENV_NVIM)); // Servername. Empty until starting=false. const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER)); - if (parent && parent[0] != NUL) { + if (parent[0] != NUL) { snprintf(name, sizeof(name), "%s/c", parent); // "/c" indicates child. } else if (serv[0] != NUL) { snprintf(name, sizeof(name), "%s", serv); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index ad03ebd1ed..17157ccdc2 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -15,6 +15,7 @@ #include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/cursor.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" @@ -416,9 +417,9 @@ static int nlua_wait(lua_State *lstate) LOOP_PROCESS_EVENTS_UNTIL(&main_loop, loop_events, (int)timeout, - is_function ? nlua_wait_condition(lstate, - &pcall_status, - &callback_result) : false || got_int); + got_int || (is_function ? nlua_wait_condition(lstate, + &pcall_status, + &callback_result) : false)); // Stop dummy timer time_watcher_stop(tw); @@ -1673,7 +1674,7 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "_ts_get_minimum_language_version"); } -int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***results) +int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char ***results) { lua_State *const lstate = global_lstate; int ret = OK; @@ -1685,7 +1686,7 @@ int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***resul lua_getfield(lstate, -1, "_expand_pat"); luaL_checktype(lstate, -1, LUA_TFUNCTION); - // [ vim, vim._on_key, buf ] + // [ vim, vim._expand_pat, buf ] lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); if (nlua_pcall(lstate, 1, 2) != 0) { @@ -1838,7 +1839,7 @@ void nlua_execute_on_key(int c) // [ vim ] lua_getglobal(lstate, "vim"); - // [ vim, vim._on_key] + // [ vim, vim._on_key ] lua_getfield(lstate, -1, "_on_key"); luaL_checktype(lstate, -1, LUA_TFUNCTION); diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 8fde85b163..6ba0056f48 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -232,7 +232,7 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int head_offset = mb_head_off((char_u *)s1, (char_u *)s1 + offset - 1); + int head_offset = utf_cp_head_off((char_u *)s1, (char_u *)s1 + offset - 1); lua_pushinteger(lstate, head_offset); return 1; } @@ -252,7 +252,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL if (offset < 0 || offset > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - int tail_offset = mb_tail_off(s1, s1 + offset - 1); + int tail_offset = utf_cp_tail_off(s1, s1 + offset - 1); lua_pushinteger(lstate, tail_offset); return 1; } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index b96193d199..f0d847e352 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -11,6 +11,7 @@ #include <lua.h> #include <lualib.h> #include <stdbool.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> @@ -208,31 +209,32 @@ int tslua_inspect_lang(lua_State *L) lua_createtable(L, 0, 2); // [retval] - size_t nsymbols = (size_t)ts_language_symbol_count(lang); + uint32_t nsymbols = ts_language_symbol_count(lang); + assert(nsymbols < INT_MAX); - lua_createtable(L, nsymbols - 1, 1); // [retval, symbols] - for (size_t i = 0; i < nsymbols; i++) { - TSSymbolType t = ts_language_symbol_type(lang, i); + lua_createtable(L, (int)(nsymbols - 1), 1); // [retval, symbols] + for (uint32_t i = 0; i < nsymbols; i++) { + TSSymbolType t = ts_language_symbol_type(lang, (TSSymbol)i); if (t == TSSymbolTypeAuxiliary) { // not used by the API continue; } lua_createtable(L, 2, 0); // [retval, symbols, elem] - lua_pushstring(L, ts_language_symbol_name(lang, i)); + lua_pushstring(L, ts_language_symbol_name(lang, (TSSymbol)i)); lua_rawseti(L, -2, 1); lua_pushboolean(L, t == TSSymbolTypeRegular); lua_rawseti(L, -2, 2); // [retval, symbols, elem] - lua_rawseti(L, -2, i); // [retval, symbols] + lua_rawseti(L, -2, (int)i); // [retval, symbols] } lua_setfield(L, -2, "symbols"); // [retval] - size_t nfields = (size_t)ts_language_field_count(lang); - lua_createtable(L, nfields, 1); // [retval, fields] + uint32_t nfields = ts_language_field_count(lang); + lua_createtable(L, (int)nfields, 1); // [retval, fields] // Field IDs go from 1 to nfields inclusive (extra index 0 maps to NULL) - for (size_t i = 1; i <= nfields; i++) { - lua_pushstring(L, ts_language_field_name_for_id(lang, i)); - lua_rawseti(L, -2, i); // [retval, fields] + for (uint32_t i = 1; i <= nfields; i++) { + lua_pushstring(L, ts_language_field_name_for_id(lang, (TSFieldId)i)); + lua_rawseti(L, -2, (int)i); // [retval, fields] } lua_setfield(L, -2, "fields"); // [retval] @@ -300,7 +302,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position *bytes_read = 0; return ""; } - char_u *line = ml_get_buf(bp, position.row + 1, false); + char *line = (char *)ml_get_buf(bp, (linenr_T)position.row + 1, false); size_t len = STRLEN(line); if (position.column > len) { *bytes_read = 0; @@ -322,9 +324,9 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position #undef BUFSIZE } -static void push_ranges(lua_State *L, const TSRange *ranges, const unsigned int length) +static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length) { - lua_createtable(L, length, 0); + lua_createtable(L, (int)length, 0); for (size_t i = 0; i < length; i++) { lua_createtable(L, 4, 0); lua_pushinteger(L, ranges[i].start_point.row); @@ -336,7 +338,7 @@ static void push_ranges(lua_State *L, const TSRange *ranges, const unsigned int lua_pushinteger(L, ranges[i].end_point.column); lua_rawseti(L, -2, 4); - lua_rawseti(L, -2, i + 1); + lua_rawseti(L, -2, (int)(i + 1)); } } @@ -365,15 +367,19 @@ static int parser_parse(lua_State *L) switch (lua_type(L, 3)) { case LUA_TSTRING: str = lua_tolstring(L, 3, &len); - new_tree = ts_parser_parse_string(*p, old_tree, str, len); + new_tree = ts_parser_parse_string(*p, old_tree, str, (uint32_t)len); break; case LUA_TNUMBER: bufnr = lua_tointeger(L, 3); - buf = handle_get_buffer(bufnr); + buf = handle_get_buffer((handle_T)bufnr); if (!buf) { - return luaL_error(L, "invalid buffer handle: %d", bufnr); +#define BUFSIZE 256 + char ebuf[BUFSIZE] = { 0 }; + vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %ld", bufnr); + return luaL_argerror(L, 3, ebuf); +#undef BUFSIZE } input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 }; @@ -382,7 +388,7 @@ static int parser_parse(lua_State *L) break; default: - return luaL_error(L, "invalid argument to parser:parse()"); + return luaL_argerror(L, 3, "expected either string or buffer handle"); } // Sometimes parsing fails (timeout, or wrong parser ABI) @@ -429,12 +435,12 @@ static int tree_edit(lua_State *L) return 0; } - long start_byte = lua_tointeger(L, 2); - long old_end_byte = lua_tointeger(L, 3); - long new_end_byte = lua_tointeger(L, 4); - TSPoint start_point = { lua_tointeger(L, 5), lua_tointeger(L, 6) }; - TSPoint old_end_point = { lua_tointeger(L, 7), lua_tointeger(L, 8) }; - TSPoint new_end_point = { lua_tointeger(L, 9), lua_tointeger(L, 10) }; + uint32_t start_byte = (uint32_t)luaL_checkint(L, 2); + uint32_t old_end_byte = (uint32_t)luaL_checkint(L, 3); + uint32_t new_end_byte = (uint32_t)luaL_checkint(L, 4); + TSPoint start_point = { (uint32_t)luaL_checkint(L, 5), (uint32_t)luaL_checkint(L, 6) }; + TSPoint old_end_point = { (uint32_t)luaL_checkint(L, 7), (uint32_t)luaL_checkint(L, 8) }; + TSPoint new_end_point = { (uint32_t)luaL_checkint(L, 9), (uint32_t)luaL_checkint(L, 10) }; TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, start_point, old_end_point, new_end_point }; @@ -456,29 +462,28 @@ static void range_from_lua(lua_State *L, TSRange *range) goto error; } - uint32_t start_row, start_col, start_byte, end_row, end_col, end_byte; lua_rawgeti(L, -1, 1); // [ range, start_row] - start_row = luaL_checkinteger(L, -1); + uint32_t start_row = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 2); // [ range, start_col] - start_col = luaL_checkinteger(L, -1); + uint32_t start_col = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 3); // [ range, start_byte] - start_byte = luaL_checkinteger(L, -1); + uint32_t start_byte = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 4); // [ range, end_row] - end_row = luaL_checkinteger(L, -1); + uint32_t end_row = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 5); // [ range, end_col] - end_col = luaL_checkinteger(L, -1); + uint32_t end_col = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 6); // [ range, end_byte] - end_byte = luaL_checkinteger(L, -1); + uint32_t end_byte = (uint32_t)luaL_checkinteger(L, -1); lua_pop(L, 1); // [ range ] *range = (TSRange) { @@ -531,13 +536,13 @@ static int parser_set_ranges(lua_State *L) // [ parser, ranges ] for (size_t index = 0; index < tbl_len; index++) { - lua_rawgeti(L, 2, index + 1); // [ parser, ranges, range ] + lua_rawgeti(L, 2, (int)index + 1); // [ parser, ranges, range ] range_from_lua(L, ranges + index); lua_pop(L, 1); } // This memcpies ranges, thus we can free it afterwards - ts_parser_set_included_ranges(*p, ranges, tbl_len); + ts_parser_set_included_ranges(*p, ranges, (uint32_t)tbl_len); xfree(ranges); return 0; @@ -550,7 +555,7 @@ static int parser_get_ranges(lua_State *L) return 0; } - unsigned int len; + uint32_t len; const TSRange *ranges = ts_parser_included_ranges(*p, &len); push_ranges(L, ranges, len); @@ -793,7 +798,7 @@ static int node_field(lua_State *L) TSTreeCursor cursor = ts_tree_cursor_new(node); lua_newtable(L); // [table] - unsigned int curr_index = 0; + size_t curr_index = 0; if (ts_tree_cursor_goto_first_child(&cursor)) { do { @@ -801,7 +806,7 @@ static int node_field(lua_State *L) if (current_field != NULL && !STRCMP(field_name, current_field)) { push_node(L, ts_tree_cursor_current_node(&cursor), 1); // [table, node] - lua_rawseti(L, -2, ++curr_index); + lua_rawseti(L, -2, (int)++curr_index); } } while (ts_tree_cursor_goto_next_sibling(&cursor)); } @@ -1036,7 +1041,7 @@ static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) { for (int i = 0; i < match->capture_count; i++) { push_node(L, match->captures[i].node, nodeidx); - lua_rawseti(L, -2, match->captures[i].index + 1); + lua_rawseti(L, -2, (int)match->captures[i].index + 1); } } @@ -1049,7 +1054,7 @@ static int query_next_match(lua_State *L) TSQueryMatch match; if (ts_query_cursor_next_match(cursor, &match)) { lua_pushinteger(L, match.pattern_index + 1); // [index] - lua_createtable(L, ts_query_capture_count(query), 2); // [index, match] + lua_createtable(L, (int)ts_query_capture_count(query), 2); // [index, match] set_match(L, &match, lua_upvalueindex(2)); return 2; } @@ -1070,7 +1075,7 @@ static int query_next_capture(lua_State *L) bool active = lua_toboolean(L, -1); lua_pop(L, 1); if (!active) { - ts_query_cursor_remove_match(cursor, ud->predicated_match); + ts_query_cursor_remove_match(cursor, (uint32_t)ud->predicated_match); } ud->predicated_match = -1; } @@ -1080,6 +1085,7 @@ static int query_next_capture(lua_State *L) if (ts_query_cursor_next_capture(cursor, &match, &capture_index)) { TSQueryCapture capture = match.captures[capture_index]; + // TODO(vigoux): handle capture quantifiers here lua_pushinteger(L, capture.index + 1); // [index] push_node(L, capture.node, lua_upvalueindex(2)); // [index, node] @@ -1088,7 +1094,7 @@ static int query_next_capture(lua_State *L) ts_query_predicates_for_pattern(query, match.pattern_index, &n_pred); if (n_pred > 0 && (ud->max_match_id < (int)match.id)) { - ud->max_match_id = match.id; + ud->max_match_id = (int)match.id; lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match] set_match(L, &match, lua_upvalueindex(2)); @@ -1096,7 +1102,7 @@ static int query_next_capture(lua_State *L) lua_setfield(L, -2, "pattern"); if (match.capture_count > 1) { - ud->predicated_match = match.id; + ud->predicated_match = (int)match.id; lua_pushboolean(L, false); lua_setfield(L, -2, "active"); } @@ -1131,10 +1137,9 @@ static int node_rawquery(lua_State *L) bool captures = lua_toboolean(L, 3); if (lua_gettop(L) >= 4) { - int start = luaL_checkinteger(L, 4); - int end = lua_gettop(L) >= 5 ? luaL_checkinteger(L, 5) : MAXLNUM; - ts_query_cursor_set_point_range(cursor, - (TSPoint){ start, 0 }, (TSPoint){ end, 0 }); + uint32_t start = (uint32_t)luaL_checkinteger(L, 4); + uint32_t end = lua_gettop(L) >= 5 ? (uint32_t)luaL_checkinteger(L, 5) : MAXLNUM; + ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 }); } TSLua_cursor *ud = lua_newuserdata(L, sizeof(*ud)); // [udata] @@ -1151,7 +1156,7 @@ static int node_rawquery(lua_State *L) if (captures) { // placeholder for match state - lua_createtable(L, ts_query_capture_count(query), 2); // [u, n, q, match] + lua_createtable(L, (int)ts_query_capture_count(query), 2); // [u, n, q, match] lua_pushcclosure(L, query_next_capture, 4); // [closure] } else { lua_pushcclosure(L, query_next_match, 3); // [closure] @@ -1187,7 +1192,7 @@ int tslua_parse_query(lua_State *L) uint32_t error_offset; TSQueryError error_type; - TSQuery *query = ts_query_new(lang, src, len, &error_offset, &error_type); + TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type); if (!query) { return luaL_error(L, "query: %s at position %d", @@ -1212,6 +1217,8 @@ static const char *query_err_string(TSQueryError err) return "invalid field"; case TSQueryErrorCapture: return "invalid capture"; + case TSQueryErrorStructure: + return "invalid structure"; default: return "error"; } @@ -1249,15 +1256,14 @@ static int query_inspect(lua_State *L) uint32_t n_pat = ts_query_pattern_count(query); lua_createtable(L, 0, 2); // [retval] - lua_createtable(L, n_pat, 1); // [retval, patterns] + lua_createtable(L, (int)n_pat, 1); // [retval, patterns] for (size_t i = 0; i < n_pat; i++) { uint32_t len; - const TSQueryPredicateStep *step = ts_query_predicates_for_pattern(query, - i, &len); + const TSQueryPredicateStep *step = ts_query_predicates_for_pattern(query, (uint32_t)i, &len); if (len == 0) { continue; } - lua_createtable(L, len/4, 1); // [retval, patterns, pat] + lua_createtable(L, (int)len/4, 1); // [retval, patterns, pat] lua_createtable(L, 3, 0); // [retval, patterns, pat, pred] int nextpred = 1; int nextitem = 1; @@ -1283,17 +1289,17 @@ static int query_inspect(lua_State *L) } // last predicate should have ended with TypeDone lua_pop(L, 1); // [retval, patters, pat] - lua_rawseti(L, -2, i + 1); // [retval, patterns] + lua_rawseti(L, -2, (int)i + 1); // [retval, patterns] } lua_setfield(L, -2, "patterns"); // [retval] uint32_t n_captures = ts_query_capture_count(query); - lua_createtable(L, n_captures, 0); // [retval, captures] + lua_createtable(L, (int)n_captures, 0); // [retval, captures] for (size_t i = 0; i < n_captures; i++) { uint32_t strlen; - const char *str = ts_query_capture_name_for_id(query, i, &strlen); + const char *str = ts_query_capture_name_for_id(query, (uint32_t)i, &strlen); lua_pushlstring(L, str, strlen); // [retval, captures, capture] - lua_rawseti(L, -2, i + 1); + lua_rawseti(L, -2, (int)i + 1); } lua_setfield(L, -2, "captures"); // [retval] diff --git a/src/nvim/main.c b/src/nvim/main.c index b06b9630e2..494ff0b4af 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -28,6 +28,7 @@ #include "nvim/highlight_group.h" #include "nvim/iconv.h" #include "nvim/if_cscope.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/main.h" #include "nvim/mapping.h" @@ -202,7 +203,6 @@ void early_init(mparm_T *paramp) set_lang_var(); // set v:lang and v:ctype init_signs(); - ui_comp_syn_init(); } #ifdef MAKE_LIB @@ -320,6 +320,7 @@ int main(int argc, char **argv) no_wait_return = true; init_highlight(true, false); // Default highlight groups. + ui_comp_syn_init(); TIME_MSG("init highlight"); // Set the break level after the terminal is initialized. diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 5a11ac686e..1797bb0365 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -235,7 +235,7 @@ static void showmap(mapblock_T *mp, bool local) /// @param[in] orig_lhs Original mapping LHS, with characters to replace. /// @param[in] orig_lhs_len `strlen` of orig_lhs. /// @param[in] orig_rhs Original mapping RHS, with characters to replace. -/// @param[in] rhs_lua Lua reference for Lua maps. +/// @param[in] rhs_lua Lua reference for Lua mappings. /// @param[in] orig_rhs_len `strlen` of orig_rhs. /// @param[in] cpo_flags See param docs for @ref replace_termcodes. /// @param[out] mapargs MapArguments struct holding the replaced strings. @@ -428,6 +428,66 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma return 0; } +/// @param args "rhs", "rhs_lua", "orig_rhs", "expr", "silent", "nowait", "replace_keycodes" and +/// and "desc" fields are used. +/// "rhs", "rhs_lua", "orig_rhs" fields are cleared if "simplified" is false. +/// @param sid -1 to use current_sctx +static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, const char_u *keys, + MapArguments *args, int noremap, int mode, bool is_abbr, scid_T sid, + linenr_T lnum, bool simplified) +{ + mapblock_T *mp = xcalloc(1, sizeof(mapblock_T)); + + // If CTRL-C has been mapped, don't always use it for Interrupting. + if (*keys == Ctrl_C) { + if (map_table == buf->b_maphash) { + buf->b_mapped_ctrl_c |= mode; + } else { + mapped_ctrl_c |= mode; + } + } + + mp->m_keys = vim_strsave(keys); + mp->m_str = args->rhs; + mp->m_orig_str = args->orig_rhs; + mp->m_luaref = args->rhs_lua; + if (!simplified) { + args->rhs = NULL; + args->orig_rhs = NULL; + args->rhs_lua = LUA_NOREF; + } + mp->m_keylen = (int)STRLEN(mp->m_keys); + mp->m_noremap = noremap; + mp->m_nowait = args->nowait; + mp->m_silent = args->silent; + mp->m_mode = mode; + mp->m_simplified = simplified; + mp->m_expr = args->expr; + mp->m_replace_keycodes = args->replace_keycodes; + if (sid >= 0) { + mp->m_script_ctx.sc_sid = sid; + mp->m_script_ctx.sc_lnum = lnum; + } else { + mp->m_script_ctx = current_sctx; + mp->m_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&mp->m_script_ctx); + } + mp->m_desc = NULL; + if (args->desc != NULL) { + mp->m_desc = xstrdup(args->desc); + } + + // add the new entry in front of the abbrlist or maphash[] list + if (is_abbr) { + mp->m_next = *abbr_table; + *abbr_table = mp; + } else { + const int n = MAP_HASH(mp->m_mode, mp->m_keys[0]); + mp->m_next = map_table[n]; + map_table[n] = mp; + } +} + /// Sets or removes a mapping or abbreviation in buffer `buf`. /// /// @param maptype @see do_map @@ -452,7 +512,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, abbr_table = &first_abbr; // For ":noremap" don't remap, otherwise do remap. - if (maptype == 2) { + if (maptype == MAPTYPE_NOREMAP) { noremap = REMAP_NONE; } else { noremap = REMAP_YES; @@ -470,10 +530,10 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, const bool has_lhs = (args->lhs[0] != NUL); const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop; - const bool do_print = !has_lhs || (maptype != 1 && !has_rhs); + const bool do_print = !has_lhs || (maptype != MAPTYPE_UNMAP && !has_rhs); // check for :unmap without argument - if (maptype == 1 && !has_lhs) { + if (maptype == MAPTYPE_UNMAP && !has_lhs) { retval = 1; goto theend; } @@ -507,13 +567,11 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, goto theend; } - if (is_abbrev && maptype != 1) { - // + if (is_abbrev && maptype != MAPTYPE_UNMAP) { // If an abbreviation ends in a keyword character, the // rest must be all keyword-char or all non-keyword-char. // Otherwise we won't be able to find the start of it in a // vi-compatible way. - // int same = -1; const int first = vim_iswordp(lhs); @@ -551,7 +609,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } // Check if a new local mapping wasn't already defined globally. - if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) { + if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs + && maptype != MAPTYPE_UNMAP) { // need to loop over all global hash lists for (int hash = 0; hash < 256 && !got_int; hash++) { if (is_abbrev) { @@ -581,7 +640,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } // When listing global mappings, also list buffer-local ones here. - if (map_table != buf->b_maphash && !has_rhs && maptype != 1) { + if (map_table != buf->b_maphash && !has_rhs && maptype != MAPTYPE_UNMAP) { // need to loop over all global hash lists for (int hash = 0; hash < 256 && !got_int; hash++) { if (is_abbrev) { @@ -616,7 +675,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, // entry with a matching 'to' part. This was done to allow ":ab foo bar" // to be unmapped by typing ":unab foo", where "foo" will be replaced by // "bar" because of the abbreviation. - for (int round = 0; (round == 0 || maptype == 1) && round <= 1 + for (int round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1 && !did_it && !got_int; round++) { int hash_start, hash_end; if (has_lhs || is_abbrev) { @@ -650,7 +709,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, p = mp->m_keys; } if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) { - if (maptype == 1) { + if (maptype == MAPTYPE_UNMAP) { // Delete entry. // Only accept a full match. For abbreviations // we ignore trailing space when matching with @@ -715,6 +774,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, mp->m_mode = mode; mp->m_simplified = keyround1_simplified; mp->m_expr = args->expr; + mp->m_replace_keycodes = args->replace_keycodes; mp->m_script_ctx = current_sctx; mp->m_script_ctx.sc_lnum += sourcing_lnum; nlua_set_sctx(&mp->m_script_ctx); @@ -745,7 +805,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } } - if (maptype == 1) { + if (maptype == MAPTYPE_UNMAP) { // delete entry if (!did_it) { if (!keyround1_simplified) { @@ -779,50 +839,10 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } // Get here when adding a new entry to the maphash[] list or abbrlist. - mp = xmalloc(sizeof(mapblock_T)); - - // If CTRL-C has been mapped, don't always use it for Interrupting. - if (*lhs == Ctrl_C) { - if (map_table == buf->b_maphash) { - buf->b_mapped_ctrl_c |= mode; - } else { - mapped_ctrl_c |= mode; - } - } - - mp->m_keys = vim_strsave(lhs); - mp->m_str = args->rhs; - mp->m_orig_str = args->orig_rhs; - mp->m_luaref = args->rhs_lua; - if (!keyround1_simplified) { - args->rhs = NULL; - args->orig_rhs = NULL; - args->rhs_lua = LUA_NOREF; - } - mp->m_keylen = (int)STRLEN(mp->m_keys); - mp->m_noremap = noremap; - mp->m_nowait = args->nowait; - mp->m_silent = args->silent; - mp->m_mode = mode; - mp->m_simplified = keyround1_simplified; // Notice this when porting patch 8.2.0807 - mp->m_expr = args->expr; - mp->m_script_ctx = current_sctx; - mp->m_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&mp->m_script_ctx); - mp->m_desc = NULL; - if (args->desc != NULL) { - mp->m_desc = xstrdup(args->desc); - } - - // add the new entry in front of the abbrlist or maphash[] list - if (is_abbrev) { - mp->m_next = *abbr_table; - *abbr_table = mp; - } else { - n = MAP_HASH(mp->m_mode, mp->m_keys[0]); - mp->m_next = map_table[n]; - map_table[n] = mp; - } + map_add(buf, map_table, abbr_table, lhs, args, noremap, mode, is_abbrev, + -1, // sid + 0, // lnum + keyround1_simplified); } theend: @@ -861,7 +881,9 @@ theend: /// for :cabbr mode is MODE_CMDLINE /// ``` /// -/// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|. +/// @param maptype MAPTYPE_MAP for |:map| +/// MAPTYPE_UNMAP for |:unmap| +/// MAPTYPE_NOREMAP for |noremap|. /// @param arg C-string containing the arguments of the map/abbrev /// command, i.e. everything except the initial `:[X][nore]map`. /// - Cannot be a read-only string; it will be modified. @@ -878,7 +900,7 @@ theend: int do_map(int maptype, char_u *arg, int mode, bool is_abbrev) { MapArguments parsed_args; - int result = str_to_mapargs(arg, maptype == 1, &parsed_args); + int result = str_to_mapargs(arg, maptype == MAPTYPE_UNMAP, &parsed_args); switch (result) { case 0: break; @@ -1230,7 +1252,7 @@ char_u *set_context_in_map_cmd(expand_T *xp, char_u *cmd, char_u *arg, bool forc /// Find all mapping/abbreviation names that match regexp "regmatch". /// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. /// @return OK if matches found, FAIL otherwise. -int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) +int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) { mapblock_T *mp; int hash; @@ -1270,7 +1292,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) if (round == 1) { count++; } else { - (*file)[count++] = vim_strsave(p); + (*file)[count++] = (char *)vim_strsave(p); } } } @@ -1293,7 +1315,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) if (round == 1) { count++; } else { - (*file)[count++] = p; + (*file)[count++] = (char *)p; p = NULL; } } @@ -1307,22 +1329,18 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) } if (round == 1) { - *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *)); + *file = xmalloc((size_t)count * sizeof(char_u *)); } } // for (round) if (count > 1) { - char_u **ptr1; - char_u **ptr2; - char_u **ptr3; - // Sort the matches sort_strings(*file, count); // Remove multiple entries - ptr1 = *file; - ptr2 = ptr1 + 1; - ptr3 = ptr1 + count; + char **ptr1 = *file; + char **ptr2 = ptr1 + 1; + char **ptr3 = ptr1 + count; while (ptr2 < ptr3) { if (STRCMP(*ptr1, *ptr2)) { @@ -1517,12 +1535,8 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) /// @param c NUL or typed character for abbreviation char_u *eval_map_expr(mapblock_T *mp, int c) { - char_u *res; char_u *p = NULL; char_u *expr = NULL; - pos_T save_cursor; - int save_msg_col; - int save_msg_row; // Remove escaping of K_SPECIAL, because "str" is in a format to be used as // typeahead. @@ -1536,9 +1550,9 @@ char_u *eval_map_expr(mapblock_T *mp, int c) textlock++; ex_normal_lock++; set_vim_var_char(c); // set v:char to the typed character - save_cursor = curwin->w_cursor; - save_msg_col = msg_col; - save_msg_row = msg_row; + const pos_T save_cursor = curwin->w_cursor; + const int save_msg_col = msg_col; + const int save_msg_row = msg_row; if (mp->m_luaref != LUA_NOREF) { Error err = ERROR_INIT; Array args = ARRAY_DICT_INIT; @@ -1564,8 +1578,15 @@ char_u *eval_map_expr(mapblock_T *mp, int c) if (p == NULL) { return NULL; } - // Escape K_SPECIAL in the result to be able to use the string as typeahead. - res = (char_u *)vim_strsave_escape_ks((char *)p); + + char_u *res = NULL; + + if (mp->m_replace_keycodes) { + replace_termcodes((char *)p, STRLEN(p), (char **)&res, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + } else { + // Escape K_SPECIAL in the result to be able to use the string as typeahead. + res = (char_u *)vim_strsave_escape_ks((char *)p); + } xfree(p); return res; @@ -1612,7 +1633,7 @@ int makemap(FILE *fd, buf_T *buf) continue; } - // skip lua mappings and mappings that contain a <SNR> (script-local thing), + // skip Lua mappings and mappings that contain a <SNR> (script-local thing), // they probably don't work when loaded again if (mp->m_luaref != LUA_NOREF) { continue; @@ -1973,9 +1994,9 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @param mp The maphash that contains the mapping information /// @param buffer_value The "buffer" value /// @param compatible True for compatible with old maparg() dict -static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value, - bool compatible) - FUNC_ATTR_NONNULL_ALL +static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, + const char *lhsrawalt, long buffer_value, bool compatible) + FUNC_ATTR_NONNULL_ARG(1, 2) { char *const lhs = str2special_save((const char *)mp->m_keys, compatible, !compatible); @@ -2007,6 +2028,11 @@ static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, l tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc)); } tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); + tv_dict_add_str(dict, S_LEN("lhsraw"), (const char *)mp->m_keys); + if (lhsrawalt != NULL) { + // Also add the value for the simplified entry. + tv_dict_add_str(dict, S_LEN("lhsrawalt"), lhsrawalt); + } tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("script"), mp->m_noremap == REMAP_SCRIPT ? 1 : 0); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); @@ -2015,23 +2041,14 @@ static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, l tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)mp->m_script_ctx.sc_lnum); tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value); tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); + if (mp->m_replace_keycodes) { + tv_dict_add_nr(dict, S_LEN("replace_keycodes"), 1); + } tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); } static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char *keys_buf = NULL; - char_u *alt_keys_buf = NULL; - bool did_simplify = false; - char_u *rhs; - LuaRef rhs_lua; - int mode; - bool abbr = false; - bool get_dict = false; - mapblock_T *mp; - int buffer_local; - int flags = REPTERM_FROM_PART | REPTERM_DO_LT; - // Return empty string for failure. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -2041,8 +2058,11 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) return; } - char buf[NUMBUFLEN]; const char *which; + char buf[NUMBUFLEN]; + bool abbr = false; + bool get_dict = false; + if (argvars[1].v_type != VAR_UNKNOWN) { which = tv_get_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { @@ -2058,13 +2078,19 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) return; } - mode = get_map_mode((char **)&which, 0); + char *keys_buf = NULL; + char_u *alt_keys_buf = NULL; + bool did_simplify = false; + const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; + const int mode = get_map_mode((char **)&which, 0); char_u *keys_simplified - = (char_u *)replace_termcodes(keys, - STRLEN(keys), &keys_buf, flags, &did_simplify, + = (char_u *)replace_termcodes(keys, STRLEN(keys), &keys_buf, flags, &did_simplify, CPO_TO_CPO_FLAGS); - rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); + mapblock_T *mp = NULL; + int buffer_local; + LuaRef rhs_lua; + char_u *rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); if (did_simplify) { // When the lhs is being simplified the not-simplified keys are // preferred for printing, like in do_map(). @@ -2093,7 +2119,8 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) tv_dict_alloc_ret(rettv); if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { // Return a dictionary. - mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true); + mapblock_fill_dict(rettv->vval.v_dict, mp, did_simplify ? (char *)keys_simplified : NULL, + buffer_local, true); } } @@ -2101,6 +2128,74 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) xfree(alt_keys_buf); } +/// "mapset()" function +void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char buf[NUMBUFLEN]; + const char *which = tv_get_string_buf_chk(&argvars[0], buf); + if (which == NULL) { + return; + } + const int mode = get_map_mode((char **)&which, 0); + const bool is_abbr = tv_get_number(&argvars[1]) != 0; + + if (argvars[2].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + dict_T *d = argvars[2].vval.v_dict; + + // Get the values in the same order as above in get_maparg(). + char *lhs = tv_dict_get_string(d, "lhs", false); + char *lhsraw = tv_dict_get_string(d, "lhsraw", false); + char *lhsrawalt = tv_dict_get_string(d, "lhsrawalt", false); + char *rhs = tv_dict_get_string(d, "rhs", false); + if (lhs == NULL || lhsraw == NULL || rhs == NULL) { + emsg(_("E460: entries missing in mapset() dict argument")); + return; + } + char *orig_rhs = rhs; + char *arg_buf = NULL; + rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + + int noremap = tv_dict_get_number(d, "noremap") ? REMAP_NONE : 0; + if (tv_dict_get_number(d, "script") != 0) { + noremap = REMAP_SCRIPT; + } + MapArguments args = { // TODO(zeertzjq): support restoring "callback"? + .rhs = (char_u *)rhs, + .rhs_lua = LUA_NOREF, + .orig_rhs = vim_strsave((char_u *)orig_rhs), + .expr = tv_dict_get_number(d, "expr") != 0, + .silent = tv_dict_get_number(d, "silent") != 0, + .nowait = tv_dict_get_number(d, "nowait") != 0, + .replace_keycodes = tv_dict_get_number(d, "replace_keycodes") != 0, + .desc = tv_dict_get_string(d, "desc", false), + }; + scid_T sid = (scid_T)tv_dict_get_number(d, "sid"); + linenr_T lnum = (linenr_T)tv_dict_get_number(d, "lnum"); + bool buffer = tv_dict_get_number(d, "buffer") != 0; + // mode from the dict is not used + + mapblock_T **map_table = buffer ? curbuf->b_maphash : maphash; + mapblock_T **abbr_table = buffer ? &curbuf->b_first_abbr : &first_abbr; + + // Delete any existing mapping for this lhs and mode. + MapArguments unmap_args = MAP_ARGUMENTS_INIT; + set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &unmap_args); + unmap_args.buffer = buffer; + buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, false, curbuf); + xfree(unmap_args.rhs); + xfree(unmap_args.orig_rhs); + + if (lhsrawalt != NULL) { + map_add(curbuf, map_table, abbr_table, (char_u *)lhsrawalt, &args, noremap, mode, is_abbr, + sid, lnum, true); + } + map_add(curbuf, map_table, abbr_table, (char_u *)lhsraw, &args, noremap, mode, is_abbr, + sid, lnum, false); +} + /// "maparg()" function void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -2123,11 +2218,11 @@ void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @param buffer If true, make a buffer-local mapping for curbuf void add_map(char *lhs, char *rhs, int mode, bool buffer) { - MapArguments args = { 0 }; + MapArguments args = MAP_ARGUMENTS_INIT; set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &args); args.buffer = buffer; - buf_do_map(2, &args, mode, false, curbuf); + buf_do_map(MAPTYPE_NOREMAP, &args, mode, false, curbuf); xfree(args.rhs); xfree(args.orig_rhs); } @@ -2307,7 +2402,8 @@ static void do_exmap(exarg_T *eap, int isabbrev) char *cmdp = eap->cmd; mode = get_map_mode(&cmdp, eap->forceit || isabbrev); - switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'), + switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP + : (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP, (char_u *)eap->arg, mode, isabbrev)) { case 1: emsg(_(e_invarg)); @@ -2396,10 +2492,16 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod KEY_TO_BOOL(script); KEY_TO_BOOL(expr); KEY_TO_BOOL(unique); + KEY_TO_BOOL(replace_keycodes); #undef KEY_TO_BOOL } parsed_args.buffer = !global; + if (parsed_args.replace_keycodes && !parsed_args.expr) { + api_set_error(err, kErrorTypeValidation, "\"replace_keycodes\" requires \"expr\""); + goto fail_and_free; + } + if (!set_maparg_lhs_rhs(lhs.data, lhs.size, rhs.data, rhs.size, lua_funcref, CPO_TO_CPO_FLAGS, &parsed_args)) { @@ -2461,11 +2563,11 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod } // buf_do_map() reads noremap/unmap as its own argument. - int maptype_val = 0; + int maptype_val = MAPTYPE_MAP; if (is_unmap) { - maptype_val = 1; + maptype_val = MAPTYPE_UNMAP; } else if (is_noremap) { - maptype_val = 2; + maptype_val = MAPTYPE_NOREMAP; } switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) { @@ -2499,7 +2601,7 @@ fail_and_free: /// /// @param mode The abbreviation for the mode /// @param buf The buffer to get the mapping array. NULL for global -/// @param from_lua Whether it is called from internal lua api. +/// @param from_lua Whether it is called from internal Lua api. /// @returns Array of maparg()-like dictionaries describing mappings ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) { @@ -2523,7 +2625,7 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) } // Check for correct mode if (int_mode & current_maphash->m_mode) { - mapblock_fill_dict(dict, current_maphash, buffer_value, false); + mapblock_fill_dict(dict, current_maphash, NULL, buffer_value, false); Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT, .vval.v_dict = dict } }); if (from_lua) { diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h index 4b0622ffa1..7c48c3bce2 100644 --- a/src/nvim/mapping.h +++ b/src/nvim/mapping.h @@ -2,7 +2,6 @@ #define NVIM_MAPPING_H #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" #include "nvim/vim.h" @@ -22,6 +21,7 @@ struct map_arguments { bool script; bool silent; bool unique; + bool replace_keycodes; /// The {lhs} of the mapping. /// @@ -45,9 +45,14 @@ struct map_arguments { char *desc; /// map description }; typedef struct map_arguments MapArguments; -#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \ +#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, false, \ { 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL } +// Used for the first argument of do_map() +#define MAPTYPE_MAP 0 +#define MAPTYPE_UNMAP 1 +#define MAPTYPE_NOREMAP 2 + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mapping.h.generated.h" #endif diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 66855c66b5..1fe3327b29 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -339,11 +339,11 @@ fmark_T *mark_get(buf_T *buf, win_T *win, fmark_T *fmp, MarkGet flag, int name) fmark_T *fm = NULL; if (ASCII_ISUPPER(name) || ascii_isdigit(name)) { // Global marks - xfmark_T *xfm = mark_get_global(!(flag & kMarkAllNoResolve), name); + xfmark_T *xfm = mark_get_global(flag != kMarkAllNoResolve, name); fm = &xfm->fmark; - // Only wanted marks belonging to the buffer - if ((flag & kMarkBufLocal) && xfm->fmark.fnum != buf->handle) { - return NULL; + if (flag == kMarkBufLocal && xfm->fmark.fnum != buf->handle) { + // Only wanted marks belonging to the buffer + return pos_to_mark(buf, NULL, (pos_T){ .lnum = 0 }); } } else if (name > 0 && name < NMARK_LOCAL_MAX) { // Local Marks @@ -491,7 +491,7 @@ fmark_T *mark_get_visual(buf_T *buf, int name) mark = pos_to_mark(buf, NULL, endp); } - if (mark != NULL && buf->b_visual.vi_mode == 'V') { + if (buf->b_visual.vi_mode == 'V') { if (name == '<') { mark->mark.col = 0; } else { @@ -508,11 +508,12 @@ fmark_T *mark_get_visual(buf_T *buf, int name) /// Pass an fmp if multiple c /// @note view fields are set to 0. /// @param buf for fmark->fnum. -/// @param pos for fmrak->mark. +/// @param pos for fmark->mark. /// @param fmp pointer to save the mark. /// /// @return[static] Mark with the given information. fmark_T *pos_to_mark(buf_T *buf, fmark_T *fmp, pos_T pos) + FUNC_ATTR_NONNULL_RET { static fmark_T fms = INIT_FMARK; fmark_T *fm = fmp == NULL ? &fms : fmp; diff --git a/src/nvim/match.c b/src/nvim/match.c index e17a95569c..8c72b13bc2 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -7,6 +7,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/eval/funcs.h" #include "nvim/fold.h" #include "nvim/highlight_group.h" #include "nvim/match.h" diff --git a/src/nvim/match.h b/src/nvim/match.h index fdcec0ae05..22a848bfdf 100644 --- a/src/nvim/match.h +++ b/src/nvim/match.h @@ -2,7 +2,6 @@ #define NVIM_MATCH_H #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index a9792cf1b9..223b4d6845 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -551,7 +551,7 @@ size_t mb_string2cells(const char *str) size_t clen = 0; for (const char_u *p = (char_u *)str; *p != NUL; p += utfc_ptr2len((char *)p)) { - clen += utf_ptr2cells((char *)p); + clen += (size_t)utf_ptr2cells((char *)p); } return clen; @@ -569,8 +569,8 @@ size_t mb_string2cells_len(const char *str, size_t size) size_t clen = 0; for (const char_u *p = (char_u *)str; *p != NUL && p < (char_u *)str + size; - p += utfc_ptr2len_len(p, size + (p - (char_u *)str))) { - clen += utf_ptr2cells((char *)p); + p += utfc_ptr2len_len(p, (int)size + (int)(p - (char_u *)str))) { + clen += (size_t)utf_ptr2cells((char *)p); } return clen; @@ -994,37 +994,37 @@ int utf_char2len(const int c) int utf_char2bytes(const int c, char *const buf) { if (c < 0x80) { // 7 bits - buf[0] = c; + buf[0] = (char)c; return 1; } else if (c < 0x800) { // 11 bits - buf[0] = 0xc0 + ((unsigned)c >> 6); - buf[1] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xc0 + ((unsigned)c >> 6)); + buf[1] = (char)(0x80 + ((unsigned)c & 0x3f)); return 2; } else if (c < 0x10000) { // 16 bits - buf[0] = 0xe0 + ((unsigned)c >> 12); - buf[1] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[2] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xe0 + ((unsigned)c >> 12)); + buf[1] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[2] = (char)(0x80 + ((unsigned)c & 0x3f)); return 3; } else if (c < 0x200000) { // 21 bits - buf[0] = 0xf0 + ((unsigned)c >> 18); - buf[1] = 0x80 + (((unsigned)c >> 12) & 0x3f); - buf[2] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[3] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xf0 + ((unsigned)c >> 18)); + buf[1] = (char)(0x80 + (((unsigned)c >> 12) & 0x3f)); + buf[2] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[3] = (char)(0x80 + ((unsigned)c & 0x3f)); return 4; } else if (c < 0x4000000) { // 26 bits - buf[0] = 0xf8 + ((unsigned)c >> 24); - buf[1] = 0x80 + (((unsigned)c >> 18) & 0x3f); - buf[2] = 0x80 + (((unsigned)c >> 12) & 0x3f); - buf[3] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[4] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xf8 + ((unsigned)c >> 24)); + buf[1] = (char)(0x80 + (((unsigned)c >> 18) & 0x3f)); + buf[2] = (char)(0x80 + (((unsigned)c >> 12) & 0x3f)); + buf[3] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[4] = (char)(0x80 + ((unsigned)c & 0x3f)); return 5; } else { // 31 bits - buf[0] = 0xfc + ((unsigned)c >> 30); - buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f); - buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f); - buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f); - buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[5] = 0x80 + (c & 0x3f); + buf[0] = (char)(0xfc + ((unsigned)c >> 30)); + buf[1] = (char)(0x80 + (((unsigned)c >> 24) & 0x3f)); + buf[2] = (char)(0x80 + (((unsigned)c >> 18) & 0x3f)); + buf[3] = (char)(0x80 + (((unsigned)c >> 12) & 0x3f)); + buf[4] = (char)(0x80 + (((unsigned)c >> 6) & 0x3f)); + buf[5] = (char)(0x80 + ((unsigned)c & 0x3f)); return 6; } } @@ -1251,7 +1251,7 @@ int mb_toupper(int a) #if defined(__STDC_ISO_10646__) // If towupper() is available and handles Unicode, use it. if (!(cmp_flags & CMP_INTERNAL)) { - return towupper(a); + return (int)towupper((wint_t)a); } #endif @@ -1282,7 +1282,7 @@ int mb_tolower(int a) #if defined(__STDC_ISO_10646__) // If towlower() is available and handles Unicode, use it. if (!(cmp_flags & CMP_INTERNAL)) { - return towlower(a); + return (int)towlower((wint_t)a); } #endif @@ -1347,10 +1347,10 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2 // to fold just one character to determine the result of comparison. if (c1 != -1 && c2 == -1) { - n1 = utf_char2bytes(utf_fold(c1), (char *)buffer); + n1 = (size_t)utf_char2bytes(utf_fold(c1), (char *)buffer); s1 = (char_u *)buffer; } else if (c2 != -1 && c1 == -1) { - n2 = utf_char2bytes(utf_fold(c2), (char *)buffer); + n2 = (size_t)utf_char2bytes(utf_fold(c2), (char *)buffer); s2 = (char_u *)buffer; } @@ -1487,7 +1487,7 @@ void mb_utflen(const char_u *s, size_t len, size_t *codepoints, size_t *codeunit size_t count = 0, extra = 0; size_t clen; for (size_t i = 0; i < len && s[i] != NUL; i += clen) { - clen = utf_ptr2len_len(s + i, len - i); + clen = (size_t)utf_ptr2len_len(s + i, (int)(len - i)); // NB: gets the byte value of invalid sequence bytes. // we only care whether the char fits in the BMP or not int c = (clen > 1) ? utf_ptr2char((char *)s + i) : s[i]; @@ -1509,7 +1509,7 @@ ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool us return 0; } for (i = 0; i < len && s[i] != NUL; i += clen) { - clen = utf_ptr2len_len(s + i, len - i); + clen = (size_t)utf_ptr2len_len(s + i, (int)(len - i)); // NB: gets the byte value of invalid sequence bytes. // we only care whether the char fits in the BMP or not int c = (clen > 1) ? utf_ptr2char((char *)s + i) : s[i]; @@ -1518,7 +1518,7 @@ ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool us count++; } if (count >= index) { - return i + clen; + return (ssize_t)(i + clen); } } return -1; @@ -1837,10 +1837,10 @@ int mb_off_next(const char_u *base, const char_u *p) return i; } -/// Return the offset from "p" to the last byte of the character it points -/// into. Can start anywhere in a stream of bytes. -/// Composing characters are not included. -int mb_tail_off(const char *base, const char *p_in) +/// Return the offset from `p_in` to the last byte of the codepoint it points +/// to. Can start anywhere in a stream of bytes. +/// Note: Counts individual codepoints of composed characters separately. +int utf_cp_tail_off(const char *base, const char *p_in) { const uint8_t *p = (uint8_t *)p_in; int i; @@ -1866,15 +1866,16 @@ int mb_tail_off(const char *base, const char *p_in) return i; } -/// Return the offset from "p" to the first byte of the character it points -/// into. Can start anywhere in a stream of bytes. -/// Unlike utf_head_off() this doesn't include composing characters and returns a negative value. +/// Return the offset from "p" to the first byte of the codepoint it points +/// to. Can start anywhere in a stream of bytes. +/// Note: Unlike `utf_head_off`, this counts individual codepoints of composed characters +/// separately and returns a negative offset. /// /// @param[in] base Pointer to start of string /// @param[in] p Pointer to byte for which to return the offset to the previous codepoint // /// @return 0 if invalid sequence, else offset to previous codepoint -int mb_head_off(const char_u *base, const char_u *p) +int utf_cp_head_off(const char_u *base, const char_u *p) { int i; int j; @@ -2165,7 +2166,7 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET if (*s == '_') { *p++ = '-'; } else { - *p++ = TOLOWER_ASC(*s); + *p++ = (char_u)TOLOWER_ASC(*s); } } *p = NUL; @@ -2269,8 +2270,8 @@ char_u *enc_locale(void) && !isalnum((int)p[4]) && p[4] != '-' && p[-3] == '_') { // Copy "XY.EUC" to "euc-XY" to buf[10]. memmove(buf, "euc-", 4); - buf[4] = (ASCII_ISALNUM(p[-2]) ? TOLOWER_ASC(p[-2]) : 0); - buf[5] = (ASCII_ISALNUM(p[-1]) ? TOLOWER_ASC(p[-1]) : 0); + buf[4] = (char)(ASCII_ISALNUM(p[-2]) ? TOLOWER_ASC(p[-2]) : 0); + buf[5] = (char)(ASCII_ISALNUM(p[-1]) ? TOLOWER_ASC(p[-1]) : 0); buf[6] = NUL; } else { s = p + 1; @@ -2282,7 +2283,7 @@ enc_locale_copy_enc: if (s[i] == '_' || s[i] == '-') { buf[i] = '-'; } else if (ASCII_ISALNUM((uint8_t)s[i])) { - buf[i] = TOLOWER_ASC(s[i]); + buf[i] = (char)TOLOWER_ASC(s[i]); } else { break; } @@ -2406,14 +2407,14 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen } l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); from += l; - fromlen -= l; + fromlen -= (size_t)l; } else if (ICONV_ERRNO != ICONV_E2BIG) { // conversion failed XFREE_CLEAR(result); break; } // Not enough room or skipping illegal sequence. - done = to - (char *)result; + done = (size_t)(to - (char *)result); } if (resultlenp != NULL && result != NULL) { @@ -2550,10 +2551,10 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp for (size_t i = 0; i < len; ++i) { c = ptr[i]; if (c < 0x80) { - *d++ = c; + *d++ = (char_u)c; } else { - *d++ = 0xc0 + ((unsigned)c >> 6); - *d++ = 0x80 + (c & 0x3f); + *d++ = (char_u)(0xc0 + (char_u)((unsigned)c >> 6)); + *d++ = (char_u)(0x80 + (c & 0x3f)); } } *d = NUL; @@ -2597,8 +2598,8 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp case CONV_TO_LATIN9: // utf-8 to latin9 conversion retval = xmalloc(len + 1); d = retval; - for (size_t i = 0; i < len; ++i) { - l = utf_ptr2len_len(ptr + i, len - i); + for (size_t i = 0; i < len; i++) { + l = utf_ptr2len_len(ptr + i, (int)(len - i)); if (l == 0) { *d++ = NUL; } else if (l == 1) { @@ -2648,7 +2649,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp } if (!utf_iscomposing(c)) { // skip composing chars if (c < 0x100) { - *d++ = c; + *d++ = (char_u)c; } else if (vcp->vc_fail) { xfree(retval); return NULL; @@ -2659,7 +2660,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp } } } - i += l - 1; + i += (size_t)l - 1; } } *d = NUL; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 5f74440747..fa3a400a68 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -309,7 +309,7 @@ int ml_open(buf_T *buf) if (!buf->b_spell) { b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; - b0p->b0_flags = get_fileformat(buf) + 1; + b0p->b0_flags = (uint8_t)(get_fileformat(buf) + 1); set_b0_fname(b0p, buf); (void)os_get_username((char *)b0p->b0_uname, B0_UNAME_SIZE); b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL; @@ -359,7 +359,7 @@ int ml_open(buf_T *buf) dp = hp->bh_data; dp->db_index[0] = --dp->db_txt_start; // at end of block - dp->db_free -= 1 + INDEX_SIZE; + dp->db_free -= 1 + (unsigned)INDEX_SIZE; dp->db_line_count = 1; *((char_u *)dp + dp->db_txt_start) = NUL; // empty line @@ -711,7 +711,7 @@ static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) if (same_directory(buf->b_ml.ml_mfp->mf_fname, (char_u *)buf->b_ffname)) { b0p->b0_flags |= B0_SAME_DIR; } else { - b0p->b0_flags &= ~B0_SAME_DIR; + b0p->b0_flags &= (uint8_t) ~B0_SAME_DIR; } } @@ -723,7 +723,7 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf) n = (int)STRLEN(buf->b_p_fenc); if ((int)STRLEN(b0p->b0_fname) + n + 1 > size) { - b0p->b0_flags &= ~B0_HAS_FENC; + b0p->b0_flags &= (uint8_t) ~B0_HAS_FENC; } else { memmove((char *)b0p->b0_fname + size - n, (char *)buf->b_p_fenc, (size_t)n); @@ -750,7 +750,6 @@ void ml_recover(bool checkext) DATA_BL *dp; infoptr_T *ip; blocknr_T bnum; - int page_count; int len; bool directly; linenr_T lnum; @@ -971,7 +970,7 @@ void ml_recover(bool checkext) int fnsize = B0_FNAME_SIZE_NOCRYPT; for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {} - b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p); + b0_fenc = vim_strnsave(p, (size_t)(b0p->b0_fname + fnsize - p)); } mf_put(mfp, hp, false, false); // release block 0 @@ -1004,11 +1003,11 @@ void ml_recover(bool checkext) } unchanged(curbuf, true, true); - bnum = 1; // start with block 1 - page_count = 1; // which is 1 page - lnum = 0; // append after line 0 in curbuf + bnum = 1; // start with block 1 + unsigned page_count = 1; // which is 1 page + lnum = 0; // append after line 0 in curbuf line_count = 0; - idx = 0; // start with first index in block 1 + idx = 0; // start with first index in block 1 error = 0; buf->b_ml.ml_stack_top = 0; // -V1048 buf->b_ml.ml_stack = NULL; @@ -1091,7 +1090,7 @@ void ml_recover(bool checkext) bnum = pp->pb_pointer[idx].pe_bnum; line_count = pp->pb_pointer[idx].pe_line_count; - page_count = pp->pb_pointer[idx].pe_page_count; + page_count = (unsigned)pp->pb_pointer[idx].pe_page_count; idx = 0; continue; } @@ -1267,12 +1266,12 @@ theend: int recover_names(char_u *fname, int list, int nr, char_u **fname_out) { int num_names; - char_u *(names[6]); + char *(names[6]); char_u *tail; char_u *p; int num_files; int file_count = 0; - char_u **files; + char **files; char_u *dirp; char_u *dir_name; char_u *fname_res = NULL; @@ -1309,22 +1308,22 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir if (fname == NULL) { - names[0] = vim_strsave((char_u *)"*.sw?"); + names[0] = xstrdup("*.sw?"); // For Unix names starting with a dot are special. MS-Windows // supports this too, on some file systems. - names[1] = vim_strsave((char_u *)".*.sw?"); - names[2] = vim_strsave((char_u *)".sw?"); + names[1] = xstrdup(".*.sw?"); + names[2] = xstrdup(".sw?"); num_names = 3; } else { num_names = recov_file_names(names, fname_res, TRUE); } } else { // check directory dir_name if (fname == NULL) { - names[0] = (char_u *)concat_fnames((char *)dir_name, "*.sw?", true); + names[0] = concat_fnames((char *)dir_name, "*.sw?", true); // For Unix names starting with a dot are special. MS-Windows // supports this too, on some file systems. - names[1] = (char_u *)concat_fnames((char *)dir_name, ".*.sw?", true); - names[2] = (char_u *)concat_fnames((char *)dir_name, ".sw?", true); + names[1] = concat_fnames((char *)dir_name, ".*.sw?", true); + names[2] = concat_fnames((char *)dir_name, ".sw?", true); num_names = 3; } else { int len = (int)STRLEN(dir_name); @@ -1361,7 +1360,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) if (swapname != NULL) { if (os_path_exists(swapname)) { files = xmalloc(sizeof(char_u *)); - files[0] = swapname; + files[0] = (char *)swapname; swapname = NULL; num_files = 1; } @@ -1377,7 +1376,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) for (int i = 0; i < num_files; i++) { // Do not expand wildcards, on Windows would try to expand // "%tmp%" in "%tmp%file" - if (path_full_compare((char *)p, (char *)files[i], true, false) & kEqualFiles) { + if (path_full_compare((char *)p, files[i], true, false) & kEqualFiles) { // Remove the name from files[i]. Move further entries // down. When the array becomes empty free it here, since // FreeWild() won't be called below. @@ -1395,7 +1394,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) if (nr > 0) { file_count += num_files; if (nr <= file_count) { - *fname_out = vim_strsave(files[nr - 1 + num_files - file_count]); + *fname_out = vim_strsave((char_u *)files[nr - 1 + num_files - file_count]); dirp = (char_u *)""; // stop searching } } else if (list) { @@ -1416,9 +1415,9 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) // print the swap file name msg_outnum((long)++file_count); msg_puts(". "); - msg_puts((const char *)path_tail((char *)files[i])); + msg_puts((const char *)path_tail(files[i])); msg_putchar('\n'); - (void)swapfile_info(files[i]); + (void)swapfile_info((char_u *)files[i]); } } else { msg_puts(_(" -- none --\n")); @@ -1518,7 +1517,7 @@ static time_t swapfile_info(char_u *fname) if (os_fileinfo((char *)fname, &file_info)) { #ifdef UNIX // print name of owner of the file - if (os_get_uname(file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) { + if (os_get_uname((uv_uid_t)file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) { msg_puts(_(" owned by: ")); msg_outtrans(uname); msg_puts(_(" dated: ")); @@ -1637,7 +1636,7 @@ static bool swapfile_unchanged(char *fname) return ret; } -static int recov_file_names(char_u **names, char_u *path, int prepend_dot) +static int recov_file_names(char **names, char_u *path, int prepend_dot) FUNC_ATTR_NONNULL_ALL { int num_names = 0; @@ -1645,7 +1644,7 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) // May also add the file name with a dot prepended, for swap file in same // dir as original file. if (prepend_dot) { - names[num_names] = (char_u *)modname((char *)path, ".sw?", true); + names[num_names] = modname((char *)path, ".sw?", true); if (names[num_names] == NULL) { return num_names; } @@ -1653,9 +1652,9 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) } // Form the normal swap file name pattern by appending ".sw?". - names[num_names] = (char_u *)concat_fnames((char *)path, ".sw?", FALSE); + names[num_names] = concat_fnames((char *)path, ".sw?", false); if (num_names >= 1) { // check if we have the same name twice - char_u *p = names[num_names - 1]; + char_u *p = (char_u *)names[num_names - 1]; int i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); if (i > 0) { p += i; // file name has been expanded to full path @@ -1970,12 +1969,9 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b int line_count; // number of indexes in current block int offset; int from, to; - int space_needed; // space needed for new line - int page_size; int page_count; int db_idx; // index for lnum in data block bhdr_T *hp; - memfile_T *mfp; DATA_BL *dp; PTR_BL *pp; infoptr_T *ip; @@ -1992,10 +1988,10 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b if (len == 0) { len = (colnr_T)STRLEN(line) + 1; // space needed for the text } - space_needed = len + INDEX_SIZE; // space needed for text + index + int space_needed = len + (int)INDEX_SIZE; // space needed for text + index - mfp = buf->b_ml.ml_mfp; - page_size = mfp->mf_page_size; + memfile_T *mfp = buf->b_ml.ml_mfp; + int page_size = (int)mfp->mf_page_size; /* * find the data block containing the previous line @@ -2053,9 +2049,9 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b /* * Insert new line in existing data block, or in data block allocated above. */ - dp->db_txt_start -= len; - dp->db_free -= space_needed; - ++(dp->db_line_count); + dp->db_txt_start -= (unsigned)len; + dp->db_free -= (unsigned)space_needed; + dp->db_line_count++; /* * move the text of the lines that follow to the front @@ -2067,17 +2063,17 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * This will become the character just after the new line. */ if (db_idx < 0) { - offset = dp->db_txt_end; + offset = (int)dp->db_txt_end; } else { offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK); } memmove((char *)dp + dp->db_txt_start, (char *)dp + dp->db_txt_start + len, - (size_t)(offset - (dp->db_txt_start + len))); - for (i = line_count - 1; i > db_idx; --i) { - dp->db_index[i + 1] = dp->db_index[i] - len; + (size_t)offset - (dp->db_txt_start + (size_t)len)); + for (i = line_count - 1; i > db_idx; i--) { + dp->db_index[i + 1] = dp->db_index[i] - (unsigned)len; } - dp->db_index[db_idx + 1] = offset - len; + dp->db_index[db_idx + 1] = (unsigned)(offset - len); } else { // add line at the end dp->db_index[db_idx + 1] = dp->db_txt_start; } @@ -2107,7 +2103,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * The line counts in the pointer blocks have already been adjusted by * ml_find_line(). */ - long line_count_left, line_count_right; + int line_count_left, line_count_right; int page_count_left, page_count_right; bhdr_T *hp_left; bhdr_T *hp_right; @@ -2142,9 +2138,9 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b in_left = false; // put new line in right block // space_needed does not change } else { - data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) - - dp->db_txt_start; - total_moved = data_moved + lines_moved * INDEX_SIZE; + data_moved = (int)(((dp->db_index[db_idx]) & DB_INDEX_MASK) - + dp->db_txt_start); + total_moved = data_moved + lines_moved * (int)INDEX_SIZE; if ((int)dp->db_free + total_moved >= space_needed) { in_left = true; // put new line in left block space_needed = total_moved; @@ -2155,7 +2151,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b } } - page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size; + page_count = ((space_needed + (int)HEADER_SIZE) + page_size - 1) / page_size; hp_new = ml_new_data(mfp, newfile, page_count); if (db_idx < 0) { // left block is new hp_left = hp_new; @@ -2172,15 +2168,15 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b dp_left = hp_left->bh_data; bnum_left = hp_left->bh_bnum; bnum_right = hp_right->bh_bnum; - page_count_left = hp_left->bh_page_count; - page_count_right = hp_right->bh_page_count; + page_count_left = (int)hp_left->bh_page_count; + page_count_right = (int)hp_right->bh_page_count; /* * May move the new line into the right/new block. */ if (!in_left) { - dp_right->db_txt_start -= len; - dp_right->db_free -= len + INDEX_SIZE; + dp_right->db_txt_start -= (unsigned)len; + dp_right->db_free -= (unsigned)len + (unsigned)INDEX_SIZE; dp_right->db_index[0] = dp_right->db_txt_start; if (mark) { dp_right->db_index[0] |= DB_MARKED; @@ -2196,21 +2192,21 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b if (lines_moved) { /* */ - dp_right->db_txt_start -= data_moved; - dp_right->db_free -= total_moved; + dp_right->db_txt_start -= (unsigned)data_moved; + dp_right->db_free -= (unsigned)total_moved; memmove((char *)dp_right + dp_right->db_txt_start, (char *)dp_left + dp_left->db_txt_start, (size_t)data_moved); - offset = dp_right->db_txt_start - dp_left->db_txt_start; - dp_left->db_txt_start += data_moved; - dp_left->db_free += total_moved; + offset = (int)(dp_right->db_txt_start - dp_left->db_txt_start); + dp_left->db_txt_start += (unsigned)data_moved; + dp_left->db_free += (unsigned)total_moved; /* * update indexes in the new block */ for (to = line_count_right, from = db_idx + 1; - from < line_count_left; ++from, ++to) { - dp_right->db_index[to] = dp->db_index[from] + offset; + from < line_count_left; from++, to++) { + dp_right->db_index[to] = dp->db_index[from] + (unsigned)offset; } line_count_right += lines_moved; line_count_left -= lines_moved; @@ -2220,8 +2216,8 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b * May move the new line into the left (old or new) block. */ if (in_left) { - dp_left->db_txt_start -= len; - dp_left->db_free -= len + INDEX_SIZE; + dp_left->db_txt_start -= (unsigned)len; + dp_left->db_free -= (unsigned)len + (unsigned)INDEX_SIZE; dp_left->db_index[line_count_left] = dp_left->db_txt_start; if (mark) { dp_left->db_index[line_count_left] |= DB_MARKED; @@ -2369,8 +2365,8 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b memmove(&pp_new->pb_pointer[0], &pp->pb_pointer[pb_idx + 1], (size_t)(total_moved) * sizeof(PTR_EN)); - pp_new->pb_count = total_moved; - pp->pb_count -= total_moved - 1; + pp_new->pb_count = (uint16_t)total_moved; + pp->pb_count = (uint16_t)(pp->pb_count - (total_moved - 1)); pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; @@ -2436,12 +2432,12 @@ void ml_add_deleted_len_buf(buf_T *buf, char_u *ptr, ssize_t len) return; } if (len == -1) { - len = STRLEN(ptr); + len = (ssize_t)STRLEN(ptr); } - curbuf->deleted_bytes += len + 1; - curbuf->deleted_bytes2 += len + 1; + curbuf->deleted_bytes += (size_t)len + 1; + curbuf->deleted_bytes2 += (size_t)len + 1; if (curbuf->update_need_codepoints) { - mb_utflen(ptr, len, &curbuf->deleted_codepoints, + mb_utflen(ptr, (size_t)len, &curbuf->deleted_codepoints, &curbuf->deleted_codeunits); curbuf->deleted_codepoints++; // NL char curbuf->deleted_codeunits++; @@ -2525,7 +2521,6 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) int count; // number of entries in block int idx; int stack_idx; - int text_start; int line_start; long line_size; int i; @@ -2568,17 +2563,16 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) dp = hp->bh_data; // compute line count before the delete - count = (long)(buf->b_ml.ml_locked_high) - - (long)(buf->b_ml.ml_locked_low) + 2; + count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 2; idx = lnum - buf->b_ml.ml_locked_low; --buf->b_ml.ml_line_count; line_start = ((dp->db_index[idx]) & DB_INDEX_MASK); if (idx == 0) { // first line in block, text at the end - line_size = dp->db_txt_end - line_start; + line_size = dp->db_txt_end - (unsigned)line_start; } else { - line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start; + line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - (unsigned)line_start; } // Line should always have an NL char internally (represented as NUL), @@ -2636,24 +2630,20 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) } CHECK(stack_idx < 0, _("deleted block 1?")); } else { - /* - * delete the text by moving the next lines forwards - */ - text_start = dp->db_txt_start; + // delete the text by moving the next lines forwards + int text_start = (int)dp->db_txt_start; memmove((char *)dp + text_start + line_size, (char *)dp + text_start, (size_t)(line_start - text_start)); - /* - * delete the index by moving the next indexes backwards - * Adjust the indexes for the text movement. - */ - for (i = idx; i < count - 1; ++i) { - dp->db_index[i] = dp->db_index[i + 1] + line_size; + // delete the index by moving the next indexes backwards + // Adjust the indexes for the text movement. + for (i = idx; i < count - 1; i++) { + dp->db_index[i] = dp->db_index[i + 1] + (unsigned)line_size; } - dp->db_free += line_size + INDEX_SIZE; - dp->db_txt_start += line_size; - --(dp->db_line_count); + dp->db_free += (unsigned)line_size + (unsigned)INDEX_SIZE; + dp->db_txt_start += (unsigned)line_size; + dp->db_line_count--; /* * mark the block dirty and make sure it is in the file (for recovery) @@ -2823,9 +2813,9 @@ static void ml_flush_line(buf_T *buf) start = ((dp->db_index[idx]) & DB_INDEX_MASK); old_line = (char_u *)dp + start; if (idx == 0) { // line is last in block - old_len = dp->db_txt_end - start; + old_len = (int)dp->db_txt_end - start; } else { // text of previous line follows - old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start; + old_len = (int)(dp->db_index[idx - 1] & DB_INDEX_MASK) - start; } new_len = (colnr_T)STRLEN(new_line) + 1; extra = new_len - old_len; // negative if lines gets smaller @@ -2840,18 +2830,18 @@ static void ml_flush_line(buf_T *buf) // move text of following lines memmove((char *)dp + dp->db_txt_start - extra, (char *)dp + dp->db_txt_start, - (size_t)(start - dp->db_txt_start)); + (size_t)(start - (int)dp->db_txt_start)); // adjust pointers of this and following lines - for (i = idx + 1; i < count; ++i) { - dp->db_index[i] -= extra; + for (i = idx + 1; i < count; i++) { + dp->db_index[i] -= (unsigned)extra; } } - dp->db_index[idx] -= extra; + dp->db_index[idx] -= (unsigned)extra; // adjust free space - dp->db_free -= extra; - dp->db_txt_start -= extra; + dp->db_free -= (unsigned)extra; + dp->db_txt_start -= (unsigned)extra; // copy new line into the data block memmove(old_line - extra, new_line, (size_t)new_len); @@ -2866,7 +2856,7 @@ static void ml_flush_line(buf_T *buf) // Don't forget to copy the mark! // How about handling errors??? (void)ml_append_int(buf, lnum, new_line, new_len, false, - (dp->db_index[idx] & DB_MARKED)); + (int)(dp->db_index[idx] & DB_MARKED)); (void)ml_delete_int(buf, lnum, false); } } @@ -2886,8 +2876,8 @@ static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count) bhdr_T *hp = mf_new(mfp, negative, (unsigned)page_count); DATA_BL *dp = hp->bh_data; dp->db_id = DATA_ID; - dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size; - dp->db_free = dp->db_txt_start - HEADER_SIZE; + dp->db_txt_start = dp->db_txt_end = (unsigned)page_count * mfp->mf_page_size; + dp->db_free = dp->db_txt_start - (unsigned)HEADER_SIZE; dp->db_line_count = 0; return hp; @@ -2900,7 +2890,7 @@ static bhdr_T *ml_new_ptr(memfile_T *mfp) PTR_BL *pp = hp->bh_data; pp->pb_id = PTR_ID; pp->pb_count = 0; - pp->pb_count_max = (mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1; + pp->pb_count_max = (uint16_t)((mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1); return hp; } @@ -3000,7 +2990,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) * search downwards in the tree until a data block is found */ for (;;) { - if ((hp = mf_get(mfp, bnum, page_count)) == NULL) { + if ((hp = mf_get(mfp, bnum, (unsigned)page_count)) == NULL) { goto error_noblock; } @@ -3110,7 +3100,7 @@ static int ml_add_stack(buf_T *buf) CHECK(top > 0, _("Stack size increases")); // more than 5 levels??? buf->b_ml.ml_stack_size += STACK_INCR; - size_t new_size = sizeof(infoptr_T) * buf->b_ml.ml_stack_size; + size_t new_size = sizeof(infoptr_T) * (size_t)buf->b_ml.ml_stack_size; buf->b_ml.ml_stack = xrealloc(buf->b_ml.ml_stack, new_size); } @@ -3179,7 +3169,7 @@ int resolve_symlink(const char_u *fname, char_u *buf) return FAIL; } - ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1); + ret = (int)readlink((char *)tmp, (char *)buf, MAXPATHL - 1); if (ret <= 0) { if (errno == EINVAL || errno == ENOENT) { // Found non-symlink or not existing file, stop here. @@ -3294,9 +3284,9 @@ char_u *get_file_in_dir(char_u *fname, char_u *dname) } else { save_char = *tail; *tail = NUL; - t = (char_u *)concat_fnames((char *)fname, (char *)dname + 2, TRUE); - *tail = save_char; - retval = (char_u *)concat_fnames((char *)t, (char *)tail, TRUE); + t = (char_u *)concat_fnames((char *)fname, (char *)dname + 2, true); + *tail = (uint8_t)save_char; + retval = (char_u *)concat_fnames((char *)t, (char *)tail, true); xfree(t); } } else { @@ -3798,8 +3788,7 @@ void ml_setflags(buf_T *buf) if (hp->bh_bnum == 0) { b0p = hp->bh_data; b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; - b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK) - | (get_fileformat(buf) + 1); + b0p->b0_flags = (uint8_t)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1)); add_b0_fenc(b0p, buf); hp->bh_flags |= BH_DIRTY; mf_sync(buf->b_ml.ml_mfp, MFS_ZERO); @@ -3887,7 +3876,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks) { buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2; buf->b_ml.ml_chunksize = xrealloc(buf->b_ml.ml_chunksize, - sizeof(chunksize_T) * buf->b_ml.ml_numchunks); + sizeof(chunksize_T) * (size_t)buf->b_ml.ml_numchunks); } if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL) { @@ -3898,8 +3887,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) memmove(buf->b_ml.ml_chunksize + curix + 1, buf->b_ml.ml_chunksize + curix, - (buf->b_ml.ml_usedchunks - curix) * - sizeof(chunksize_T)); + (size_t)(buf->b_ml.ml_usedchunks - curix) * sizeof(chunksize_T)); // Compute length of first half of lines in the split chunk size = 0; linecnt = 0; @@ -3910,12 +3898,11 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) return; } dp = hp->bh_data; - count = (long)(buf->b_ml.ml_locked_high) - - (long)(buf->b_ml.ml_locked_low) + 1; + count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; idx = curline - buf->b_ml.ml_locked_low; curline = buf->b_ml.ml_locked_high + 1; if (idx == 0) { // first line in block, text at the end - text_end = dp->db_txt_end; + text_end = (int)dp->db_txt_end; } else { text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); } @@ -3928,7 +3915,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) idx = count - 1; linecnt += rest; } - size += text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); + size += text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK); } buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt; buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt; @@ -3959,11 +3946,10 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) } dp = hp->bh_data; if (dp->db_line_count == 1) { - rest = dp->db_txt_end - dp->db_txt_start; + rest = (int)(dp->db_txt_end - dp->db_txt_start); } else { - rest = - ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK) - - dp->db_txt_start; + rest = (int)((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK) + - (int)dp->db_txt_start; } curchnk->mlcs_totalsize = rest; curchnk->mlcs_numlines = 1; @@ -3982,7 +3968,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) } else if (curix == 0 && curchnk->mlcs_numlines <= 0) { buf->b_ml.ml_usedchunks--; memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1, - buf->b_ml.ml_usedchunks * sizeof(chunksize_T)); + (size_t)buf->b_ml.ml_usedchunks * sizeof(chunksize_T)); return; } else if (curix == 0 || (curchnk->mlcs_numlines > 10 && (curchnk->mlcs_numlines + @@ -3998,8 +3984,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) if (curix < buf->b_ml.ml_usedchunks) { memmove(buf->b_ml.ml_chunksize + curix, buf->b_ml.ml_chunksize + curix + 1, - (buf->b_ml.ml_usedchunks - curix) * - sizeof(chunksize_T)); + (size_t)(buf->b_ml.ml_usedchunks - curix) * sizeof(chunksize_T)); } return; } @@ -4049,7 +4034,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) if (lnum == 0 || buf->b_ml.ml_line_lnum < lnum || !no_ff) { ml_flush_line(curbuf); } else if (can_cache && buf->b_ml.ml_line_offset > 0) { - return buf->b_ml.ml_line_offset; + return (long)buf->b_ml.ml_line_offset; } if (buf->b_ml.ml_usedchunks == -1 @@ -4071,7 +4056,8 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) * special because it will never qualify */ curline = 1; - curix = size = 0; + curix = 0; + size = 0; while (curix < buf->b_ml.ml_usedchunks - 1 && ((lnum != 0 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines) @@ -4093,11 +4079,10 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) return -1; } dp = hp->bh_data; - count = (long)(buf->b_ml.ml_locked_high) - - (long)(buf->b_ml.ml_locked_low) + 1; + count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; start_idx = idx = curline - buf->b_ml.ml_locked_low; if (idx == 0) { // first line in block, text at the end - text_end = dp->db_txt_end; + text_end = (int)dp->db_txt_end; } else { text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); } @@ -4123,7 +4108,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) idx++; } } - len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); + len = text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK); size += len; if (offset != 0 && size >= offset) { if (size + ffdos == offset) { @@ -4132,7 +4117,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) *offp = offset - size + len; } else { *offp = offset - size + len - - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK)); + - (text_end - (int)((dp->db_index[idx - 1]) & DB_INDEX_MASK)); } curline += idx - start_idx + extra; if (curline > buf->b_ml.ml_line_count) { @@ -4158,7 +4143,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) } if (can_cache && size > 0) { - buf->b_ml.ml_line_offset = size; + buf->b_ml.ml_line_offset = (size_t)size; } return size; @@ -4168,14 +4153,13 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) void goto_byte(long cnt) { long boff = cnt; - linenr_T lnum; ml_flush_line(curbuf); // cached line may be dirty setpcmark(); if (boff) { boff--; } - lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff, false); + linenr_T lnum = (linenr_T)ml_find_line_or_offset(curbuf, (linenr_T)0, &boff, false); if (lnum < 1) { // past the end curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_curswant = MAXCOL; diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 4d5cf047f9..5ae7f7bbe3 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -14,6 +14,7 @@ #include "nvim/eval.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/mapping.h" #include "nvim/memfile.h" @@ -545,7 +546,7 @@ void arena_start(Arena *arena, ArenaMem *reuse_blk) /// Finnish the allocations in an arena. /// -/// This does not immedately free the memory, but leaves existing allocated +/// This does not immediately free the memory, but leaves existing allocated /// objects valid, and returns an opaque ArenaMem handle, which can be used to /// free the allocations using `arena_mem_free`, when the objects allocated /// from the arena are not needed anymore. @@ -710,6 +711,7 @@ void free_all_mem(void) free_search_patterns(); free_old_sub(); free_last_insert(); + free_insexpand_stuff(); free_prev_shellcmd(); free_regexp_stuff(); free_tag_stuff(); diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 018c62d604..febb66081a 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -121,7 +121,7 @@ void ex_menu(exarg_T *eap) } if (ascii_iswhite(*p)) { for (i = 0; i < MENUDEPTH && !ascii_iswhite(*arg); i++) { - pri_tab[i] = getdigits_long((char_u **)&arg, false, 0); + pri_tab[i] = getdigits_long(&arg, false, 0); if (pri_tab[i] == 0) { pri_tab[i] = 500; } diff --git a/src/nvim/message.c b/src/nvim/message.c index 2c96613bb3..621a9212df 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -493,6 +493,7 @@ int smsg(const char *s, ...) va_start(arglist, s); vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); va_end(arglist); + return msg((char *)IObuff); } @@ -1389,7 +1390,7 @@ void msg_start(void) need_fileinfo = false; } - bool no_msg_area = !ui_has(kUIMessages) && p_ch < 1; + const bool no_msg_area = !ui_has_messages(); if (need_clr_eos || (no_msg_area && redrawing_cmdline)) { // Halfway an ":echo" command and getting an (error) message: clear @@ -2503,6 +2504,7 @@ static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int if (do_clear_sb_text == SB_CLEAR_ALL || do_clear_sb_text == SB_CLEAR_CMDLINE_DONE) { clear_sb_text(do_clear_sb_text == SB_CLEAR_ALL); + msg_sb_eol(); // prevent messages from overlapping do_clear_sb_text = SB_CLEAR_NONE; } @@ -2537,14 +2539,44 @@ void may_clear_sb_text(void) do_clear_sb_text = SB_CLEAR_ALL; } -/// Starting to edit the command line, do not clear messages now. +/// Starting to edit the command line: do not clear messages now. void sb_text_start_cmdline(void) { + if (do_clear_sb_text == SB_CLEAR_CMDLINE_BUSY) { + // Invoking command line recursively: the previous-level command line + // doesn't need to be remembered as it will be redrawn when returning + // to that level. + sb_text_restart_cmdline(); + } else { + msg_sb_eol(); + do_clear_sb_text = SB_CLEAR_CMDLINE_BUSY; + } +} + +/// Redrawing the command line: clear the last unfinished line. +void sb_text_restart_cmdline(void) +{ + // Needed when returning from nested command line. do_clear_sb_text = SB_CLEAR_CMDLINE_BUSY; - msg_sb_eol(); + + if (last_msgchunk == NULL || last_msgchunk->sb_eol) { + // No unfinished line: don't clear anything. + return; + } + + msgchunk_T *tofree = msg_sb_start(last_msgchunk); + last_msgchunk = tofree->sb_prev; + if (last_msgchunk != NULL) { + last_msgchunk->sb_next = NULL; + } + while (tofree != NULL) { + msgchunk_T *tofree_next = tofree->sb_next; + xfree(tofree); + tofree = tofree_next; + } } -/// Ending to edit the command line. Clear old lines but the last one later. +/// Ending to edit the command line: clear old lines but the last one later. void sb_text_end_cmdline(void) { do_clear_sb_text = SB_CLEAR_CMDLINE_DONE; @@ -2564,7 +2596,7 @@ void clear_sb_text(int all) if (last_msgchunk == NULL) { return; } - lastp = &last_msgchunk->sb_prev; + lastp = &msg_sb_start(last_msgchunk)->sb_prev; } while (*lastp != NULL) { @@ -3081,7 +3113,7 @@ void msg_clr_eos_force(void) msg_row = msg_grid_pos; } - if (p_ch > 0) { + if (ui_has_messages()) { grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ', ' ', HL_ATTR(HLF_MSG)); grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, diff --git a/src/nvim/move.c b/src/nvim/move.c index bd68ad6f97..6d4eb8ef49 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -2271,7 +2271,13 @@ void do_check_cursorbind(void) int restart_edit_save = restart_edit; restart_edit = true; check_cursor(); - validate_cursor(); + + // Avoid a scroll here for the cursor position, 'scrollbind' is + // more important. + if (!curwin->w_p_scb) { + validate_cursor(); + } + restart_edit = restart_edit_save; } // Correct cursor for multi-byte character. diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de01443313..1ca68979f4 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -239,7 +239,14 @@ static void parse_msgpack(Channel *channel) { Unpacker *p = channel->rpc.unpacker; while (unpacker_advance(p)) { - if (p->type == kMessageTypeResponse) { + if (p->type == kMessageTypeRedrawEvent) { + if (p->grid_line_event) { + ui_client_event_raw_line(p->grid_line_event); + } else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { + p->ui_handler.fn(p->result.data.array); + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + } + } else if (p->type == kMessageTypeResponse) { ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); if (p->request_id != frame->request_id) { char buf[256]; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 26c1843026..c8e9fdd4c3 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -6,6 +6,7 @@ #include "nvim/memory.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" +#include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/unpacker.c.generated.h" @@ -280,10 +281,20 @@ error: // is relatively small, just ~10 bytes + the method name. Thus we can simply refuse // to advance the stream beyond the header until it can be parsed in its entirety. // -// Of course, later on, we want to specialize state 2 into sub-states depending +// Later on, we want to specialize state 2 into more sub-states depending // on the specific method. "nvim_exec_lua" should just decode direct into lua -// objects, and "redraw/grid_line" should use a hand-rolled decoder to avoid -// a blizzard of small objects for each screen cell. +// objects. For the moment "redraw/grid_line" uses a hand-rolled decoder, +// to avoid a blizzard of small objects for each screen cell. +// +// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] +// +// Where [args] gets unpacked as an Array. Note: first {11} is not saved as a state. +// +// When method is "grid_line", we furthermore decode a cell at a time like: +// +// <0>[2, "redraw", <10>[{11}["grid_line", <13>[g, r, c, [<14>[cell], <14>[cell], ...]], ...], <11>[...], ...]] +// +// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional bool unpacker_advance(Unpacker *p) { @@ -292,8 +303,27 @@ bool unpacker_advance(Unpacker *p) if (!unpacker_parse_header(p)) { return false; } - p->state = p->type == kMessageTypeResponse ? 1 : 2; - arena_start(&p->arena, &p->reuse_blk); + if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) { + p->type = kMessageTypeRedrawEvent; + p->state = 10; + } else { + p->state = p->type == kMessageTypeResponse ? 1 : 2; + arena_start(&p->arena, &p->reuse_blk); + } + } + + if (p->state >= 10 && p->state != 12) { + if (!unpacker_parse_redraw(p)) { + return false; + } + + if (p->state == 14) { + // grid_line event already unpacked + goto done; + } else { + // unpack other ui events using mpack_parse() + arena_start(&p->arena, &p->reuse_blk); + } } int result; @@ -310,13 +340,173 @@ rerun: return false; } - if (p->state == 1) { +done: + switch (p->state) { + case 1: p->error = p->result; p->state = 2; goto rerun; - } else { - assert(p->state == 2); + case 2: p->state = 0; + return true; + case 12: + case 14: + p->ncalls--; + if (p->ncalls > 0) { + p->state = (p->state == 14) ? 13 : 12; + } else if (p->nevents > 0) { + p->state = 11; + } else { + p->state = 0; + } + return true; + default: + abort(); + } +} + +bool unpacker_parse_redraw(Unpacker *p) +{ + mpack_token_t tok; + int result; + + const char *data = p->read_ptr; + size_t size = p->read_size; + GridLineEvent *g = p->grid_line_event; + +#define NEXT_TYPE(tok, typ) \ + result = mpack_rtoken(&data, &size, &tok); \ + if (result == MPACK_EOF) { \ + return false; \ + } else if (result || (tok.type != typ \ + && !(typ == MPACK_TOKEN_STR && tok.type == MPACK_TOKEN_BIN) \ + && !(typ == MPACK_TOKEN_SINT && tok.type == MPACK_TOKEN_UINT))) { \ + p->state = -1; \ + return false; \ + } + +redo: + switch (p->state) { + case 10: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + p->nevents = (int)tok.length; + FALLTHROUGH; + + case 11: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + p->ncalls = (int)tok.length; + + if (p->ncalls-- == 0) { + p->state = -1; + return false; + } + + NEXT_TYPE(tok, MPACK_TOKEN_STR); + if (tok.length > size) { + return false; + } + + p->ui_handler = ui_client_get_redraw_handler(data, tok.length, NULL); + data += tok.length; + size -= tok.length; + + p->nevents--; + p->read_ptr = data; + p->read_size = size; + if (p->ui_handler.fn != ui_client_event_grid_line) { + p->state = 12; + if (p->grid_line_event) { + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + p->grid_line_event = NULL; + } + return true; + } else { + p->state = 13; + arena_start(&p->arena, &p->reuse_blk); + p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true); + g = p->grid_line_event; + } + FALLTHROUGH; + + case 13: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int eventarrsize = (int)tok.length; + if (eventarrsize != 4) { + p->state = -1; + return false; + } + + for (int i = 0; i < 3; i++) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + g->args[i] = (int)tok.data.value.lo; + } + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + g->ncells = (int)tok.length; + g->icell = 0; + g->coloff = 0; + g->cur_attr = -1; + + p->read_ptr = data; + p->read_size = size; + p->state = 14; + FALLTHROUGH; + + case 14: + assert(g->icell < g->ncells); + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int cellarrsize = (int)tok.length; + if (cellarrsize < 1 || cellarrsize > 3) { + p->state = -1; + return false; + } + + NEXT_TYPE(tok, MPACK_TOKEN_STR); + if (tok.length > size) { + return false; + } + + const char *cellbuf = data; + size_t cellsize = tok.length; + data += cellsize; + size -= cellsize; + + if (cellarrsize >= 2) { + NEXT_TYPE(tok, MPACK_TOKEN_SINT); + g->cur_attr = (int)tok.data.value.lo; + } + + int repeat = 1; + if (cellarrsize >= 3) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + repeat = (int)tok.data.value.lo; + } + + g->clear_width = 0; + if (g->icell == g->ncells - 1 && cellsize == 1 && cellbuf[0] == ' ' && repeat > 1) { + g->clear_width = repeat; + } else { + for (int r = 0; r < repeat; r++) { + if (g->coloff >= (int)grid_line_buf_size) { + p->state = -1; + return false; + } + memcpy(grid_line_buf_char[g->coloff], cellbuf, cellsize); + grid_line_buf_char[g->coloff][cellsize] = NUL; + grid_line_buf_attr[g->coloff++] = g->cur_attr; + } + } + + g->icell++; + p->read_ptr = data; + p->read_size = size; + if (g->icell == g->ncells) { + return true; + } + goto redo; + + default: + abort(); } - return true; } diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index e0dc6f0a68..f39439be63 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -32,8 +32,13 @@ struct Unpacker { Error unpack_error; Arena arena; - // one lenght free-list of reusable blocks + // one length free-list of reusable blocks ArenaMem reuse_blk; + + int nevents; + int ncalls; + UIClientHandler ui_handler; + GridLineEvent *grid_line_event; }; // unrecovareble error. unpack_error should be set! diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b675abfb7d..2d752eade7 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1908,6 +1908,13 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs : wp->w_winbar_click_defs; + if (in_status_line && global_stl_height() > 0) { + // global statusline is displayed for the current window, + // and spans the whole screen. + click_defs = curwin->w_status_click_defs; + click_col = mouse_col; + } + if (click_defs != NULL) { switch (click_defs[click_col].type) { case kStlClickDisabled: @@ -2474,7 +2481,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **te col = 0; // Search for point of changing multibyte character class. this_class = mb_get_class(ptr); - while (ptr[col] != NUL + while (ptr[col] != NUL // -V781 && ((i == 0 ? mb_get_class(ptr + col) == this_class : mb_get_class(ptr + col) != 0) @@ -2803,7 +2810,7 @@ void pop_showcmd(void) static void display_showcmd(void) { - if (p_ch < 1 && !ui_has(kUIMessages)) { + if (!ui_has_messages()) { return; } @@ -2915,10 +2922,11 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { curwin = wp; curbuf = curwin->w_buffer; - // skip original window and windows with 'noscrollbind' + // skip original window and windows with 'noscrollbind' if (curwin == old_curwin || !curwin->w_p_scb) { continue; } + // do the vertical scroll if (want_ver) { if (old_curwin->w_p_diff && curwin->w_p_diff) { @@ -3478,7 +3486,8 @@ void scroll_redraw(int up, long count) redraw_later(curwin, VALID); } -/// Get the count specified after a 'z' command. +/// Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh', +/// 'z<Left>', and 'z<Right>' commands accept a count after 'z'. /// @return true to process the 'z' command and false to skip it. static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg) { @@ -5094,6 +5103,7 @@ static void nv_brackets(cmdarg_T *cap) } else if (cap->nchar == '\'' || cap->nchar == '`') { // "['", "[`", "]'" and "]`": jump to next mark fmark_T *fm = pos_to_mark(curbuf, NULL, curwin->w_cursor); + assert(fm != NULL); fmark_T *prev_fm; for (n = cap->count1; n > 0; n--) { prev_fm = fm; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3602eb2a3e..9a44c36d44 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -928,6 +928,7 @@ int do_record(int c) { char_u *p; static int regname; + static bool changed_cmdheight = false; yankreg_T *old_y_previous; int retval; @@ -941,6 +942,15 @@ int do_record(int c) showmode(); regname = c; retval = OK; + + if (!ui_has_messages()) { + // Enable macro indicator temporarily + set_option_value("ch", 1L, NULL, 0); + update_screen(VALID); + + changed_cmdheight = true; + } + apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf); } } else { // stop recording @@ -986,6 +996,12 @@ int do_record(int c) y_previous = old_y_previous; } + + if (changed_cmdheight) { + // Restore cmdheight + set_option_value("ch", 0L, NULL, 0); + redraw_all_later(CLEAR); + } } return retval; } @@ -1050,7 +1066,7 @@ static int execreg_lastc = NUL; /// with a \. Lines that start with a comment "\ character are ignored. /// @returns the concatenated line. The index of the line that should be /// processed next is returned in idx. -static char_u *execreg_line_continuation(char_u **lines, size_t *idx) +static char_u *execreg_line_continuation(char **lines, size_t *idx) { size_t i = *idx; assert(i > 0); @@ -1065,7 +1081,7 @@ static char_u *execreg_line_continuation(char_u **lines, size_t *idx) // Any line not starting with \ or "\ is the start of the // command. while (--i > 0) { - p = (char_u *)skipwhite((char *)lines[i]); + p = (char_u *)skipwhite(lines[i]); if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) { break; } @@ -1073,9 +1089,9 @@ static char_u *execreg_line_continuation(char_u **lines, size_t *idx) const size_t cmd_start = i; // join all the lines - ga_concat(&ga, (char *)lines[cmd_start]); + ga_concat(&ga, lines[cmd_start]); for (size_t j = cmd_start + 1; j <= cmd_end; j++) { - p = (char_u *)skipwhite((char *)lines[j]); + p = (char_u *)skipwhite(lines[j]); if (*p == '\\') { // Adjust the growsize to the current length to // speed up concatenating many lines. @@ -1188,7 +1204,7 @@ int do_execreg(int regname, int colon, int addcr, int silent) if (colon && i > 0) { p = (char_u *)skipwhite((char *)str); if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) { - str = execreg_line_continuation((char_u **)reg->y_array, &i); + str = execreg_line_continuation(reg->y_array, &i); free_str = true; } } @@ -1945,7 +1961,7 @@ static void mb_adjust_opend(oparg_T *oap) { if (oap->inclusive) { char *p = (char *)ml_get(oap->end.lnum); - oap->end.col += mb_tail_off(p, p + oap->end.col); + oap->end.col += utf_cp_tail_off(p, p + oap->end.col); } } @@ -2936,7 +2952,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) xfree(reg->y_array); } - if (message && (p_ch > 0 || ui_has(kUIMessages))) { // Display message about yank? + if (message) { // Display message about yank? if (yank_type == kMTCharWise && yanklines == 1) { yanklines = 0; } @@ -4480,6 +4496,9 @@ static void op_format(oparg_T *oap, int keep_cursor) if (keep_cursor) { curwin->w_cursor = saved_cursor; saved_cursor.lnum = 0; + + // formatting may have made the cursor position invalid + check_cursor(); } if (oap->is_VIsual) { @@ -5017,7 +5036,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) linenr_T amount = Prenum1; // do_addsub() might trigger re-evaluation of 'foldexpr' halfway, when the - // buffer is not completly updated yet. Postpone updating folds until before + // buffer is not completely updated yet. Postpone updating folds until before // the call to changed_lines(). disable_fold_update++; diff --git a/src/nvim/option.c b/src/nvim/option.c index cfd8248eb6..ba77d12a3d 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -37,6 +37,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -49,6 +50,7 @@ #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/macros.h" #include "nvim/mapping.h" @@ -1325,7 +1327,6 @@ int do_set(char *arg, int opt_flags) char *saved_newval = NULL; unsigned newlen; int comma; - bool new_value_alloced = false; // new string option was allocated // When using ":set opt=val" for a global option // with a local value the local value will be @@ -1365,7 +1366,6 @@ int do_set(char *arg, int opt_flags) // default value was already expanded, only // required when an environment variable was set // later - new_value_alloced = true; if (newval == NULL) { newval = empty_option; } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) { @@ -1379,7 +1379,6 @@ int do_set(char *arg, int opt_flags) } } else if (nextchar == '<') { // set to global val newval = vim_strsave(*(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL)); - new_value_alloced = true; } else { arg++; // jump to after the '=' or ':' @@ -1624,7 +1623,6 @@ int do_set(char *arg, int opt_flags) if (save_arg != NULL) { // number for 'whichwrap' arg = (char *)save_arg; } - new_value_alloced = true; } // Set the new value. @@ -1659,8 +1657,7 @@ int do_set(char *arg, int opt_flags) // for ":set" on local options. Note: when setting // 'syntax' or 'filetype' autocommands may be // triggered that can cause havoc. - errmsg = did_set_string_option(opt_idx, (char_u **)varp, - new_value_alloced, oldval, + errmsg = did_set_string_option(opt_idx, (char_u **)varp, oldval, errbuf, sizeof(errbuf), opt_flags, &value_checked); @@ -2299,9 +2296,9 @@ static char *set_string_option(const int opt_idx, const char *const value, const char *const saved_newval = xstrdup(s); int value_checked = false; - char *const r = did_set_string_option(opt_idx, (char_u **)varp, true, - (char_u *)oldval, - NULL, 0, opt_flags, &value_checked); + char *const r = did_set_string_option(opt_idx, (char_u **)varp, (char_u *)oldval, + NULL, 0, + opt_flags, &value_checked); if (r == NULL) { did_set_option(opt_idx, opt_flags, true, value_checked); } @@ -2430,19 +2427,18 @@ static char *check_mousescroll(char *string) } /// Handle string options that need some action to perform when changed. +/// The new value must be allocated. /// Returns NULL for success, or an error message for an error. /// /// @param opt_idx index in options[] table /// @param varp pointer to the option variable -/// @param new_value_alloced new value was allocated /// @param oldval previous value of the option /// @param errbuf buffer for errors, or NULL /// @param errbuflen length of errors buffer /// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL /// @param value_checked value was checked to be safe, no need to set P_INSECURE -static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_alloced, - char_u *oldval, char *errbuf, size_t errbuflen, int opt_flags, - int *value_checked) +static char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char *errbuf, + size_t errbuflen, int opt_flags, int *value_checked) { char *errmsg = NULL; char_u *s, *p; @@ -3021,7 +3017,7 @@ ambw_end: } // add / remove window bars for 'winbar' if (gvarp == (char_u **)&p_wbr) { - set_winbar(); + set_winbar(true); } } else if (gvarp == &p_cpt) { // check if it is a valid value for 'complete' -- Acevedo @@ -3097,11 +3093,8 @@ ambw_end: (char **)&p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); if (p != NULL) { - if (new_value_alloced) { - free_string_option(p_pt); - } + free_string_option(p_pt); p_pt = p; - new_value_alloced = true; } } } else if (varp == &p_bs) { // 'backspace' @@ -3344,9 +3337,7 @@ ambw_end: * If error detected, restore the previous value. */ if (errmsg != NULL) { - if (new_value_alloced) { - free_string_option(*varp); - } + free_string_option(*varp); *varp = oldval; /* * When resetting some values, need to act on it. @@ -3363,11 +3354,7 @@ ambw_end: if (free_oldval) { free_string_option(oldval); } - if (new_value_alloced) { - options[opt_idx].flags |= P_ALLOCED; - } else { - options[opt_idx].flags &= ~P_ALLOCED; - } + options[opt_idx].flags |= P_ALLOCED; if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && ((int)options[opt_idx].indir & PV_BOTH)) { @@ -5045,28 +5032,29 @@ static int findoption(const char *const arg) /// @param stringval NULL when only checking existence /// /// @returns: -/// Toggle option: 2, *numval gets value. -/// Number option: 1, *numval gets value. -/// String option: 0, *stringval gets allocated string. -/// Hidden Number or Toggle option: -1. -/// hidden String option: -2. -/// unknown option: -3. -int get_option_value(const char *name, long *numval, char **stringval, int opt_flags) +/// Number option: gov_number, *numval gets value. +/// Tottle option: gov_bool, *numval gets value. +/// String option: gov_string, *stringval gets allocated string. +/// Hidden Number option: gov_hidden_number. +/// Hidden Toggle option: gov_hidden_bool. +/// Hidden String option: gov_hidden_string. +/// Unknown option: gov_unknown. +getoption_T get_option_value(const char *name, long *numval, char **stringval, int opt_flags) { if (get_tty_option(name, stringval)) { - return 0; + return gov_string; } int opt_idx = findoption(name); - if (opt_idx < 0) { // Unknown option. - return -3; + if (opt_idx < 0) { // option not in the table + return gov_unknown; } char_u *varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (options[opt_idx].flags & P_STRING) { if (varp == NULL) { // hidden option - return -2; + return gov_hidden_string; } if (stringval != NULL) { if ((char_u **)varp == &p_pt) { // 'pastetoggle' @@ -5075,26 +5063,24 @@ int get_option_value(const char *name, long *numval, char **stringval, int opt_f *stringval = xstrdup(*(char **)(varp)); } } - return 0; + return gov_string; } if (varp == NULL) { // hidden option - return -1; + return (options[opt_idx].flags & P_NUM) ? gov_hidden_number : gov_hidden_bool; } if (options[opt_idx].flags & P_NUM) { *numval = *(long *)varp; - return 1; - } - - // Special case: 'modified' is b_changed, but we also want to consider - // it set when 'ff' or 'fenc' changed. - if ((int *)varp == &curbuf->b_changed) { - *numval = curbufIsChanged(); } else { - *numval = (long)*(int *)varp; // NOLINT(whitespace/cast) + // Special case: 'modified' is b_changed, but we also want to consider + // it set when 'ff' or 'fenc' changed. + if ((int *)varp == &curbuf->b_changed) { + *numval = curbufIsChanged(); + } else { + *numval = (long)(*(int *)varp); + } } - - return 2; + return (options[opt_idx].flags & P_NUM) ? gov_number : gov_bool; } // Returns the option attributes and its value. Unlike the above function it @@ -5301,6 +5287,14 @@ char *set_option_value(const char *const name, const long number, const char *co return NULL; } +/// Return true if "name" is a string option. +/// Returns false if option "name" does not exist. +bool is_string_option(const char *name) +{ + int idx = findoption(name); + return idx >= 0 && (options[idx].flags & P_STRING); +} + // Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. // When "has_lt" is true there is a '<' before "*arg_arg". // Returns 0 when the key is not recognized. @@ -6457,6 +6451,7 @@ void didset_window_options(win_T *wp) set_chars_option(wp, &wp->w_p_lcs, true); parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl check_blending(wp); + set_winbar_win(wp, false); wp->w_grid_alloc.blending = wp->w_p_winbl > 0; } @@ -6972,7 +6967,7 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags) } } -int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file) +int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file) { int num_normal = 0; // Nr of matching non-term-code settings int match; @@ -6994,7 +6989,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u *** if (loop == 0) { num_normal++; } else { - (*file)[count++] = vim_strsave((char_u *)names[match]); + (*file)[count++] = xstrdup(names[match]); } } } @@ -7021,7 +7016,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u *** if (loop == 0) { num_normal++; } else { - (*file)[count++] = vim_strsave(str); + (*file)[count++] = (char *)vim_strsave(str); } } } @@ -7032,18 +7027,18 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u *** } else { return OK; } - *file = (char_u **)xmalloc((size_t)(*num_file) * sizeof(char_u *)); + *file = xmalloc((size_t)(*num_file) * sizeof(char_u *)); } } return OK; } -void ExpandOldSetting(int *num_file, char_u ***file) +void ExpandOldSetting(int *num_file, char ***file) { char_u *var = NULL; *num_file = 0; - *file = (char_u **)xmalloc(sizeof(char_u *)); + *file = xmalloc(sizeof(char_u *)); /* * For a terminal key code expand_option_idx is < 0. @@ -7078,7 +7073,7 @@ void ExpandOldSetting(int *num_file, char_u ***file) } #endif - *file[0] = buf; + *file[0] = (char *)buf; *num_file = 1; } @@ -7890,16 +7885,15 @@ static bool briopt_check(win_T *wp) bool bri_sbr = false; int bri_list = 0; - char_u *p = wp->w_p_briopt; - while (*p != NUL) - { + char *p = (char *)wp->w_p_briopt; + while (*p != NUL) { if (STRNCMP(p, "shift:", 6) == 0 && ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) { p += 6; - bri_shift = getdigits_int((char **)&p, true, 0); + bri_shift = getdigits_int(&p, true, 0); } else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) { p += 4; - bri_min = getdigits_int((char **)&p, true, 0); + bri_min = getdigits_int(&p, true, 0); } else if (STRNCMP(p, "sbr", 3) == 0) { p += 3; bri_sbr = true; diff --git a/src/nvim/option.h b/src/nvim/option.h index 9321dd5454..a5a57cc66d 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -3,6 +3,17 @@ #include "nvim/ex_cmds_defs.h" // for exarg_T +/// Returned by get_option_value(). +typedef enum { + gov_unknown, + gov_bool, + gov_number, + gov_string, + gov_hidden_bool, + gov_hidden_number, + gov_hidden_string, +} getoption_T; + // flags for buf_copy_options() #define BCO_ENTER 1 // going to enter the buffer #define BCO_ALWAYS 2 // always copy the options diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 37f3770506..fd4661f9f9 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2063,7 +2063,7 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, varname='p_ssop', - defaults={if_true="blank,buffers,curdir,folds,help,tabpages,winsize"} + defaults={if_true="blank,buffers,curdir,folds,help,tabpages,winsize,terminal"} }, { full_name='shada', abbreviation='sd', diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 9283ea2e42..8f2018c1f4 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -47,30 +47,30 @@ typedef struct { # include "os/shell.c.generated.h" #endif -static void save_patterns(int num_pat, char_u **pat, int *num_file, char_u ***file) +static void save_patterns(int num_pat, char **pat, int *num_file, char ***file) { *file = xmalloc((size_t)num_pat * sizeof(char_u *)); for (int i = 0; i < num_pat; i++) { - char_u *s = vim_strsave(pat[i]); + char_u *s = vim_strsave((char_u *)pat[i]); // Be compatible with expand_filename(): halve the number of // backslashes. backslash_halve(s); - (*file)[i] = s; + (*file)[i] = (char *)s; } *num_file = num_pat; } -static bool have_wildcard(int num, char_u **file) +static bool have_wildcard(int num, char **file) { for (int i = 0; i < num; i++) { - if (path_has_wildcard(file[i])) { + if (path_has_wildcard((char_u *)file[i])) { return true; } } return false; } -static bool have_dollars(int num, char_u **file) +static bool have_dollars(int num, char **file) { for (int i = 0; i < num; i++) { if (vim_strchr((char *)file[i], '$') != NULL) { @@ -98,7 +98,7 @@ static bool have_dollars(int num, char_u **file) /// copied into *file. /// /// @returns OK for success or FAIL for error. -int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) +int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, int flags) FUNC_ATTR_NONNULL_ARG(3) FUNC_ATTR_NONNULL_ARG(4) { @@ -151,7 +151,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file // Don't allow the use of backticks in secure. if (secure) { for (i = 0; i < num_pat; i++) { - if (vim_strchr((char *)pat[i], '`') != NULL + if (vim_strchr(pat[i], '`') != NULL && (check_secure())) { return FAIL; } @@ -297,7 +297,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file } // Copy one character. - *p++ = pat[i][j]; + *p++ = (char_u)pat[i][j]; } *p = NUL; } @@ -471,7 +471,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file // Isolate the individual file names. p = buffer; for (i = 0; i < *num_file; i++) { - (*file)[i] = p; + (*file)[i] = (char *)p; // Space or NL separates if (shell_style == STYLE_ECHO || shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) { @@ -496,19 +496,19 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file // Move the file names to allocated memory. for (j = 0, i = 0; i < *num_file; i++) { // Require the files to exist. Helps when using /bin/sh - if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) { + if (!(flags & EW_NOTFOUND) && !os_path_exists((char_u *)(*file)[i])) { continue; } // check if this entry should be included - dir = (os_isdir((*file)[i])); + dir = (os_isdir((char_u *)(*file)[i])); 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((char *)(*file)[i], NULL, !(flags & EW_SHELLCMD))) { + && !os_can_exe((*file)[i], NULL, !(flags & EW_SHELLCMD))) { continue; } @@ -517,7 +517,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file if (dir) { add_pathsep((char *)p); // add '/' to a directory name } - (*file)[j++] = p; + (*file)[j++] = (char *)p; } xfree(buffer); *num_file = j; diff --git a/src/nvim/path.c b/src/nvim/path.c index b22c0a18bd..a0b09bcec2 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1211,7 +1211,7 @@ static bool has_special_wildchar(char_u *p) /// 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 gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) +int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, int flags) { garray_T ga; char_u *p; @@ -1240,8 +1240,8 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil * For `=expr` do use the internal function. */ for (int i = 0; i < num_pat; i++) { - if (has_special_wildchar(pat[i]) - && !(vim_backtick(pat[i]) && pat[i][1] == '=')) { + if (has_special_wildchar((char_u *)pat[i]) + && !(vim_backtick((char_u *)pat[i]) && pat[i][1] == '=')) { return os_expand_wildcards(num_pat, pat, num_file, file, flags); } } @@ -1256,13 +1256,13 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil for (int i = 0; i < num_pat; ++i) { add_pat = -1; - p = pat[i]; + p = (char_u *)pat[i]; if (vim_backtick(p)) { add_pat = expand_backtick(&ga, p, flags); if (add_pat == -1) { recursive = false; - FreeWild(ga.ga_len, (char_u **)ga.ga_data); + FreeWild(ga.ga_len, ga.ga_data); *num_file = 0; *file = NULL; return FAIL; @@ -1272,7 +1272,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~') { p = expand_env_save_opt(p, true); if (p == NULL) { - p = pat[i]; + p = (char_u *)pat[i]; } #ifdef UNIX /* @@ -1338,13 +1338,13 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil if (did_expand_in_path && !GA_EMPTY(&ga) && (flags & EW_PATH)) { uniquefy_paths(&ga, p); } - if (p != pat[i]) { + if (p != (char_u *)pat[i]) { xfree(p); } } *num_file = ga.ga_len; - *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : NULL; + *file = (ga.ga_data != NULL) ? ga.ga_data : NULL; recursive = false; @@ -1352,7 +1352,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil } /// Free the list of files returned by expand_wildcards() or other expansion functions. -void FreeWild(int count, char_u **files) +void FreeWild(int count, char **files) { if (count <= 0 || files == NULL) { return; @@ -2123,21 +2123,20 @@ char_u *path_shorten_fname(char_u *full_path, char_u *dir_name) /// 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_eval(char_u **pat, int *num_file, char_u ***file, int flags) +int expand_wildcards_eval(char_u **pat, int *num_file, char ***file, int flags) { int ret = FAIL; char_u *eval_pat = NULL; - char_u *exp_pat = *pat; + char *exp_pat = (char *)(*pat); char *ignored_msg; size_t usedlen; if (*exp_pat == '%' || *exp_pat == '#' || *exp_pat == '<') { - ++emsg_off; - eval_pat = eval_vars(exp_pat, exp_pat, &usedlen, - NULL, &ignored_msg, NULL); - --emsg_off; + emsg_off++; + eval_pat = eval_vars((char_u *)exp_pat, (char_u *)exp_pat, &usedlen, NULL, &ignored_msg, NULL); + emsg_off--; if (eval_pat != NULL) { - exp_pat = concat_str(eval_pat, exp_pat + usedlen); + exp_pat = (char *)concat_str(eval_pat, (char_u *)exp_pat + usedlen); } } @@ -2167,7 +2166,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, int flags /// 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_files, char_u ***files, int flags) +int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int flags) { int retval; int i, j; @@ -2190,10 +2189,10 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, // check all files in (*files)[] assert(*num_files == 0 || *files != NULL); for (i = 0; i < *num_files; i++) { - ffname = (char_u *)FullName_save((char *)(*files)[i], false); + ffname = (char_u *)FullName_save((*files)[i], false); assert((*files)[i] != NULL); assert(ffname != NULL); - if (match_file_list(p_wig, (*files)[i], ffname)) { + if (match_file_list(p_wig, (char_u *)(*files)[i], ffname)) { // remove this matching file from the list xfree((*files)[i]); for (j = i; j + 1 < *num_files; j++) { @@ -2213,15 +2212,15 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, if (*num_files > 1) { non_suf_match = 0; for (i = 0; i < *num_files; i++) { - if (!match_suffix((*files)[i])) { + if (!match_suffix((char_u *)(*files)[i])) { // // Move the name without matching suffix to the front of the list. // - p = (*files)[i]; + p = (char_u *)(*files)[i]; for (j = i; j > non_suf_match; j--) { (*files)[j] = (*files)[j - 1]; } - (*files)[non_suf_match++] = p; + (*files)[non_suf_match++] = (char *)p; } } } diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po index ac96b8b3e4..fae2fd4967 100644 --- a/src/nvim/po/tr.po +++ b/src/nvim/po/tr.po @@ -1,44 +1,29 @@ -# Turkish translations for Vim -# Vim Türkçe çevirileri -# Copyright (C) 2021 Emir SARI <emir_sari@msn.com> +# Turkish translations for Neovim +# Neovim Türkçe çevirileri +# Copyright (C) 2019-2022 Emir SARI <emir_sari@icloud.com> # This file is distributed under the same license as the Vim package. -# Emir SARI <emir_sari@msn.com>, 2019-2021 +# Emir SARI <emir_sari@icloud.com>, 2019-2022 # msgid "" msgstr "" -"Project-Id-Version: Vim Turkish Localization Project\n" +"Project-Id-Version: Neovim Turkish Localization Project\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-16 17:56+0300\n" -"PO-Revision-Date: 2021-07-16 20:00+0300\n" -"Last-Translator: Emir SARI <emir_sari@msn.com>\n" -"Language-Team: Turkish <https://github.com/bitigchi/vim>\n" +"POT-Creation-Date: 2022-04-13 22:59+0300\n" +"PO-Revision-Date: 2022-07-20 05:00+0300\n" +"Last-Translator: Emir SARI <emir_sari@icloud.com>\n" +"Language-Team: Turkish <https://github.com/bitigchi/neovim>\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -msgid "E163: There is only one file to edit" -msgstr "E163: Düzenlenecek yalnızca bir dosya var" - -msgid "E164: Cannot go before first file" -msgstr "E164: İlk dosyadan öncesine gidilemez" - -msgid "E165: Cannot go beyond last file" -msgstr "E165: Son dosyadan öteye gidilemez" - -msgid "E610: No argument to delete" -msgstr "E610: Silinecek bir argüman yok" - -msgid "E249: window layout changed unexpectedly" -msgstr "E249: Pencere yerleşimi beklenmedik bir biçimde değişti" - msgid "--Deleted--" msgstr "--Silindi--" #, c-format msgid "auto-removing autocommand: %s <buffer=%d>" -msgstr "otokomut kendiliğinden kaldırılıyor: %s <buffer=%d>" +msgstr "otokomut kendiliğinden kaldırılıyor: %s <arabellek=%d>" #, c-format msgid "E367: No such group: \"%s\"" @@ -50,18 +35,6 @@ msgstr "E936: Geçerli grup silinemiyor" msgid "W19: Deleting augroup that is still in use" msgstr "W19: Kullanımda olan otokomut grubu siliniyor" -#, c-format -msgid "E215: Illegal character after *: %s" -msgstr "E215: * sonrası izin verilmeyen karakter: %s" - -#, c-format -msgid "E216: No such event: %s" -msgstr "E216: Böyle bir olay yok: %s" - -#, c-format -msgid "E216: No such group or event: %s" -msgstr "E216: Böyle bir grup veya olay yok: %s" - msgid "" "\n" "--- Autocommands ---" @@ -71,7 +44,7 @@ msgstr "" #, c-format msgid "E680: <buffer=%d>: invalid buffer number " -msgstr "E680: <buffer=%d>: Geçersiz arabellek numarası" +msgstr "E680: <arabellek=%d>: Geçersiz arabellek numarası " msgid "E217: Can't execute autocommands for ALL events" msgstr "E217: Otokomutlar TÜM olaylar için çalıştırılamıyor" @@ -94,23 +67,17 @@ msgstr "%s çalıştırılıyor" msgid "autocommand %s" msgstr "%s otokomutu" -msgid "E972: Blob value does not have the right number of bytes" -msgstr "E972: İkili geniş nesne değeri doğru bayt sayısına sahip değil" - -msgid "E831: bf_key_init() called with empty password" -msgstr "E831: bf_key_init() boş bir şifre ile çağrıldı" - -msgid "E820: sizeof(uint32_t) != 4" -msgstr "E820: sizeof(uint32_t) != 4" - -msgid "E817: Blowfish big/little endian use wrong" -msgstr "E817: Blowfish yüksek/düşük son haneli kullanımı yanlış" +#, c-format +msgid "E215: Illegal character after *: %s" +msgstr "E215: * sonrası izin verilmeyen karakter: %s" -msgid "E818: sha256 test failed" -msgstr "E818: sha256 testi başarısız oldu" +#, c-format +msgid "E216: No such event: %s" +msgstr "E216: Böyle bir olay yok: %s" -msgid "E819: Blowfish test failed" -msgstr "E819: Blowfish testi başarısız oldu" +#, c-format +msgid "E216: No such group or event: %s" +msgstr "E216: Böyle bir grup veya olay yok: %s" msgid "[Location List]" msgstr "[Konum Listesi]" @@ -127,12 +94,17 @@ msgstr "E82: Arabellek ayrılamadı, çıkılıyor..." msgid "E83: Cannot allocate buffer, using other one..." msgstr "E83: Arabellek ayrılamadı, başka bir tane kullanılıyor..." -msgid "E931: Buffer cannot be registered" -msgstr "E931: Arabellek kaydedilemedi" +msgid "E937: Attempt to delete a buffer that is in use" +msgstr "E937: Kullanımda olan bir arabellek silinmeye çalışılıyor" -#, c-format -msgid "E937: Attempt to delete a buffer that is in use: %s" -msgstr "E937: Kullanımda olan bir arabellek silinmeye çalışılıyor: %s" +msgid "E515: No buffers were unloaded" +msgstr "E515: Hiçbir arabellek bellekten kaldırılmadı" + +msgid "E516: No buffers were deleted" +msgstr "E516: Hiçbir arabellek silinmedi" + +msgid "E517: No buffers were wiped out" +msgstr "E517: Hiçbir arabellek yok edilmedi" msgid "E90: Cannot unload last buffer" msgstr "E90: Son arabellek bellekten kaldırılamıyor" @@ -150,37 +122,15 @@ msgid "E88: Cannot go before first buffer" msgstr "E88: İlk arabellekten öncesine gidilemez" #, c-format -msgid "E89: No write since last change for buffer %d (add ! to override)" +msgid "" +"E89: No write since last change for buffer %<PRId64> (add ! to override)" msgstr "" -"E89: %d numaralı arabellek son değişiklikten sonra yazılmadı (geçersiz " +"E89: %<PRId64> numaralı arabellek son değişiklikten sonra yazılmadı (geçersiz " "kılmak için ! ekleyin)" -msgid "E515: No buffers were unloaded" -msgstr "E515: Hiçbir arabellek bellekten kaldırılmadı" - -msgid "E516: No buffers were deleted" -msgstr "E516: Hiçbir arabellek silinmedi" - -msgid "E517: No buffers were wiped out" -msgstr "E517: Hiçbir arabellek yok edilmedi" - -#, c-format -msgid "%d buffer unloaded" -msgid_plural "%d buffers unloaded" -msgstr[0] "%d arabellek bellekten kaldırıldı" -msgstr[1] "%d arabellek bellekten kaldırıldı" - -#, c-format -msgid "%d buffer deleted" -msgid_plural "%d buffers deleted" -msgstr[0] "%d arabellek silindi" -msgstr[1] "%d arabellek silindi" - #, c-format -msgid "%d buffer wiped out" -msgid_plural "%d buffers wiped out" -msgstr[0] "%d arabellek yok edildi" -msgstr[1] "%d arabellek yok edildi" +msgid "E89: %s will be killed (add ! to override)" +msgstr "E89: %s sonlandırılacak (geçersiz kılmak için ! ekleyin)" msgid "E948: Job still running (add ! to end the job)" msgstr "E948: İş hâlâ sürüyor (bitirmek için ! ekleyin)" @@ -199,8 +149,8 @@ msgid "W14: Warning: List of file names overflow" msgstr "W14: Uyarı: Dosya adları taşımı" #, c-format -msgid "E92: Buffer %d not found" -msgstr "E92: %d numaralı arabellek bulunamadı" +msgid "E92: Buffer %<PRId64> not found" +msgstr "E92: %<PRId64> numaralı arabellek bulunamadı" #, c-format msgid "E93: More than one match for %s" @@ -211,8 +161,8 @@ msgid "E94: No matching buffer for %s" msgstr "E94: %s için eşleşen arabellek yok" #, c-format -msgid "line %ld" -msgstr "%ld. satır" +msgid "line %<PRId64>" +msgstr "%<PRId64>. satır" msgid "E95: Buffer with this name already exists" msgstr "E95: Aynı adda bir arabellek hâlihazırda var" @@ -233,14 +183,8 @@ msgid "[readonly]" msgstr "[saltokunur]" #, c-format -msgid "%ld line --%d%%--" -msgid_plural "%ld lines --%d%%--" -msgstr[0] "%ld. satır --%d%%" -msgstr[1] "%ld. satır --%d%%" - -#, c-format -msgid "line %ld of %ld --%d%%-- col " -msgstr "satır %ld/%ld --%d%%-- sütun " +msgid "line %<PRId64> of %<PRId64> --%d%%-- col " +msgstr "satır %<PRId64>/%<PRId64> --%d%%-- sütun " msgid "[No Name]" msgstr "[Adsız]" @@ -269,270 +213,41 @@ msgstr "E382: Yazılamıyor, 'buftype' seçeneği ayarlanmamış" msgid "[Prompt]" msgstr "[İstem]" -msgid "[Popup]" -msgstr "[Açılır pencere]" - msgid "[Scratch]" msgstr "[Geçici alan]" -msgid "WARNING: The file has been changed since reading it!!!" -msgstr "UYARI: Bu dosya açıldıktan sonra başkası tarafından değiştirilmiş!!!" - -msgid "Do you really want to write to it" -msgstr "Yine de yazmak istiyor musunuz?" - -msgid "[New]" -msgstr "[Yeni]" - -msgid "[New File]" -msgstr "[Yeni Dosya]" - -msgid "E676: No matching autocommands for acwrite buffer" -msgstr "E676: acwrite arabelleği için eşleşen bir otokomut yok" - -msgid "E203: Autocommands deleted or unloaded buffer to be written" -msgstr "E203: Otokomutlar arabelleği silmiş veya yazılması için kaldırmışlar" - -msgid "E204: Autocommand changed number of lines in unexpected way" -msgstr "E204: Otokomut satır sayısını beklenmeyen biçimde değiştirdi" - -msgid "NetBeans disallows writes of unmodified buffers" -msgstr "NetBeans değiştirilmemiş arabelleklerin yazılmasına izin vermez" - -msgid "Partial writes disallowed for NetBeans buffers" -msgstr "NetBeans arabellekleri için kısmî yazmalara izin verilmez" - -msgid "is a directory" -msgstr "bir dizin" - -msgid "is not a file or writable device" -msgstr "bir dosya veya yazılabilir aygıt değil" - -msgid "writing to device disabled with 'opendevice' option" -msgstr "aygıta yazma 'opendevice' seçeneği ile kapatılmış" - -msgid "is read-only (add ! to override)" -msgstr "saltokunur (geçersiz kılmak için ! ekleyin)" - -msgid "E506: Can't write to backup file (add ! to override)" -msgstr "E506: Yedek dosyasına yazılamıyor (geçersiz kılmak için ! ekleyin)" - -msgid "E507: Close error for backup file (add ! to override)" -msgstr "E507: Yedek dosya için kapatma hatası (geçersiz kılmak için ! ekleyin)" - -msgid "E508: Can't read file for backup (add ! to override)" -msgstr "E508: Yedek dosyası okunamıyor (geçersiz kılmak için ! ekleyin)" - -msgid "E509: Cannot create backup file (add ! to override)" -msgstr "E509: Yedek dosyası oluşturulamıyor (geçersiz kılmak için ! ekleyin)" - -msgid "E510: Can't make backup file (add ! to override)" -msgstr "E510: Yedek dosyası yapılamıyor (geçersiz kılmak için ! ekleyin) " - -msgid "E214: Can't find temp file for writing" -msgstr "E214: Yazma için geçici dosya bulunamıyor" - -msgid "E213: Cannot convert (add ! to write without conversion)" -msgstr "E213: Dönüştürülemiyor (dönüştürmeden yazmak için ! ekleyin)" - -msgid "E166: Can't open linked file for writing" -msgstr "E166: Bağlı dosya yazma için açılamıyor" - -msgid "E212: Can't open file for writing" -msgstr "E212: Dosya yazma için açılamıyor" - -msgid "E949: File changed while writing" -msgstr "E949: Dosya yazma sırasında değiştirildi" - -msgid "E512: Close failed" -msgstr "E512: Kapatma başarısız oldu" - -msgid "E513: write error, conversion failed (make 'fenc' empty to override)" -msgstr "" -"E513: Yazma hatası, dönüştürme başarısız (geçersiz kılmak için 'fenc'i boş " -"bırakın)" - -#, c-format -msgid "" -"E513: write error, conversion failed in line %ld (make 'fenc' empty to " -"override)" -msgstr "" -"E513: Yazma hatası, %ld. satırda dönüştürme başarısız (geçersiz kılmak için " -"'fenc'i boş bırakın)" - -msgid "E514: write error (file system full?)" -msgstr "E514: Yazma hatası (dosya sistemi dolu mu?)" - -msgid " CONVERSION ERROR" -msgstr " DÖNÜŞTÜRME HATASI" - -#, c-format -msgid " in line %ld;" -msgstr " %ld. satırda;" - -msgid "[NOT converted]" -msgstr "[dönüştürülmedi]" - -msgid "[converted]" -msgstr "[dönüştürüldü]" - -msgid "[Device]" -msgstr "[Aygıt]" - -msgid " [a]" -msgstr " [a]" - -msgid " appended" -msgstr " iliştirildi" - -msgid " [w]" -msgstr " [w]" - -msgid " written" -msgstr " yazıldı" - -msgid "E205: Patchmode: can't save original file" -msgstr "E205: Yama kipi: Orijinal dosya kaydedilemiyor" - -msgid "E206: patchmode: can't touch empty original file" -msgstr "E206: Yama kipi: Orijinal boş dosyaya dokunulamıyor" - -msgid "E207: Can't delete backup file" -msgstr "E207: Yedek dosyası silinemiyor" - -msgid "" -"\n" -"WARNING: Original file may be lost or damaged\n" -msgstr "" -"\n" -"UYARI: Orijinal dosya kaybolmuş veya hasar görmüş olabilir\n" - -msgid "don't quit the editor until the file is successfully written!" -msgstr "dosya başarılı bir biçimde yazılana kadar düzenleyiciden çıkmayın!" - msgid "W10: Warning: Changing a readonly file" msgstr "W10: Uyarı: Saltokunur bir dosya değiştiriliyor" -msgid "E902: Cannot connect to port" -msgstr "E902: Kapıya bağlanılamıyor" - -msgid "E898: socket() in channel_connect()" -msgstr "E898: channel_connect() içinde socket()" - -#, c-format -msgid "E901: getaddrinfo() in channel_open(): %s" -msgstr "E901: channel_open() içinde getaddrinfo(): %s" - -msgid "E901: gethostbyname() in channel_open()" -msgstr "E901: channel_open() içinde gethostbyname()" - -msgid "E903: received command with non-string argument" -msgstr "E903: Dizi olmayan argüman içeren komut alındı" - -msgid "E904: last argument for expr/call must be a number" -msgstr "E904: İfadenin/çağrının son argüman bir sayı olmalıdır" - -msgid "E904: third argument for call must be a list" -msgstr "E904: Çağrının üçüncü argümanı bir liste olmalıdır" - -#, c-format -msgid "E905: received unknown command: %s" -msgstr "E905: Bilinmeyen komut alındı: %s" - -msgid "E906: not an open channel" -msgstr "E906: Açık bir kanal değil" - -#, c-format -msgid "E630: %s(): write while not connected" -msgstr "E630: %s(): Bağlantı yokken yazım" - -#, c-format -msgid "E631: %s(): write failed" -msgstr "E631: %s(): Yazma başarısız" - -#, c-format -msgid "E917: Cannot use a callback with %s()" -msgstr "E917: %s() ile geri çağırma kullanılamıyor" +msgid "can only be opened in headless mode" +msgstr "yalnızca başsız kipte açılabilir" -msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" -msgstr "E912: ch_evalexpr()/ch_sendexpr() raw/nl kanalları ile kullanılamaz" - -msgid "No display" -msgstr "Görüntü yok" - -msgid ": Send failed.\n" -msgstr ": Gönderme başarısız oldu.\n" - -msgid ": Send failed. Trying to execute locally\n" -msgstr ": Gönderme başarısız oldu. Yerel ortamda çalıştırma deneniyor\n" - -#, c-format -msgid "%d of %d edited" -msgstr "%d/%d düzenlendi" +msgid "channel was already open" +msgstr "kanal halihazırda açıktı" -msgid "No display: Send expression failed.\n" -msgstr "Görüntü yok: Gönderme başarısız oldu.\n" +msgid "Can't send data to closed stream" +msgstr "Kapalı akışa veri gönderilemez" -msgid ": Send expression failed.\n" -msgstr ": Gönderme başarısız oldu.\n" +msgid "Can't send raw data to rpc channel" +msgstr "rpc kanalına ham veri gönderilemez" -msgid "E240: No connection to the X server" -msgstr "E240: X sunucusuna bağlanılamadı" +msgid "E474: Failed to convert list to msgpack string buffer" +msgstr "E474: Liste, msgpack dizi arabelleğine dönüştürülemedi" -#, c-format -msgid "E241: Unable to send to %s" -msgstr "E241: Şuraya gönderilemedi: %s" - -msgid "E277: Unable to read a server reply" -msgstr "E277: Bir sunucu yanıtı okunamadı" - -msgid "E941: already started a server" -msgstr "E941: Sunucu hâlihazırda çalışıyor" - -msgid "E942: +clientserver feature not available" -msgstr "E942: +clientserver özelliği mevcut değil" - -msgid "E258: Unable to send to client" -msgstr "E258: İstemciye gönderilemiyor" - -msgid "Used CUT_BUFFER0 instead of empty selection" -msgstr "Boş seçim yerine CUT_BUFFER0 kullanıldı" - -msgid "tagname" -msgstr "etiket adı" - -msgid " kind file\n" -msgstr " dosya türü\n" - -msgid "'history' option is zero" -msgstr "'history' seçeneği sıfır" - -msgid "E821: File is encrypted with unknown method" -msgstr "E821: Dosya bilinmeyen bir yöntemle şifrelenmiş" - -msgid "Warning: Using a weak encryption method; see :help 'cm'" -msgstr "" -"Uyarı: Zayıf bir şifreleme yöntemi kullanılıyor; bilgi için: :help 'cm'" - -msgid "" -"Note: Encryption of swapfile not supported, disabling swap- and undofile" -msgstr "Takas dosyası şifrelemesi desteklenmiyor, takas ve geri al dosyası " -"devre dışı bırakılıyor" - -msgid "Enter encryption key: " -msgstr "Şifreleme anahtarı girin: " +msgid "E545: Missing colon" +msgstr "E545: İki nokta eksik" -msgid "Enter same key again: " -msgstr "Aynı anahtarı yeniden girin: " +msgid "E546: Illegal mode" +msgstr "E546: İzin verilmeyen kip" -msgid "Keys don't match!" -msgstr "Anahtarlar eşleşmiyor!" +msgid "E548: digit expected" +msgstr "E548: Basamak bekleniyordu" -msgid "[crypted]" -msgstr "[şifreli]" +msgid "E549: Illegal percentage" +msgstr "E549: İzin verilmeyen yüzde" msgid "Entering Debug mode. Type \"cont\" to continue." -msgstr "Hata Ayıklama kipine giriliyor. Sürdürmek için \"cont\" yazın." +msgstr "Hata ayıklama kipine giriliyor. Sürdürmek için \"cont\" yazın." #, c-format msgid "Oldval = \"%s\"" @@ -544,8 +259,8 @@ msgid "Newval = \"%s\"" msgstr "Yeni değer = \"%s\"" #, c-format -msgid "line %ld: %s" -msgstr "%ld. satır: %s" +msgid "line %<PRId64>: %s" +msgstr "%<PRId64>. satır: %s" #, c-format msgid "cmd: %s" @@ -559,8 +274,8 @@ msgid "frame at highest level: %d" msgstr "çerçeve en yüksek düzeyde: %d" #, c-format -msgid "Breakpoint in \"%s%s\" line %ld" -msgstr "\"%s%s\" içinde kesme noktası, %ld. satır" +msgid "Breakpoint in \"%s%s\" line %<PRId64>" +msgstr "\"%s%s\" içinde kesme noktası, %<PRId64>. satır" #, c-format msgid "E161: Breakpoint not found: %s" @@ -570,23 +285,16 @@ msgid "No breakpoints defined" msgstr "Hiçbir kesme noktası tanımlanmamış" #, c-format -msgid "%3d %s %s line %ld" -msgstr "%3d %s %s %ld. satır" +msgid "%3d %s %s line %<PRId64>" +msgstr "%3d %s %s %<PRId64>. satır" #, c-format msgid "%3d expr %s" msgstr "%3d ifade %s" -msgid "extend() argument" -msgstr "extend() argümanı" - #, c-format -msgid "E737: Key already exists: %s" -msgstr "E737: Anahtar hâlihazırda var: %s" - -#, c-format -msgid "E96: Cannot diff more than %d buffers" -msgstr "E96: %d arabellekten fazlasında karşılaştırma yapılamıyor" +msgid "E96: Cannot diff more than %<PRId64> buffers" +msgstr "E96: %<PRId64> arabellekten fazlasında karşılaştırma yapılamıyor" #, c-format msgid "Not enough memory to use internal diff for buffer \"%s\"" @@ -601,18 +309,12 @@ msgstr "E97: Karşılaştırma yapılamıyor" msgid "E960: Problem creating the internal diff" msgstr "E960: Karşılaştırma hazırlanırken sorun" -msgid "Patch file" -msgstr "Yama dosyası" - msgid "E816: Cannot read patch output" msgstr "E816: Yama çıktısı okunamıyor" msgid "E98: Cannot read diff output" msgstr "E98: Karşılaştırma çıktısı okunamıyor" -msgid "E959: Invalid diff format." -msgstr "E959: Geçersiz karşılaştırma biçimi" - msgid "E99: Current buffer is not in diff mode" msgstr "E99: Şu anki arabellek karşılaştırma kipinde değil" @@ -639,6 +341,19 @@ msgstr "E103: Arabellek \"%s\" karşılaştırma kipinde değil" msgid "E787: Buffer changed unexpectedly" msgstr "E787: Arabellek beklenmeyen bir biçimde değiştirildi" +#, c-format +msgid "E1214: Digraph must be just two characters: %s" +msgstr "E1214: İkili harf yalnızca iki karakter olmalıdır: %s" + +#, c-format +msgid "E1215: Digraph must be one character: %s" +msgstr "E1215: İkili harf tek bir karakter olmalıdır: %s" + +msgid "" +"E1216: digraph_setlist() argument must be a list of lists with two items" +msgstr "" +"E1216: digraph_setlist() argümanı iki ögeli listelerin bir listesi olmalıdır" + msgid "E104: Escape not allowed in digraph" msgstr "E104: Kaçan, ikili harflerde kullanılamaz" @@ -726,6 +441,186 @@ msgstr "E105: :loadkeymap kaynak alınmayan bir dosyada kullanılıyor" msgid "E791: Empty keymap entry" msgstr "E791: Boş düğme eşlem girdisi" +msgid " Keyword completion (^N^P)" +msgstr " Anahtar sözcük tamamlaması (^N^P)" + +msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" +msgstr " ^X kipi (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" + +msgid " Whole line completion (^L^N^P)" +msgstr " Tam satır tamamlaması (^L^N^P)" + +msgid " File name completion (^F^N^P)" +msgstr " Dosya adı tamamlaması (^F^N^P)" + +msgid " Tag completion (^]^N^P)" +msgstr " Etiket tamamlaması (^]^N^P)" + +msgid " Path pattern completion (^N^P)" +msgstr " Yol dizgisi tamamlaması (^N^P)" + +msgid " Definition completion (^D^N^P)" +msgstr " Tanım tamamlaması (^D^N^P)" + +msgid " Dictionary completion (^K^N^P)" +msgstr " Sözlük tamamlaması (^K^N^P)" + +msgid " Thesaurus completion (^T^N^P)" +msgstr " Eşanlamlılar sözlüğü tamamlaması (^T^N^P)" + +msgid " Command-line completion (^V^N^P)" +msgstr " Komut satırı tamamlaması (^V^N^P)" + +msgid " User defined completion (^U^N^P)" +msgstr " Kullanıcı tanımlı tamamlamalar (^U^N^P)" + +msgid " Omni completion (^O^N^P)" +msgstr " Omni tamamlaması (^O^N^P)" + +msgid " Spelling suggestion (s^N^P)" +msgstr " Yazım önerisi (s^N^P)" + +msgid " Keyword Local completion (^N^P)" +msgstr " Dahili anahtar sözcük tamamlaması (^N^P)" + +msgid "Hit end of paragraph" +msgstr "Paragrafın sonuna varıldı" + +msgid "E839: Completion function changed window" +msgstr "E839: Tamamlama işlevi pencereyi değiştirdi" + +msgid "E840: Completion function deleted text" +msgstr "E840: Tamamlama işlevi metni sildi" + +msgid "'dictionary' option is empty" +msgstr "'dictionary' seçeneği boş" + +msgid "'thesaurus' option is empty" +msgstr "'thesaurus' seçeneği boş" + +#, c-format +msgid "Scanning dictionary: %s" +msgstr "Sözlük taranıyor: %s" + +msgid " (insert) Scroll (^E/^Y)" +msgstr " (ekle) Kaydır (^E/^Y)" + +msgid " (replace) Scroll (^E/^Y)" +msgstr " (değiştir) Kaydır (^E/^Y)" + +#, c-format +msgid "Scanning: %s" +msgstr "Taranıyor: %s" + +msgid "Scanning tags." +msgstr "Etiketler taranıyor..." + +msgid "match in file" +msgstr "dosya içinde eşleşme" + +msgid " Adding" +msgstr " Ekleniyor" + +msgid "-- Searching..." +msgstr "-- Aranıyor..." + +msgid "Back at original" +msgstr "Başlangıca geri dönüldü" + +msgid "Word from other line" +msgstr "Sözcük diğer satırdan" + +msgid "The only match" +msgstr "Tek eşleşen" + +#, c-format +msgid "match %d of %d" +msgstr "eşleşme %d/%d" + +#, c-format +msgid "match %d" +msgstr "eşleşme %d" + +msgid "E18: Unexpected characters in :let" +msgstr "E18: :let içinde beklenmeyen karakter" + +msgid "E111: Missing ']'" +msgstr "E111: ']' eksik" + +msgid "E719: Cannot use [:] with a Dictionary" +msgstr "E719: [:], bir Sözlük ile kullanılamaz" + +#, c-format +msgid "E461: Illegal variable name: %s" +msgstr "E461: İzin verilmeyen değişken adı: %s" + +msgid "E995: Cannot modify existing variable" +msgstr "E995: Mevcut değişken değiştirilemiyor" + +msgid "E274: No white space allowed before parenthesis" +msgstr "E274: Ayraçtan önce boşluğa izin verilmiyor" + +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: Değişken %s kilitlenemiyor veya açılamıyor" + +#, c-format +msgid "E80: Error while writing: %s" +msgstr "E80: Yazma sırasında hata: %s" + +msgid "E1098: String, List or Blob required" +msgstr "E1098: Dizi, Liste veya İkili Nesne gerekiyor" + +#, c-format +msgid "E734: Wrong variable type for %s=" +msgstr "E734: %s= için yanlış değişken türü" + +msgid "" +"E5700: Expression from 'spellsuggest' must yield lists with exactly two " +"values" +msgstr "" +"E5700: 'spellsuggest'ten olan ifade, yalnızca iki değerli listelere izin " +"vermelidir" + +msgid "E991: cannot use =<< here" +msgstr "E991: Burada =<< kullanılamaz" + +msgid "E221: Marker cannot start with lower case letter" +msgstr "E221: İmleyici küçük harfle başlayamaz" + +msgid "E172: Missing marker" +msgstr "E172: İmleyici eksik" + +#, c-format +msgid "E990: Missing end marker '%s'" +msgstr "E990: Son imleyici '%s' eksik" + +msgid "E687: Less targets than List items" +msgstr "E687: Liste ögelerinden daha az hedef var" + +msgid "E688: More targets than List items" +msgstr "E688: Liste ögelerinden daha fazla hedef var" + +msgid "E452: Double ; in list of variables" +msgstr "E452: Değişkenler listesinde çifte ;" + +#, c-format +msgid "E738: Can't list variables for %s" +msgstr "E738: %s için değişkenler listelenemiyor" + +msgid "E996: Cannot lock an environment variable" +msgstr "E996: Ortam değişkeni kilitlenemiyor" + +msgid "E996: Cannot lock an option" +msgstr "E996: Seçenek kilitlenemiyor" + +msgid "E996: Cannot lock a register" +msgstr "E996: Yazmaç kilitlenemiyor" + +#, c-format +msgid "E121: Undefined variable: %.*s" +msgstr "E121: Tanımlanmamış değişken: %.*s" + msgid "E689: Can only index a List, Dictionary or Blob" msgstr "" "E689: Yalnızca bir liste, sözlük, veya ikili geniş nesne dizinlenebilir" @@ -733,26 +628,144 @@ msgstr "" msgid "E708: [:] must come last" msgstr "E708: [:] en son gelmelidir" +msgid "E713: Cannot use empty key after ." +msgstr "E713: . sonrası boş anahtar kullanılamaz" + msgid "E709: [:] requires a List or Blob value" msgstr "E709: [:] bir liste veya ikili geniş nesne değeri gerektirir" +msgid "E972: Blob value does not have the right number of bytes" +msgstr "E972: İkili geniş nesne değeri doğru bayt sayısına sahip değil" + msgid "E996: Cannot lock a range" msgstr "E996: Erim kilitlenemiyor" +msgid "E710: List value has more items than target" +msgstr "E710: Liste değeri hedeften daha fazla ögeye sahip" + +msgid "E711: List value has not enough items" +msgstr "E711: Liste değeri yeterli ögeye sahip değil" + msgid "E996: Cannot lock a list or dict" msgstr "E996: Bir liste veya sözlük kilitlenemiyor" +msgid "E690: Missing \"in\" after :for" +msgstr "E690: :for sonrası \"in\" eksik" + +#, c-format +msgid "E108: No such variable: \"%s\"" +msgstr "E108: Böyle bir değişken yok: \"%s\"" + +msgid "E109: Missing ':' after '?'" +msgstr "E109: '?' sonrası ':' eksik" + +msgid "E804: Cannot use '%' with Float" +msgstr "E804: Bir kayan noktalı değer ile '%' kullanılamaz" + +msgid "E973: Blob literal should have an even number of hex characters" +msgstr "" +"E973: İkili geniş nesne hazır bilgisi çift onalt. karakterlere iye olmalıdır" + +msgid "E110: Missing ')'" +msgstr "E110: ')' eksik" + msgid "E260: Missing name after ->" msgstr "E260: -> sonrası ad eksik" msgid "E695: Cannot index a Funcref" msgstr "E695: Bir Funcref dizinlenemiyor" +msgid "E909: Cannot index a special variable" +msgstr "E909: Özel bir değişken dizinlenemiyor" + +#, c-format +msgid "E112: Option name missing: %s" +msgstr "E112: Seçenek adı eksik: %s" + +#, c-format +msgid "E113: Unknown option: %s" +msgstr "E113: Bilinmeyen seçenek: %s" + +#, c-format +msgid "E114: Missing quote: %s" +msgstr "E114: Tırnak eksik: %s" + +#, c-format +msgid "E115: Missing quote: %s" +msgstr "E115: Tırnak eksik: %s" + +#, c-format +msgid "E696: Missing comma in List: %s" +msgstr "E696: Listede virgül eksik: %s" + +#, c-format +msgid "E697: Missing end of List ']': %s" +msgstr "E697: Liste sonunda ']' eksik: %s" + msgid "Not enough memory to set references, garbage collection aborted!" msgstr "Referansları ayarlamak için yetersiz bellek, atık toplama durduruldu" -msgid "E724: variable nested too deep for displaying" -msgstr "E724: Değişken çok iç içe geçtiğinden görüntülenemiyor" +#, c-format +msgid "E720: Missing colon in Dictionary: %s" +msgstr "E720: Sözlükte iki nokta eksik: %s" + +#, c-format +msgid "E721: Duplicate key in Dictionary: \"%s\"" +msgstr "E721: Sözlükte yinelenmiş anahtar: \"%s\"" + +#, c-format +msgid "E722: Missing comma in Dictionary: %s" +msgstr "E722: Sözlükte virgül eksik: %s" + +#, c-format +msgid "E723: Missing end of Dictionary '}': %s" +msgstr "E723: Sözlük sonu '}' eksik: %s" + +msgid "map() argument" +msgstr "map() argümanı" + +msgid "filter() argument" +msgstr "filter() argümanı" + +#, c-format +msgid "E700: Unknown function: %s" +msgstr "E700: Bilinmeyen işlev: %s" + +msgid "E922: expected a dict" +msgstr "E922: Bir sözlük bekleniyordu" + +msgid "E923: Second argument of function() must be a list or a dict" +msgstr "E923: function() ikinci argümanı bir liste veya sözlük olmalıdır" + +msgid "E5050: {opts} must be the only argument" +msgstr "E5050: {opts}, tek argüman olmalıdır" + +#, c-format +msgid "Executing command: \"%s\"" +msgstr "Komut çalıştırılıyor: \"%s\"" + +msgid "E921: Invalid callback argument" +msgstr "E921: Geçersiz geri çağırma argümanı" + +#, c-format +msgid "E963: setting %s to value with wrong type" +msgstr "E963: %s yanlış türe sahip değere ayarlanıyor" + +#, c-format +msgid "E794: Cannot set variable in the sandbox: \"%.*s\"" +msgstr "E794: Kum havuzunda değişken ayarlanamaz: \"%.*s\"" + +#, c-format +msgid "E795: Cannot delete variable %.*s" +msgstr "E795: %.*s değişkeni silinemiyor" + +#, c-format +msgid "E704: Funcref variable name must start with a capital: %s" +msgstr "E704: Funcref değişkeni BÜYÜK harf ile başlamalıdır: %s" + +#, c-format +msgid "E705: Variable name conflicts with existing function: %s" +msgstr "E705: Değişken adı mevcut işlevle çakışıyor: %s" msgid "E698: variable nested too deep for making a copy" msgstr "E698: Değişken kopyalama için çok iç içe geçmiş" @@ -764,127 +777,705 @@ msgstr "" "\n" "\tEn son şuradan ayarlandı: " +msgid "E5009: $VIMRUNTIME is empty or unset" +msgstr "E5009: $VIMRUNTIME boş veya ayarlanmamış" + +#, c-format +msgid "E5009: Invalid $VIMRUNTIME: %s" +msgstr "E5009: Geçersiz VIMRUNTIME: %s" + +msgid "E5009: Invalid 'runtimepath'" +msgstr "E5009: Geçersiz 'runtimepath'" + +msgid "E977: Can only compare Blob with Blob" +msgstr "" +"E977: Bir ikili geniş öğe yalnızca kendinden bir başkası ile " +"karşılaştırılabilir" + +msgid "E691: Can only compare List with List" +msgstr "E691: Bir liste yalnızca başka bir liste ile karşılaştırılabilir" + +msgid "E692: Invalid operation for List" +msgstr "E692: Geçersiz liste işlemi" + +msgid "E735: Can only compare Dictionary with Dictionary" +msgstr "E735: Bir sözlük yalnızca başka bir sözlük ile karşılaştırılabilir" + +msgid "E736: Invalid operation for Dictionary" +msgstr "E736: Geçersiz sözlük işlemi" + +msgid "E694: Invalid operation for Funcrefs" +msgstr "E694: Geçersiz Funcref işlemi" + +#, c-format +msgid "E474: Expected comma before list item: %s" +msgstr "E474: Liste ögesi öncesi virgül bekleniyordu: %s" + +#, c-format +msgid "E474: Expected colon before dictionary value: %s" +msgstr "E474: Sözlük değeri öncesi iki nokta bekleniyordu: %s" + +#, c-format +msgid "E474: Expected string key: %s" +msgstr "E474: Dizi anahtarı bekleniyordu: %s" + +#, c-format +msgid "E474: Expected comma before dictionary key: %s" +msgstr "E474: Sözlük anahtarı öncesi virgül bekleniyordu: %s" + +#, c-format +msgid "E474: Unfinished escape sequence: %.*s" +msgstr "E474: Bitirilmemiş kaçış sırası: %.*s" + +#, c-format +msgid "E474: Unfinished unicode escape sequence: %.*s" +msgstr "E474: Tamamlanmamış Unicode kaçış sıralaması: %.*s" + +#, c-format +msgid "E474: Expected four hex digits after \\u: %.*s" +msgstr "E474: \\u sonrası dört onaltılık basamak bekleniyordu: %.*s" + +#, c-format +msgid "E474: Unknown escape sequence: %.*s" +msgstr "E474: Bilinmeyen kaçış sıralaması: %.*s" + +#, c-format +msgid "E474: ASCII control characters cannot be present inside string: %.*s" +msgstr "E474: ASCII denetim karakterleri, dizi içinde var olamaz: %.*s" + +#, c-format +msgid "E474: Only UTF-8 strings allowed: %.*s" +msgstr "E474: Yalnızca UTF-8 dizilere izin verilir: %.*s" + +#, c-format +msgid "" +"E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: " +"%.*s" +msgstr "" +"E474: Yalnızca U+10FFFF'e kadar olan UTF-8 kod noktalarına kaçışsız " +"var olma izni verilir: %.*s" + +#, c-format +msgid "E474: Expected string end: %.*s" +msgstr "E474: Dizi sonu bekleniyordu: %.*s" + +#, c-format +msgid "E474: Leading zeroes are not allowed: %.*s" +msgstr "E474: Öncü sıfırlara zin verilmiyor: %.*s" + +#, c-format +msgid "E474: Missing number after minus sign: %.*s" +msgstr "E474: Eksi imi sonrası sayı eksik: %.*s" + +#, c-format +msgid "E474: Missing number after decimal dot: %.*s" +msgstr "E474: Ondalık noktası sonrası sayı eksik: %.*s" + +#, c-format +msgid "E474: Missing exponent: %.*s" +msgstr "E474: Argüman eksik: %.*s" + +#, c-format +msgid "" +"E685: internal error: while converting number \"%.*s\" to float string2float " +"consumed %zu bytes in place of %zu" +msgstr "" +"E685: İçsel hata: \"%.*s\" sayısı kayan noktalı değere dönüştürülürken " +"string2float %zu bayt harcadı, %zu bayt harcaması gerekiyordu" + +#, c-format +msgid "" +"E685: internal error: while converting number \"%.*s\" to integer vim_str2nr " +"consumed %i bytes in place of %zu" +msgstr "" +"E685: İçsel hata: \"%.*s\" sayısı tamsayıya dönüştürülürken " +"vim_str2nr %i bayt harcadı, %zu bayt harcaması gerekiyordu" + +msgid "E474: Attempt to decode a blank string" +msgstr "E474: Boş bir dizinin kodu çözülmeye çalışılıyor" + +#, c-format +msgid "E474: No container to close: %.*s" +msgstr "E474: Kapatılacak kapsayıcı yok: %.*s" + +#, c-format +msgid "E474: Closing list with curly bracket: %.*s" +msgstr "E474: Liste, kıvrımlı ayraçla kapatılıyor: %.*s" + +#, c-format +msgid "E474: Closing dictionary with square bracket: %.*s" +msgstr "E474: Sözlük, bir kare ayraç ile kapatılıyor: %.*s" + +#, c-format +msgid "E474: Trailing comma: %.*s" +msgstr "E474: Sonda virgül: %.*s" + +#, c-format +msgid "E474: Expected value after colon: %.*s" +msgstr "E474: İki nokta sonrası değer bekleniyordu: %.*s" + +#, c-format +msgid "E474: Expected value: %.*s" +msgstr "E474: Değer bekleniyordu: %.*s" + +#, c-format +msgid "E474: Comma not inside container: %.*s" +msgstr "E474: Virgül, kapsayıcı içinde değil: %.*s" + +#, c-format +msgid "E474: Duplicate comma: %.*s" +msgstr "E474: Yinelenen virgül: %.*s" + +#, c-format +msgid "E474: Comma after colon: %.*s" +msgstr "E474: İki nokta sonrası virgül: %.*s" + +#, c-format +msgid "E474: Using comma in place of colon: %.*s" +msgstr "E474: İki nokta yerine virgül kullanılıyor: %.*s" + +#, c-format +msgid "E474: Leading comma: %.*s" +msgstr "E474: Öncü virgül: %.*s" + +#, c-format +msgid "E474: Colon not inside container: %.*s" +msgstr "E474: İki nokta, kapsayıcı içinde değil: %.*s" + +#, c-format +msgid "E474: Using colon not in dictionary: %.*s" +msgstr "E474: Sözlük dışında iki nokta kullanılıyor: %.*s" + +#, c-format +msgid "E474: Unexpected colon: %.*s" +msgstr "E474: Beklenmedik iki nokta: %.*s" + +#, c-format +msgid "E474: Colon after comma: %.*s" +msgstr "E474: Virgül sonrası iki nokta: %.*s" + +#, c-format +msgid "E474: Duplicate colon: %.*s" +msgstr "E474: Yinelenen iki nokta: %.*s" + +#, c-format +msgid "E474: Expected null: %.*s" +msgstr "E474: null bekleniyordu: %.*s" + +#, c-format +msgid "E474: Expected true: %.*s" +msgstr "E474: true bekleniyordu: %.*s" + +#, c-format +msgid "E474: Expected false: %.*s" +msgstr "E474: false bekleniyordu: %.*s" + +#, c-format +msgid "E474: Unidentified byte: %.*s" +msgstr "E474: Tanımlanmamış bayt: %.*s" + +#, c-format +msgid "E474: Trailing characters: %.*s" +msgstr "E474: Sonda fazladan karakterler: %.*s" + +#, c-format +msgid "E474: Unexpected end of input: %.*s" +msgstr "E474: Beklenmedik girdi sonu: %.*s" + +#, c-format +msgid "key %s" +msgstr "%s anahtarı" + +#, c-format +msgid "key %s at index %i from special map" +msgstr "özel eşlemden %s anahtarı, %i indeksinde" + +#, c-format +msgid "index %i" +msgstr "%i indeksi" + +msgid "partial" +msgstr "kısımsal" + +#, c-format +msgid "argument %i" +msgstr "%i argümanı" + +msgid "partial self dictionary" +msgstr "kısımsal öz sözlük" + +msgid "itself" +msgstr "kendisi" + +msgid "E724: unable to correctly dump variable with self-referencing container" +msgstr "E724: Özüne başvuran kapsayıcı ile değişken düzgünce dökülemiyor" + +msgid "E474: Unable to represent NaN value in JSON" +msgstr "E474: JSON içinde NaN değer düzgünce temsil edilemiyor" + +msgid "E474: Unable to represent infinity in JSON" +msgstr "E474: JSON içinde sonsuzluk temsil edilemiyor" + +#, c-format +msgid "" +"E474: String \"%.*s\" contains byte that does not start any UTF-8 character" +msgstr "E474: \"%.*s\" dizisi, herhangi bir UTF-8 karakter başlatmayan bayt " +"içeriyor" + +#, c-format +msgid "" +"E474: UTF-8 string contains code point which belongs to a surrogate pair: " +"%.*s" +msgstr "E474: UTF-8 dizisi, bir vekil çifte iye olan kod noktası içeriyor: " +"%.*s" + +msgid "E474: Unable to convert EXT string to JSON" +msgstr "E474: EXT dizisi, JSON'a dönüştürülemiyor" + +#, c-format +msgid "E474: Error while dumping %s, %s: attempt to dump function reference" +msgstr "E474: %s dökülürken hata, %s: İşlev başvurusu dökme girişimi" + +msgid "E474: Invalid key in special dictionary" +msgstr "E474: Özel sözlükte geçersiz anahtar" + +msgid "encode_tv2string() argument" +msgstr "encode_tv2string() argümanı" + +msgid ":echo argument" +msgstr ":echo argümanı" + +msgid "encode_tv2json() argument" +msgstr "encode_tv2json() argümanı" + +#, c-format +msgid "E5004: Error while dumping %s, %s: attempt to dump function reference" +msgstr "E5004: %s dökülürken hata, %s: İşlev başvurusu dökme girişimi" + +#, c-format +msgid "E5005: Unable to dump %s: container references itself in %s" +msgstr "E5005: %s dökülemiyor: Kapsayıcı, %s içinde özüne başvuruyor" + +#, c-format +msgid "E684: list index out of range: %<PRId64>" +msgstr "E684: Liste indeksi erim dışında: %<PRId64>" + +#, c-format +msgid "E899: Argument of %s must be a List or Blob" +msgstr "E899: %s argümanı bir liste veya ikili geniş nesne olmalıdır" + +msgid "E957: Invalid window number" +msgstr "E957: Geçersiz pencere numarası" + +#, c-format +msgid "E998: Reduce of an empty %s with no initial value" +msgstr "E998: Başlangıç değeri olmayan boş bir %s için reduce() yapılamıyor" + +#, c-format +msgid "Error converting the call result: %s" +msgstr "Çağrı sonuçlarını dönüştürürken hata: %s" + +msgid "add() argument" +msgstr "add() argümanı" + #, c-format msgid "E158: Invalid buffer name: %s" msgstr "E158: Geçersiz arabellek adı: %s" +#, c-format +msgid "Invalid channel stream \"%s\"" +msgstr "Geçersiz kanal akışı \"%s\"" + +msgid "E785: complete() can only be used in Insert mode" +msgstr "E785: complete() yalnızca Ekleme kipinde kullanılabilir" + msgid "&Ok" msgstr "&Tamam" -msgid "E980: lowlevel input not supported" -msgstr "E980: Alt düzey girdi desteklenmiyor" +msgid "Context stack is empty" +msgstr "Bağlam yığını boş" -#, c-format -msgid "E700: Unknown function: %s" -msgstr "E700: Bilinmeyen işlev: %s" +msgid "dictwatcheradd() argument" +msgstr "dictwatcheradd() argümanı" -msgid "E922: expected a dict" -msgstr "E922: Bir sözlük bekleniyordu" +msgid "E900: maxdepth must be non-negative number" +msgstr "E900: maxdepth negatif olmayan bir sayı olmalı" -msgid "E923: Second argument of function() must be a list or a dict" -msgstr "E923: function() ikinci argümanı bir liste veya sözlük olmalıdır" +msgid "flatten() argument" +msgstr "flatten() argümanı" -msgid "" -"&OK\n" -"&Cancel" -msgstr "" -"&Tamam\n" -"İ&ptal" +msgid "extend() argument" +msgstr "extend() argümanı" + +msgid "E5000: Cannot find tab number." +msgstr "E5000: Sekme numarası bulunamıyor." + +msgid "E5001: Higher scope cannot be -1 if lower scope is >= 0." +msgstr "E5001: Daha yüksek kapsam, düşük kapsam >= 0 ise -1 olamaz." + +msgid "E5002: Cannot find window number." +msgstr "E5002: Pencere numarası bulunamıyor." msgid "called inputrestore() more often than inputsave()" msgstr "inputrestore(), inputsave()'den daha fazla çağrıldı" +msgid "insert() argument" +msgstr "insert() argümanı" + msgid "E786: Range not allowed" msgstr "E786: Erime izin verilmiyor" +msgid "E474: Failed to convert list to string" +msgstr "E474: Liste, diziye dönüştürülemedi" + +#, c-format +msgid "E474: Failed to parse %.*s" +msgstr "E474: %.*s ayrıştırılamadı" + msgid "E701: Invalid type for len()" msgstr "E701: len() için geçersiz tür" +#, c-format +msgid "msgpackdump() argument, index %i" +msgstr "msgpackdump() argümanı, %i indeksi" + +msgid "E5070: Character number must not be less than zero" +msgstr "E5070: Karakter numarası sıfırdan küçük olmamalı" + +#, c-format +msgid "E5071: Character number must not be greater than INT_MAX (%i)" +msgstr "E5071: Karakter numarası INT_MAX (%i) değerinden büyük olmamalı" + msgid "E726: Stride is zero" msgstr "E726: Sıfır adım" msgid "E727: Start past end" msgstr "E727: Başlangıç bitişten sonra" +msgid "<empty>" +msgstr "<boş>" + +msgid "remove() argument" +msgstr "remove() argümanı" + +msgid "E655: Too many symbolic links (cycle?)" +msgstr "E655: Çok fazla sembolik bağlantı (çevrim?)" + +msgid "reverse() argument" +msgstr "reverse() argümanı" + +#, c-format +msgid "E5010: List item %d of the second argument is not a string" +msgstr "E5010: İkinci argümanın %d liste ögesi bir dizi değil" + +#, c-format +msgid "E927: Invalid action: '%s'" +msgstr "E927: Geçersiz eylem: '%s'" + #, c-format msgid "E962: Invalid action: '%s'" msgstr "E962: Geçersiz eylem: '%s'" #, c-format +msgid "connection failed: %s" +msgstr "bağlantı başarısız: %s" + +msgid "sort() argument" +msgstr "sort() argümanı" + +msgid "uniq() argument" +msgstr "uniq() argümanı" + +msgid "E702: Sort compare function failed" +msgstr "E702: Sıralayıp karşılaştırma işlevi başarısız oldu" + +msgid "E882: Uniq compare function failed" +msgstr "E882: Benzersizlik karşılaştırma işlevi başarısız oldu" + +#, c-format +msgid "E6100: \"%s\" is not a valid stdpath" +msgstr "E6100: \"%s\", geçerli bir stdpath değil" + +msgid "(Invalid)" +msgstr "(Geçersiz)" + +#, c-format msgid "E935: invalid submatch number: %d" msgstr "E935: Geçersiz alteşleşme numarası: %d" -msgid "E991: cannot use =<< here" -msgstr "E991: Burada =<< kullanılamaz" +msgid "Can only call this function in an unmodified buffer" +msgstr "Bu işlev yalnızca değiştirilmemiş bir arabellekte çağrılabilir" -msgid "E221: Marker cannot start with lower case letter" -msgstr "E221: İmleyici küçük harfle başlayamaz" - -msgid "E172: Missing marker" -msgstr "E172: İmleyici eksik" +msgid "writefile() first argument must be a List or a Blob" +msgstr "writefile() ilk argümanı bir liste veya ikili geniş nesne olmalıdır" #, c-format -msgid "E990: Missing end marker '%s'" -msgstr "E990: Son imleyici '%s' eksik" +msgid "E5060: Unknown flag: %s" +msgstr "E5060: Bilinmeyen bayrak: %s" -msgid "E985: .= is not supported with script version >= 2" -msgstr "E985: .= betiğin ikinci sürümünden itibaren desteklenmiyor" +msgid "E482: Can't open file with an empty name" +msgstr "E482: Adı boş olan bir dosya açılamaz" -msgid "E687: Less targets than List items" -msgstr "E687: Liste ögelerinden daha az hedef var" +#, c-format +msgid "E482: Can't open file %s for writing: %s" +msgstr "E482: %s dosyası yazma için açılamıyor: %s" -msgid "E688: More targets than List items" -msgstr "E688: Liste ögelerinden daha fazla hedef var" +#, c-format +msgid "E80: Error when closing file %s: %s" +msgstr "E80: %s dosyası kapatılırken hata: %s" -msgid "E452: Double ; in list of variables" -msgstr "E452: Değişkenler listesinde çifte ;" +#, c-format +msgid "E5142: Failed to open file %s: %s" +msgstr "E5142: %s dosyası açılamadı: %s" #, c-format -msgid "E738: Can't list variables for %s" -msgstr "E738: %s için değişkenler listelenemiyor" +msgid "E5143: Failed to write to file %s: %s" +msgstr "E5143: %s dosyası yazılamadı: %s" -msgid "E996: Cannot lock an environment variable" -msgstr "E996: Ortam değişkeni kilitlenemiyor" +#, c-format +msgid "E5144: Failed to close file %s: %s" +msgstr "E5144: %s dosyası kapatılamadı: %s" -msgid "E996: Cannot lock a register" -msgstr "E996: Yazmaç kilitlenemiyor" +msgid "E6000: Argument is not a function or function name" +msgstr "E6000: Argüman bir işlev veya işlev adı değil" #, c-format -msgid "E108: No such variable: \"%s\"" -msgstr "E108: Böyle bir değişken yok: \"%s\"" +msgid "E737: Key already exists: %s" +msgstr "E737: Anahtar hâlihazırda var: %s" msgid "E743: variable nested too deep for (un)lock" msgstr "E743: Değişken kilitlenemez/kilidi açılamaz, çok iç içe geçmiş" #, c-format -msgid "E963: setting %s to value with wrong type" -msgstr "E963: %s yanlış türe sahip değere ayarlanıyor" +msgid "E741: Value is locked: %.*s" +msgstr "E741: Değer kilitli: %.*s" + +#, c-format +msgid "E742: Cannot change value of %.*s" +msgstr "E742: %.*s ögesinin değeri değiştirilemiyor" + +msgid "Unknown" +msgstr "Bilinmiyor" + +msgid "E805: Expected a Number or a String, Float found" +msgstr "E805: Bir sayı veya dizi bekleniyordu, kayan noktalı değer bulundu" + +msgid "E703: Expected a Number or a String, Funcref found" +msgstr "E703: Bir sayı veya dizi bekleniyordu, Funcref bulundu" + +msgid "E745: Expected a Number or a String, List found" +msgstr "E745: Bir sayı veya dizi bekleniyordu, liste bulundu" + +msgid "E728: Expected a Number or a String, Dictionary found" +msgstr "E728: Bir sayı veya dizi bekleniyordu, sözlük bulundu" + +msgid "E974: Expected a Number or a String, Blob found" +msgstr "E974: Bir sayı veya dizi bekleniyordu, ikili geniş nesne bulundu" + +msgid "E5299: Expected a Number or a String, Boolean found" +msgstr "E5299: Bir sayı veya dizi bekleniyordu, Boole bulundu" + +msgid "E5300: Expected a Number or a String" +msgstr "E5300: Bir sayı veya dizi bekleniyordu" + +msgid "E745: Using a List as a Number" +msgstr "E745: Bir sayı olarak liste kullanılıyor" + +msgid "E728: Using a Dictionary as a Number" +msgstr "E728: Bir sayı olarak liste kullanılıyor" + +msgid "E805: Using a Float as a Number" +msgstr "E805: Bir sayı olarak kayan noktalı değer kullanılıyor" + +msgid "E974: Using a Blob as a Number" +msgstr "E974: Bir Sayı olarak ikili geniş nesne kullanılıyor" + +msgid "E685: using an invalid value as a Number" +msgstr "E685: Bir sayı olarak geçersiz bir değer kullanılıyor" + +msgid "E730: using List as a String" +msgstr "E730: Bir dizi olarak liste kullanılıyor" + +msgid "E731: using Dictionary as a String" +msgstr "E731: Bir dizi olarak sözlük kullanılıyor" + +msgid "E976: using Blob as a String" +msgstr "E976: Bir dizi olarak ikili geniş nesne kullanılıyor" + +msgid "E908: using an invalid value as a String" +msgstr "E908: Bir dizi olarak geçersiz bir değer kullanılıyor" + +msgid "E891: Using a Funcref as a Float" +msgstr "E891: Bir kayan noktalı değer olarak bir Funcref kullanılıyor" + +msgid "E892: Using a String as a Float" +msgstr "E892: Bir kayan noktalı değer olarak bir dizi kullanılıyor" + +msgid "E893: Using a List as a Float" +msgstr "E893: Bir kayan noktalı değer olarak bir liste kullanılıyor" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: Bir kayan noktalı değer olarak bir sözlük kullanılıyor" + +msgid "E362: Using a boolean value as a Float" +msgstr "E362: Bir kayan noktalı değer olarak bir Boole kullanılıyor" + +msgid "E907: Using a special value as a Float" +msgstr "E907: Bir Kayan Noktalı Değer olarak bir özel değer kullanılıyor" + +msgid "E975: Using a Blob as a Float" +msgstr "E975: Bir kayan noktalı değer olarak bir ikili geniş nesne kullanılıyor" + +msgid "E808: Number or Float required" +msgstr "E808: Sayı veya kayan noktalı değer gerekiyor" #, c-format -msgid "E795: Cannot delete variable %s" -msgstr "E795: %s değişkeni silinemiyor" +msgid "E122: Function %s already exists, add ! to replace it" +msgstr "E122: %s işlevi hâlihazırda mevcut, değiştirmek için ! ekleyin" + +msgid "E717: Dictionary entry already exists" +msgstr "E717: Sözlük girdisi hâlihazırda mevcut" + +msgid "E718: Funcref required" +msgstr "E718: Funcref gerekiyor" #, c-format -msgid "E704: Funcref variable name must start with a capital: %s" -msgstr "E704: Funcref değişkeni BÜYÜK harf ile başlamalıdır: %s" +msgid "E130: Unknown function: %s" +msgstr "E130: Bilinmeyen işlev: %s" #, c-format -msgid "E705: Variable name conflicts with existing function: %s" -msgstr "E705: Değişken adı mevcut işlevle çakışıyor: %s" +msgid "E125: Illegal argument: %s" +msgstr "E125: İzin verilmeyen argüman: %s" #, c-format -msgid "E741: Value is locked: %s" -msgstr "E741: Değer kilitli: %s" +msgid "E853: Duplicate argument name: %s" +msgstr "E853: Yinelenen argüman adı: %s" -msgid "Unknown" -msgstr "Bilinmiyor" +msgid "E989: Non-default argument follows default argument" +msgstr "E989: Öntanımlı olmayan argüman öntanımlı argümandan sonra" #, c-format -msgid "E742: Cannot change value of %s" -msgstr "E742: %s değeri değiştirilemiyor" +msgid "E740: Too many arguments for function %s" +msgstr "E740: %s işlevi için pek fazla argüman" -msgid "E921: Invalid callback argument" -msgstr "E921: Geçersiz geri çağırma argümanı" +#, c-format +msgid "E116: Invalid arguments for function %s" +msgstr "E116: %s işlevi için geçersiz argümanlar" + +msgid "E132: Function call depth is higher than 'maxfuncdepth'" +msgstr "E132: İşlevin çağırdığı derinlik 'maxfuncdepth'ten daha yüksek" + +#, c-format +msgid "calling %s" +msgstr "%s çağrılıyor" + +#, c-format +msgid "%s aborted" +msgstr "%s durduruldu" + +#, c-format +msgid "%s returning #%<PRId64>" +msgstr "%s, #%<PRId64> döndürüyor" + +#, c-format +msgid "%s returning %s" +msgstr "%s, %s döndürüyor" + +#, c-format +msgid "continuing in %s" +msgstr "%s içinde sürdürülüyor" + +msgid "E699: Too many arguments" +msgstr "E699: Çok fazla argüman" + +#, c-format +msgid "E117: Unknown function: %s" +msgstr "E117: Bilinmeyen işlev: %s" + +#, c-format +msgid "E276: Cannot use function as a method: %s" +msgstr "E276: İşlev bir yöntem olarak kullanılamaz: %s" + +#, c-format +msgid "E933: Function was deleted: %s" +msgstr "E933: İşlev silinmiş: %s" + +#, c-format +msgid "E119: Not enough arguments for function: %s" +msgstr "E119: Şu işlev için yetersiz sayıda argüman: %s" + +#, c-format +msgid "E120: Using <SID> not in a script context: %s" +msgstr "E120: <SID> bir betik bağlamında kullanılmıyor: %s" + +#, c-format +msgid "E725: Calling dict function without Dictionary: %s" +msgstr "E725: dic işlevi bir sözlük olmadan çağrılıyor: %s" + +msgid "E129: Function name required" +msgstr "E129: İşlev adı gerekiyor" + +#, c-format +msgid "E128: Function name must start with a capital or \"s:\": %s" +msgstr "E128: İşlev adı bir BÜYÜK harfle veya \"s:\" ile başlamalı: %s" + +#, c-format +msgid "E884: Function name cannot contain a colon: %s" +msgstr "E884: İşlev adı iki nokta içeremez: %s" + +#, c-format +msgid "E123: Undefined function: %s" +msgstr "E123: Tanımlanmamış işlev: %s" + +#, c-format +msgid "E124: Missing '(': %s" +msgstr "E124: '(' eksik: %s" + +msgid "E862: Cannot use g: here" +msgstr "E862: g: burada kullanılamaz" + +#, c-format +msgid "E932: Closure function should not be at top level: %s" +msgstr "E932: Kapatma işlevi en üst düzeyde olmamalıdır: %s" + +msgid "E126: Missing :endfunction" +msgstr "E126: :endfunction eksik" + +#, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: :endfunction sonrası metin bulundu: %s" + +#, c-format +msgid "E707: Function name conflicts with variable: %s" +msgstr "E707: İşlev adı şu değişken ile çakışıyor: %s" + +#, c-format +msgid "E127: Cannot redefine function %s: It is in use" +msgstr "E127: %s işlevi yeniden tanımlanamıyor: Şu an kullanımda" + +#, c-format +msgid "E746: Function name does not match script file name: %s" +msgstr "E746: İşlev adı betik dosyası adına eşleşmiyor: %s" + +#, c-format +msgid "E131: Cannot delete function %s: It is in use" +msgstr "E131: %s işlevi silinemiyor: Şu an kullanımda" + +#, c-format +msgid "Cannot delete function %s: It is being used internally" +msgstr "%s işlevi silinemiyor: Şu an program içinde kullanımda" + +msgid "E133: :return not inside a function" +msgstr "E133: :return bir işlev içinde değil" + +msgid "tcp address must be host:port" +msgstr "tcp adresi makine:kapı olmalı" + +msgid "failed to lookup host or port" +msgstr "makine veya kapı bulunamadı" + +msgid "connection refused" +msgstr "bağlantı reddedildi" #, c-format msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" @@ -914,14 +1505,12 @@ msgid "E134: Cannot move a range of lines into itself" msgstr "E134: Satırlardan oluşan erim kendi içine taşınamaz" #, c-format -msgid "%ld line moved" -msgid_plural "%ld lines moved" -msgstr[0] "%ld satır taşındı" -msgstr[1] "%ld satır taşındı" +msgid "E482: Can't create file %s" +msgstr "E482: %s dosyası oluşturulamıyor" #, c-format -msgid "%ld lines filtered" -msgstr "%ld satır süzüldü" +msgid "%<PRId64> lines filtered" +msgstr "%<PRId64> satır süzüldü" msgid "E135: *Filter* Autocommands must not change current buffer" msgstr "E135: *Süzgeç* otokomutları şu anki arabelleği değiştirmemelidir" @@ -929,13 +1518,6 @@ msgstr "E135: *Süzgeç* otokomutları şu anki arabelleği değiştirmemelidir" msgid "[No write since last change]\n" msgstr "[Son değişiklikten sonra yazılmadı]\n" -#, c-format -msgid "E503: \"%s\" is not a file or writable device" -msgstr "E503: \"%s\", bir dosya veya yazılabilir aygıt değil" - -msgid "Save As" -msgstr "Farklı Kaydet" - msgid "Write partial file?" msgstr "Dosyanın bir kısmı yazılsın mı?" @@ -955,8 +1537,8 @@ msgid "E768: Swap file exists: %s (:silent! overrides)" msgstr "E768: Takas dosyası mevcut: %s (:silent! geçersiz kılar)" #, c-format -msgid "E141: No file name for buffer %ld" -msgstr "E141: %ld numaralı arabelleğin bir adı yok" +msgid "E141: No file name for buffer %<PRId64>" +msgstr "E141: %<PRId64> numaralı arabelleğin bir adı yok" msgid "E142: File not written: Writing is disabled by 'write' option" msgstr "E142: Dosya yazılamadı: Yazma 'write' seçeneği ile kapatılmış" @@ -983,9 +1565,6 @@ msgstr "" msgid "E505: \"%s\" is read-only (add ! to override)" msgstr "E505: \"%s\" saltokunur (geçersiz kılmak için ! ekleyin)" -msgid "Edit File" -msgstr "Dosya Düzenle" - #, c-format msgid "E143: Autocommands unexpectedly deleted new buffer %s" msgstr "E143: yeni %s arabelleğini otokomutlar beklenmedik bir biçimde sildi" @@ -993,9 +1572,6 @@ msgstr "E143: yeni %s arabelleğini otokomutlar beklenmedik bir biçimde sildi" msgid "E144: non-numeric argument to :z" msgstr "E144: :z için sayısal olmayan argüman" -msgid "E145: Shell commands and some functionality not allowed in rvim" -msgstr "E145: rvim içinde kabuk komutları ve bazı işlevselliğe izin verilmez" - msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Düzenli ifadeler harflerle sınırlandırılamaz" @@ -1006,52 +1582,74 @@ msgstr "%s ile değiştir (y/n/a/q/l/^E/^Y)?" msgid "(Interrupted) " msgstr "(Yarıda kesildi) " +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global özyineli olarak bir erim ile yapılamaz" + +msgid "E148: Regular expression missing from global" +msgstr "E148: Düzenli ifade :global'den eksik" + #, c-format -msgid "%ld match on %ld line" -msgid_plural "%ld matches on %ld line" -msgstr[0] "%ld eşleşme, %ld satırda" -msgstr[1] "%ld eşleşme, %ld satırda" +msgid "Pattern found in every line: %s" +msgstr "Dizginin bulunduğu her satır: %s" #, c-format -msgid "%ld substitution on %ld line" -msgid_plural "%ld substitutions on %ld line" -msgstr[0] "%ld değiştirme, %ld satırda" -msgstr[1] "%ld değiştirme, %ld satırda" +msgid "Pattern not found: %s" +msgstr "Dizgi bulunamadı: %s" + +msgid "E478: Don't panic!" +msgstr "E478: Panik yok!" #, c-format -msgid "%ld match on %ld lines" -msgid_plural "%ld matches on %ld lines" -msgstr[0] "%ld eşleşme, %ld satırda" -msgstr[1] "%ld eşleşme, %ld satırda" +msgid "E661: Sorry, no '%s' help for %s" +msgstr "E661: Üzgünüm, '%s' yardımı %s için mevcut değil" #, c-format -msgid "%ld substitution on %ld lines" -msgid_plural "%ld substitutions on %ld lines" -msgstr[0] "%ld değiştirme, %ld satırda" -msgstr[1] "%ld değiştirme, %ld satırda" +msgid "E149: Sorry, no help for %s" +msgstr "E149: Üzgünüm, %s için yardım mevcut değil" -msgid "E147: Cannot do :global recursive with a range" -msgstr "E147: :global özyineli olarak bir erim ile yapılamaz" +#, c-format +msgid "Sorry, help file \"%s\" not found" +msgstr "Üzgünüm, \"%s\" yardım dosyası bulunamadı" -msgid "E148: Regular expression missing from global" -msgstr "E148: Düzenli ifade eksik" +#, c-format +msgid "E151: No match: %s" +msgstr "E151: Eşleşme bulunamadı: %s" #, c-format -msgid "Pattern found in every line: %s" -msgstr "Dizginin bulunduğu her satır: %s" +msgid "E152: Cannot open %s for writing" +msgstr "E152: %s yazma için açılamıyor" #, c-format -msgid "Pattern not found: %s" -msgstr "Dizgi bulunamadı: %s" +msgid "E153: Unable to open %s for reading" +msgstr "E153: %s okuma için açılamıyor" + +#, c-format +msgid "E670: Mix of help file encodings within a language: %s" +msgstr "E670: Bir dilde yardım dosyası kodlamaları karıştı: %s" + +#, c-format +msgid "E154: Duplicate tag \"%s\" in file %s/%s" +msgstr "E154: Şu dosyada yinelenen \"%s\" etiketi: %s/%s" + +#, c-format +msgid "E150: Not a directory: %s" +msgstr "E150: %s, bir dizin değil" msgid "No old files" msgstr "Eski dosya yok" +msgid "E750: First use \":profile start {fname}\"" +msgstr "E750: İlk kullanım \":profile start {dosyaadı}\"" + #, c-format msgid "Save changes to \"%s\"?" msgstr "Değişiklikler şuraya kaydedilsin mi: \"%s\"?" #, c-format +msgid "Close \"%s\"?" +msgstr "\"%s\" kapatılsın mı?" + +#, c-format msgid "E947: Job still running in buffer \"%s\"" msgstr "E947: İş \"%s\" arabelleğinde hâlâ sürüyor" @@ -1062,17 +1660,102 @@ msgstr "E162: \"%s\" arabelleği son değişiklikten sonra yazılmadı" msgid "Warning: Entered other buffer unexpectedly (check autocommands)" msgstr "Uyarı: Diğer arabelleğe aniden girildi (otokomutları denetleyin)" +msgid "E163: There is only one file to edit" +msgstr "E163: Düzenlenecek yalnızca bir dosya var" + +msgid "E164: Cannot go before first file" +msgstr "E164: İlk dosyadan öncesine gidilemez" + +msgid "E165: Cannot go beyond last file" +msgstr "E165: Son dosyadan öteye gidilemez" + +msgid "E610: No argument to delete" +msgstr "E610: Silinecek bir argüman yok" + #, c-format msgid "E666: compiler not supported: %s" msgstr "E666: Derleyici desteklenmiyor: %s" #, c-format -msgid "W20: Required python version 2.x not supported, ignoring file: %s" -msgstr "W20: Gerekli 2.x Python sürümü desteklenmiyor, dosya yok sayılıyor: %s" +msgid "Cannot source a directory: \"%s\"" +msgstr "Dizin kaynak alınamıyor: \"%s\"" + +#, c-format +msgid "could not source \"%s\"" +msgstr "\"%s\" kaynak alınamadı" + +#, c-format +msgid "line %<PRId64>: could not source \"%s\"" +msgstr "%<PRId64>. satır: \"%s\" kaynak alınamadı" + +#, c-format +msgid "sourcing \"%s\"" +msgstr "\"%s\" kaynak alınıyor" + +#, c-format +msgid "line %<PRId64>: sourcing \"%s\"" +msgstr "%<PRId64>. satır: \"%s\" kaynak alınıyor" + +#, c-format +msgid "finished sourcing %s" +msgstr "%s kaynak alınması bitti" + +msgid "modeline" +msgstr "kip satırı" + +msgid "--cmd argument" +msgstr "--cmd argümanı" + +msgid "-c argument" +msgstr "-c argümanı" + +msgid "environment variable" +msgstr "ortam değişkeni" + +msgid "error handler" +msgstr "hata işleyicisi" + +msgid "changed window size" +msgstr "değiştirilen pencere boyutu" + +msgid "Lua" +msgstr "Lua" + +#, c-format +msgid "API client (channel id %<PRIu64>)" +msgstr "API istemcisi (kanal kimliği %<PRIu64>" + +msgid "anonymous :source" +msgstr "anonim :source" + +#, c-format +msgid "anonymous :source (script id %d)" +msgstr "anonim :source (betik kimliği %d)" + +msgid "W15: Warning: Wrong line separator, ^M may be missing" +msgstr "W15: Uyarı: Yanlış satır ayırıcısı, ^M eksik olabilir" + +msgid "E167: :scriptencoding used outside of a sourced file" +msgstr "E167: :scriptencoding kaynak alınmış bir dosyanın dışında kullanıldı" + +msgid "E168: :finish used outside of a sourced file" +msgstr "E168: :finish kaynak alınmış bir dosyanın dışında kullanıldı" + +#, c-format +msgid "Current %slanguage: \"%s\"" +msgstr "Şu anki %sdil: \"%s\"" #, c-format -msgid "W21: Required python version 3.x not supported, ignoring file: %s" -msgstr "W21: Gerekli Python sürümü 3.x desteklenmiyor, dosya yok sayılıyor: %s" +msgid "E197: Cannot set language to \"%s\"" +msgstr "E197: \"%s\" diline ayarlanamıyor" + +#, c-format +msgid "E184: No such user-defined command: %s" +msgstr "E184: Böyle bir kullanıcı tanımlı komut yok: %s" + +#, c-format +msgid "E1237: No such user-defined command in current buffer: %s" +msgstr "E1237: Geçerli arabellekte böyle bir kullanıcı tanımlı komut yok: %s" msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." msgstr "Ex kipine giriliyor. Normal kipe geri dönmek için \"visual\" yazın." @@ -1084,6 +1767,9 @@ msgstr "E501: Dosyanın sonunda" msgid "Executing: %s" msgstr "Çalıştırılıyor: %s" +msgid "line %" +msgstr "satır %" + msgid "E169: Command too recursive" msgstr "E169: Komut çok özyineli" @@ -1097,12 +1783,12 @@ msgstr "Kaynak alınan dosyanın sonu" msgid "End of function" msgstr "İşlevin sonu" +msgid "E464: Ambiguous use of user-defined command" +msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı" + msgid "E492: Not an editor command" msgstr "E492: Bir düzenleyici komutu değil" -msgid "E981: Command not allowed in rvim" -msgstr "E981: Bu komuta rvim'de izin verilmiyor" - msgid "E493: Backwards range given" msgstr "E493: Geriye dönük erim verildi" @@ -1112,33 +1798,79 @@ msgstr "Geriye dönük erim verildi, takas edilebilir" msgid "E494: Use w or w>>" msgstr "E494: w veya w>> kullanın" -msgid "E943: Command table needs to be updated, run 'make cmdidxs'" -msgstr "" -"E943: Komut tablosunun güncellenmesi gerekiyor, 'make cmdidxs' çalıştırın" - msgid "" "INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX" msgstr "" "DAHİLİ: EX_DFLALL; ADDR_NONE, ADDR_UNSIGNED veya ADDR_QUICKFIX ile birlikte " "kullanılamaz" -msgid "E319: Sorry, the command is not available in this version" +msgid "E943: Command table needs to be updated, run 'make'" +msgstr "E943: Komut tablosunun güncellenmesi gerekiyor, 'make' çalıştırın" + +msgid "E319: The command is not available in this version" msgstr "E319: Üzgünüm, komut bu sürümde mevcut değil" #, c-format -msgid "%d more file to edit. Quit anyway?" -msgid_plural "%d more files to edit. Quit anyway?" -msgstr[0] "Düzenlenecek %d dosya daha var. Yine de çıkılsın mı?" -msgstr[1] "Düzenlenecek %d dosya daha var. Yine de çıkılsın mı?" +msgid "E174: Command already exists: add ! to replace it: %s" +msgstr "E174: Komut zaten mevcut: Değiştirmek için ! ekleyin: %s" + +msgid "" +"\n" +" Name Args Address Complete Definition" +msgstr "" +"\n" +" Ad Dğkl Adres Tam Tanım" + +msgid "No user-defined commands found" +msgstr "Kullanıcı tanımlı bir komut bulunamadı" + +msgid "E175: No attribute specified" +msgstr "E175: Bir öznitelik belirtilmemiş" + +msgid "E176: Invalid number of arguments" +msgstr "E176: Geçersiz argüman sayısı" + +msgid "E177: Count cannot be specified twice" +msgstr "E177: Sayım iki defa belirtilemez" + +msgid "E178: Invalid default value for count" +msgstr "E178: Sayım için geçersiz öntanımlı değer" + +msgid "E179: argument required for -complete" +msgstr "E179: -complete için argüman gerekiyor" + +msgid "E179: argument required for -addr" +msgstr "E179: -addr için argüman gerekiyor" + +#, c-format +msgid "E181: Invalid attribute: %s" +msgstr "E181: Geçersiz öznitelik: %s" + +msgid "E1208: -complete used without -nargs" +msgstr "E1208: -complete, -nargs olmadan kullanıldı" + +msgid "E182: Invalid command name" +msgstr "E182: Geçersiz komut adı" + +msgid "E183: User defined commands must start with an uppercase letter" +msgstr "E183: Kullanıcı tanımlı komutlar BÜYÜK harfle başlamalıdır" + +msgid "E841: Reserved name, cannot be used for user defined command" +msgstr "E841: Ayrılmış ad, kullanıcı tanımlı komut için kullanılamaz" + +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Geçersiz adres türü değeri: %s" #, c-format -msgid "E173: %d more file to edit" -msgid_plural "E173: %d more files to edit" -msgstr[0] "E173: Düzenlenecek %d dosya daha var" -msgstr[1] "E173: Düzenlenecek %d dosya daha var" +msgid "E180: Invalid complete value: %s" +msgstr "E180: Geçersiz tam değer: %s" + +msgid "E468: Completion argument only allowed for custom completion" +msgstr "E468: Tamamlama argümanına yalnızca özel tamamlamalarda izin verilir" -msgid "unknown" -msgstr "bilinmeyen" +msgid "E467: Custom completion requires a function argument" +msgstr "E467: Özel tamamlama bir işlev argümanı gerektirir" #, c-format msgid "E185: Cannot find color scheme '%s'" @@ -1153,57 +1885,26 @@ msgstr "E784: Son sekme sayfası kapatılamıyor" msgid "Already only one tab page" msgstr "Zaten bir sekme sayfası var" -msgid "Edit File in new tab page" -msgstr "Dosyayı yeni sekme sayfasında düzenle" - -msgid "Edit File in new window" -msgstr "Dosyayı yeni pencerede düzenle" - #, c-format msgid "Tab page %d" msgstr "Sekme sayfası %d" +msgid "E25: Nvim does not have a built-in GUI" +msgstr "E25: Nvim, bir grafik arabirim ile gelmez" + msgid "No swap file" msgstr "Takas dosyası yok" -msgid "Append File" -msgstr "Dosya iliştir" - -msgid "E747: Cannot change directory, buffer is modified (add ! to override)" -msgstr "" -"E747: Dizin değiştirilemiyor, arabellek değiştirilmiş (geçersiz kılmak " -"için ! ekleyin)" - msgid "E186: No previous directory" msgstr "E186: Öncesinde dizin yok" msgid "E187: Unknown" -msgstr "E187: Bilinmeyen" +msgstr "E187: Bilinmiyor" msgid "E465: :winsize requires two number arguments" msgstr "E465: :winsize iki adet sayı argüman gerektirir" #, c-format -msgid "Window position: X %d, Y %d" -msgstr "Pencere konumu: X %d, Y %d" - -msgid "E188: Obtaining window position not implemented for this platform" -msgstr "E188: Pencere konumunu alma özelliği bu platformda mevcut değil" - -msgid "E466: :winpos requires two number arguments" -msgstr "E466: :winpos iki adet sayı argüman gerektirir" - -msgid "E930: Cannot use :redir inside execute()" -msgstr "E930: :redir, execute() içinde kullanılamaz" - -msgid "Save Redirection" -msgstr "Yeniden yönlendirmeyi kaydet" - -#, c-format -msgid "E739: Cannot create directory: %s" -msgstr "E739: Dizin oluşturulamıyor: %s" - -#, c-format msgid "E189: \"%s\" exists (add ! to override)" msgstr "E189: \"%s\" zaten var (geçersiz kılmak için ! ekleyin)" @@ -1212,31 +1913,26 @@ msgid "E190: Cannot open \"%s\" for writing" msgstr "E190: \"%s\" yazma için açılamıyor" msgid "E191: Argument must be a letter or forward/backward quote" -msgstr "E191: Değişken bir harf veya açma/kapama tırnağı olmalıdır" +msgstr "E191: Değişken bir harf veya açma/kapatma tırnağı olmalıdır" msgid "E192: Recursive use of :normal too deep" msgstr "E192: :normal'in özyineli kullanımı çok derinde" -msgid "E809: #< is not available without the +eval feature" -msgstr "E809: #<, +eval özelliği olmadan kullanılamaz" - msgid "E194: No alternate file name to substitute for '#'" msgstr "E194: '#' yerine koymak için başka dosya adı yok" msgid "E495: no autocommand file name to substitute for \"<afile>\"" -msgstr "E495: \"<afile>\" yerine koymak için otokomut dosya adı yok" +msgstr "E495: \"<odosyası>\" yerine koymak için otokomut dosya adı yok" msgid "E496: no autocommand buffer number to substitute for \"<abuf>\"" -msgstr "E496: \"<abuf>\" yerine koymak için otokomut arabellek numarası yok" +msgstr "" +"E496: \"<oarabelleği>\" yerine koymak için otokomut arabellek numarası yok" msgid "E497: no autocommand match name to substitute for \"<amatch>\"" -msgstr "E497: \"<amatch>\" yerine koymak için otokomut eşleşme adı yok" +msgstr "E497: \"<oeşi>\" yerine koymak için otokomut eşleşme adı yok" msgid "E498: no :source file name to substitute for \"<sfile>\"" -msgstr "E498: \"<sfile>\" yerine koymak için :source dosya adı yok" - -msgid "E489: no call stack to substitute for \"<stack>\"" -msgstr "E489: \"<stack>\" yerine koymak için bir çağrı yığını yok" +msgstr "E498: \"<kdosyası>\" yerine koymak için :source dosya adı yok" msgid "E842: no line number to use for \"<slnum>\"" msgstr "E842: \"<slnum>\" kullanımı için satır numarası yok" @@ -1254,9 +1950,6 @@ msgstr "E500: Boş bir satır olarak değer biçer" msgid "Untitled" msgstr "Adsız" -msgid "E196: No digraphs in this version" -msgstr "E196: Bu sürümde ikili harfler bulunmamaktadır" - msgid "E608: Cannot :throw exceptions with 'Vim' prefix" msgstr "E608: 'Vim' öneki ile kural dışı durumlar :throw edilemez" @@ -1273,8 +1966,8 @@ msgid "Exception discarded: %s" msgstr "Kural dışı durum kenara atıldı: %s" #, c-format -msgid "%s, line %ld" -msgstr "%s, %ld. satır" +msgid "%s, line %<PRId64>" +msgstr "%s, %<PRId64>. satır" #, c-format msgid "Exception caught: %s" @@ -1307,6 +2000,15 @@ msgstr "Yarıda Kesilme" msgid "E579: :if nesting too deep" msgstr "E579: :if'ler çok iç içe geçmiş" +msgid "E580: :endif without :if" +msgstr "E580: :if olmadan :endif" + +msgid "E581: :else without :if" +msgstr "E581: :if olmadan :else" + +msgid "E582: :elseif without :if" +msgstr "E582: :if olmadan :elseif" + msgid "E583: multiple :else" msgstr "E583: Birden fazla :else" @@ -1316,26 +2018,38 @@ msgstr "E584: :else sonrası :elseif" msgid "E585: :while/:for nesting too deep" msgstr "E585: :while/:for çok iç içe geçmiş" +msgid "E586: :continue without :while or :for" +msgstr "E586: :while veya :for olmadan :continue" + +msgid "E587: :break without :while or :for" +msgstr "E587: :while veya :for olmadan :break" + msgid "E732: Using :endfor with :while" msgstr "E732: :endfor, :while ile kullanılıyor" msgid "E733: Using :endwhile with :for" msgstr "E733: :endwhile, :for ile kullanılıyor" -msgid "E579: block nesting too deep" -msgstr "E579: İç içe geçmeler çok derin" - msgid "E601: :try nesting too deep" msgstr "E601: :try çok iç içe geçmiş" +msgid "E603: :catch without :try" +msgstr "E603: :try olmadan :catch" + msgid "E604: :catch after :finally" msgstr "E604: :finally sonrası :catch" -msgid "E193: :enddef not inside a function" -msgstr "E193: :enddef bir işlev içinde değil" +msgid "E606: :finally without :try" +msgstr "E606: :try olmadan :finally" + +msgid "E607: multiple :finally" +msgstr "E607: Birden fazla :finally" + +msgid "E602: :endtry without :try" +msgstr "E602: :try olmadan :endtry" msgid "E193: :endfunction not inside a function" -msgstr "E193: :endfunction bir işlev içinde değil" +msgstr "E193: :endfunction, bir işlev içinde değil" msgid "E788: Not allowed to edit another buffer now" msgstr "E788: Şu anda başka bir arabellek düzenlenemez" @@ -1343,24 +2057,91 @@ msgstr "E788: Şu anda başka bir arabellek düzenlenemez" msgid "E811: Not allowed to change buffer information now" msgstr "E811: Şu anda arabellek bilgisi değiştirilemez" +#, c-format +msgid "E5408: Unable to get g:Nvim_color_cmdline callback: %s" +msgstr "E5408: g:Nvim_color_cmdline geri çağrısı alınamıyor: %s" + +#, c-format +msgid "E5407: Callback has thrown an exception: %s" +msgstr "E5407: Geri çağrı bir istisna döndürdü: %s" + +msgid "E5400: Callback should return list" +msgstr "E5400: Geri çağrı, liste döndürmeli" + +#, c-format +msgid "E5401: List item %i is not a List" +msgstr "E5401: Liste ögesi %i, bir Liste değil" + +#, c-format +msgid "E5402: List item %i has incorrect length: %d /= 3" +msgstr "E5402: %i liste ögesinin uzunluğu yanlış: %d /= 3" + +msgid "E5403: Chunk %i start %" +msgstr "E5403: %i parçası başlangıç %" + +msgid "E5405: Chunk %i start %" +msgstr "E5405: %i parçası başlangıç %" + +msgid "E5404: Chunk %i end %" +msgstr "E5404: %i parçası bitiş %" + +msgid "E5406: Chunk %i end %" +msgstr "E5406: %i parçası bitiş %" + +msgid "tagname" +msgstr "etiket adı" + +msgid " kind file\n" +msgstr " dosya türü\n" + +msgid "'history' option is zero" +msgstr "'history' seçeneği sıfır" + msgid "[Command Line]" msgstr "[Komut Satırı]" msgid "E199: Active window or buffer deleted" msgstr "E199: Etkin pencere veya arabellek silinmiş" +msgid "E854: path too long for completion" +msgstr "E854: Yol tamamlama için çok uzun" + +#, c-format +msgid "" +"E343: Invalid path: '**[number]' must be at the end of the path or be " +"followed by '%s'." +msgstr "" +"E343: Geçersiz yol: '**[sayı]' yolun sonunda olmalı veya sonrasında '%s' " +"gelmelidir" + +#, c-format +msgid "E344: Can't find directory \"%s\" in cdpath" +msgstr "E344: \"%s\" dizini cdpath içinde bulunamadı" + +#, c-format +msgid "E345: Can't find file \"%s\" in path" +msgstr "E345: \"%s\" dosyası yol içinde bulunamadı" + +#, c-format +msgid "E346: No more directory \"%s\" found in cdpath" +msgstr "E346: Başka bir \"%s\" dizini cdpath içinde bulunamadı" + +#, c-format +msgid "E347: No more file \"%s\" found in path" +msgstr "E347: Başka bir \"%s\" dosyası yol içinde bulunamadı" + msgid "E812: Autocommands changed buffer or buffer name" msgstr "E812: Otokomutlar arabelleği veya arabellek adını değiştirdi" +msgid "is a directory" +msgstr "bir dizin" + msgid "Illegal file name" msgstr "İzin verilmeyen dosya adı" msgid "is not a file" msgstr "bir dosya değil" -msgid "is a device (disabled with 'opendevice' option)" -msgstr "bir aygıt ('opendevice' seçeneği ile kapatılır)" - msgid "[New DIRECTORY]" msgstr "[Yeni DİZİN]" @@ -1376,17 +2157,11 @@ msgstr "E200: *ReadPre otokomutları dosyayı okunamaz hale getirdi" msgid "E201: *ReadPre autocommands must not change current buffer" msgstr "E201: *ReadPre otokomutları şu anki arabelleği değiştirmemeli" -msgid "Vim: Reading from stdin...\n" -msgstr "Vim: stdin'den okunuyor...\n" - -msgid "Reading from stdin..." -msgstr "stdin'den okunuyor..." - msgid "E202: Conversion made file unreadable!" msgstr "E202: Dönüştürme dosyayı okunamaz hale getirdi!" msgid "[fifo]" -msgstr "[fifo]" +msgstr "[ilk giren ilk çıkar]" msgid "[socket]" msgstr "[uç nokta]" @@ -1400,13 +2175,19 @@ msgstr "[Eksik CR]" msgid "[long lines split]" msgstr "[uzun satırlar bölünmüş]" +msgid "[NOT converted]" +msgstr "[dönüştürülmedi]" + +msgid "[converted]" +msgstr "[dönüştürüldü]" + #, c-format -msgid "[CONVERSION ERROR in line %ld]" -msgstr "[%ld. satırda DÖNÜŞTÜRME HATASI]" +msgid "[CONVERSION ERROR in line %<PRId64>]" +msgstr "[%<PRId64>. satırda DÖNÜŞTÜRME HATASI]" #, c-format -msgid "[ILLEGAL BYTE in line %ld]" -msgstr "[%ld. satırda GEÇERSİZ BAYT]" +msgid "[ILLEGAL BYTE in line %<PRId64>]" +msgstr "[%<PRId64>. satırda İZİN VERİLMEYEN BAYT]" msgid "[READ ERRORS]" msgstr "[OKUMA HATALARI]" @@ -1420,41 +2201,138 @@ msgstr "'charconvert' ile dönüştürme başarısız" msgid "can't read output of 'charconvert'" msgstr "'charconvert' çıktısı okunamıyor" -msgid "[dos]" -msgstr "[dos]" +msgid "[New File]" +msgstr "[Yeni Dosya]" + +msgid "[New]" +msgstr "[Yeni]" + +msgid "E676: No matching autocommands for acwrite buffer" +msgstr "E676: acwrite arabelleği için eşleşen bir otokomut yok" + +msgid "E203: Autocommands deleted or unloaded buffer to be written" +msgstr "E203: Otokomutlar arabelleği silmiş veya yazılması için kaldırmışlar" + +msgid "E204: Autocommand changed number of lines in unexpected way" +msgstr "E204: Otokomut satır sayısını beklenmeyen biçimde değiştirdi" + +msgid "is not a file or writable device" +msgstr "bir dosya veya yazılabilir aygıt değil" + +msgid "is read-only (add ! to override)" +msgstr "saltokunur (geçersiz kılmak için ! ekleyin)" + +#, c-format +msgid "E303: Unable to create directory \"%s\" for backup file: %s" +msgstr "E303: Yedek dosyası için \"%s\" dizini oluşturulamadı: %s" + +msgid "E506: Can't write to backup file (add ! to override)" +msgstr "E506: Yedek dosyasına yazılamıyor (geçersiz kılmak için ! ekleyin)" + +msgid "E509: Cannot create backup file (add ! to override)" +msgstr "E509: Yedek dosyası oluşturulamıyor (geçersiz kılmak için ! ekleyin)" + +msgid "E510: Can't make backup file (add ! to override)" +msgstr "E510: Yedek dosyası yapılamıyor (geçersiz kılmak için ! ekleyin)" + +msgid "E214: Can't find temp file for writing" +msgstr "E214: Yazma için geçici dosya bulunamıyor" + +msgid "E213: Cannot convert (add ! to write without conversion)" +msgstr "E213: Dönüştürülemiyor (dönüştürmeden yazmak için ! ekleyin)" + +msgid "E166: Can't open linked file for writing" +msgstr "E166: Bağlı dosya yazma için açılamıyor" + +#, c-format +msgid "E212: Can't open file for writing: %s" +msgstr "E212: Dosya yazma için açılamıyor: %s" + +#, c-format +msgid "E512: Close failed: %s" +msgstr "E512: Kapatma başarısız oldu: %s" + +msgid "E513: write error, conversion failed (make 'fenc' empty to override)" +msgstr "" +"E513: Yazma hatası, dönüştürme başarısız (geçersiz kılmak için 'fenc'i boş " +"bırakın)" + +msgid "E513: write error, conversion failed in line %" +msgstr "E513: Yazma hatası, şu satırda dönüştürme başarısız: %" + +msgid "E514: write error (file system full?)" +msgstr "E514: Yazma hatası (dosya sistemi dolu mu?)" + +msgid " CONVERSION ERROR" +msgstr " DÖNÜŞTÜRME HATASI" + +#, c-format +msgid " in line %<PRId64>;" +msgstr " %<PRId64>. satırda;" + +msgid "[Device]" +msgstr "[Aygıt]" + +msgid " [a]" +msgstr " [i]" + +msgid " appended" +msgstr " iliştirildi" + +msgid " [w]" +msgstr " [y]" + +msgid " written" +msgstr " yazıldı" + +msgid "E205: Patchmode: can't save original file" +msgstr "E205: Yama kipi: Orijinal dosya kaydedilemiyor" + +msgid "E206: patchmode: can't touch empty original file" +msgstr "E206: Yama kipi: Orijinal boş dosyaya dokunulamıyor" + +msgid "E207: Can't delete backup file" +msgstr "E207: Yedek dosyası silinemiyor" + +msgid "" +"\n" +"WARNING: Original file may be lost or damaged\n" +msgstr "" +"\n" +"UYARI: Orijinal dosya kaybolmuş veya hasar görmüş olabilir\n" + +msgid "don't quit the editor until the file is successfully written!" +msgstr "dosya başarılı bir biçimde yazılana kadar düzenleyiciden çıkmayın!" msgid "[dos format]" msgstr "[dos biçimi]" -msgid "[mac]" -msgstr "[mac]" +msgid "[dos]" +msgstr "[dos]" msgid "[mac format]" msgstr "[mac biçimi]" -msgid "[unix]" -msgstr "[unix]" +msgid "[mac]" +msgstr "[mac]" msgid "[unix format]" msgstr "[unix biçimi]" -#, c-format -msgid "%ld line, " -msgid_plural "%ld lines, " -msgstr[0] "%ld satır, " -msgstr[1] "%ld satır, " +msgid "[unix]" +msgstr "[unix]" -#, c-format -msgid "%lld byte" -msgid_plural "%lld bytes" -msgstr[0] "%lld bayt" -msgstr[1] "%lld bayt" +msgid "[Incomplete last line]" +msgstr "[Tamamlanmamış son satır]" msgid "[noeol]" -msgstr "[noeol]" +msgstr "[satır sonu yok]" -msgid "[Incomplete last line]" -msgstr "[Tamamlanmamış son satır]" +msgid "WARNING: The file has been changed since reading it!!!" +msgstr "UYARI: Bu dosya açıldıktan sonra başkası tarafından değiştirilmiş!!!" + +msgid "Do you really want to write to it" +msgstr "Yine de yazmak istiyor musunuz?" #, c-format msgid "E208: Error writing to \"%s\"" @@ -1479,9 +2357,7 @@ msgstr "E211: \"%s\" dosyası artık mevcut değil" msgid "" "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as " "well" -msgstr "" -"W12: Uyarı: \"%s\" dosyası Vim'deki arabellek de dahil olmak üzere " -"değiştirildi" +msgstr "W12: Uyarı: \"%s\" dosyası ve Vim'deki arabellek değişti" msgid "See \":help W12\" for more info." msgstr "Ek bilgi için \":help W12\" yazın." @@ -1510,10 +2386,12 @@ msgstr "Uyarı" msgid "" "&OK\n" -"&Load File" +"&Load File\n" +"Load File &and Options" msgstr "" "&Tamam\n" -"&Dosya Yükle" +"&Dosyayı Yükle\n" +"Dosyayı ve &Seçenekleri Yükle" #, c-format msgid "E462: Could not prepare for reloading \"%s\"" @@ -1529,386 +2407,508 @@ msgstr "E219: { eksik." msgid "E220: Missing }." msgstr "E220: } eksik." -msgid "<empty>" -msgstr "<boş>" - -msgid "E655: Too many symbolic links (cycle?)" -msgstr "E655: Çok fazla sembolik bağlantı (çevrim?)" +msgid "E490: No fold found" +msgstr "E490: Kıvırma bulunamadı" -msgid "writefile() first argument must be a List or a Blob" -msgstr "writefile() ilk argümanı bir liste veya ikili geniş nesne olmalıdır" +msgid "E350: Cannot create fold with current 'foldmethod'" +msgstr "E350: Şu anki 'foldmethod' ile kıvırma oluşturulamıyor" -msgid "Select Directory dialog" -msgstr "Dizin Seç iletişim kutusu" +msgid "E351: Cannot delete fold with current 'foldmethod'" +msgstr "E351: Şu anki 'foldmethod' ile kıvırma silinemiyor" -msgid "Save File dialog" -msgstr "Dosya Kaydet iletişim kutusu" +msgid "E222: Add to read buffer" +msgstr "E222: Okunan arabelleğe ekle" -msgid "Open File dialog" -msgstr "Dosya Aç iletişim kutusu" +msgid "E223: recursive mapping" +msgstr "E223: Özyineli eşlemleme" -msgid "E338: Sorry, no file browser in console mode" -msgstr "E338: Üzgünüm, konsol kipinde dosya tarayıcı yoktur" +#, c-format +msgid "E224: global abbreviation already exists for %s" +msgstr "E224: %s için global kısaltma hâlihazırda var" -msgid "no matches" -msgstr "eşleşme yok" +#, c-format +msgid "E225: global mapping already exists for %s" +msgstr "E225: %s için global eşlemleme hâlihazırda var" -msgid "E854: path too long for completion" -msgstr "E854: Yol tamamlama için çok uzun" +#, c-format +msgid "E226: abbreviation already exists for %s" +msgstr "E226: %s için kısaltma hâlihazırda var" #, c-format -msgid "" -"E343: Invalid path: '**[number]' must be at the end of the path or be " -"followed by '%s'." +msgid "E227: mapping already exists for %s" +msgstr "E227: %s için eşlemleme hâlihazırda var" + +msgid "No abbreviation found" +msgstr "Kısaltma bulunamadı" + +msgid "No mapping found" +msgstr "Eşlemleme bulunamadı" + +msgid "E228: makemap: Illegal mode" +msgstr "E228: makemap: İzin verilmeyen kip" + +msgid "--No lines in buffer--" +msgstr "--Arabellek içinde satır yok--" + +msgid "E470: Command aborted" +msgstr "E470: Komut durduruldu" + +msgid "E905: Cannot set this option after startup" +msgstr "E905: Bu seçenek, başlangıçtan sonra ayarlanamaz" + +msgid "E903: Could not spawn API job" +msgstr "E903: API işi ortaya çıkarılamadı" + +msgid "E471: Argument required" +msgstr "E471: Argüman gerekiyor" + +msgid "E10: \\ should be followed by /, ? or &" +msgstr "E10: \\ sonrasında /, ? veya & gelmeli" + +msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" +msgstr "E11: Komut satırı penceresinde geçersiz; <CR> çalıştırır, CTRL-C çıkar" + +msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" msgstr "" -"E343: Geçersiz yol: '**[sayı]' yolun sonunda olmalı veya sonrasında '%s' " -"gelmelidir" +"E12: Geçerli dizin veya etiket aramasında exrc veya vimrc'den komutlara izin " +"verilmiyor" + +msgid "E171: Missing :endif" +msgstr "E171: :endif eksik" + +msgid "E600: Missing :endtry" +msgstr "E600: :endtry eksik" + +msgid "E170: Missing :endwhile" +msgstr "E170: :endwhile eksik" + +msgid "E170: Missing :endfor" +msgstr "E170: :endfor eksik" + +msgid "E588: :endwhile without :while" +msgstr "E588: :while olmadan :endwhile" + +msgid "E588: :endfor without :for" +msgstr "E588: :for olmadan :endfor" + +msgid "E13: File exists (add ! to override)" +msgstr "E13: Dosya mevcut (geçersiz kılmak için ! ekleyin)" + +msgid "E472: Command failed" +msgstr "E472: Komut başarısız oldu" + +msgid "E473: Internal error" +msgstr "E473: İçsel hata" #, c-format -msgid "E344: Can't find directory \"%s\" in cdpath" -msgstr "E344: \"%s\" dizini cdpath içinde bulunamadı" +msgid "E685: Internal error: %s" +msgstr "E685: İçsel hata: %s" + +msgid "Interrupted" +msgstr "Yarıda kesildi" + +msgid "E474: Invalid argument" +msgstr "E474: Geçersiz argüman" #, c-format -msgid "E345: Can't find file \"%s\" in path" -msgstr "E345: \"%s\" dosyası yol içinde bulunamadı" +msgid "E475: Invalid argument: %s" +msgstr "E475: Geçersiz argüman: %s" #, c-format -msgid "E346: No more directory \"%s\" found in cdpath" -msgstr "E346: Başka bir \"%s\" dizini cdpath içinde bulunamadı" +msgid "E475: Invalid value for argument %s" +msgstr "E475: %s argümanı için geçersiz değer" #, c-format -msgid "E347: No more file \"%s\" found in path" -msgstr "E347: Başka bir \"%s\" dosyası yol içinde bulunamadı" +msgid "E475: Invalid value for argument %s: %s" +msgstr "E475: %s argümanı için geçersiz değer: %s" -msgid "E446: No file name under cursor" -msgstr "E446: İmleç altında bir dosya adı yok" +#, c-format +msgid "E983: Duplicate argument: %s" +msgstr "E983: Yinelenen argüman: %s" #, c-format -msgid "E447: Can't find file \"%s\" in path" -msgstr "E447: \"%s\" dosyası yol içinde bulunamadı" +msgid "E15: Invalid expression: %s" +msgstr "E15: Geçersiz ifade: %s" -msgid "E808: Number or Float required" -msgstr "E808: Sayı veya kayan noktalı değer gerekiyor" +msgid "E16: Invalid range" +msgstr "E16: Geçersiz erim" -msgid "E490: No fold found" -msgstr "E490: Kıvırma bulunamadı" +msgid "E476: Invalid command" +msgstr "E476: Geçersiz komut" -msgid "E350: Cannot create fold with current 'foldmethod'" -msgstr "E350: Şu anki 'foldmethod' ile kıvırma oluşturulamıyor" +#, c-format +msgid "E17: \"%s\" is a directory" +msgstr "E17: \"%s\" bir dizin" -msgid "E351: Cannot delete fold with current 'foldmethod'" -msgstr "E351: Şu anki 'foldmethod' ile kıvırma silinemiyor" +msgid "E756: Spell checking is not possible" +msgstr "E756: Yazım denetimi olanaklı değil" + +msgid "E900: Invalid channel id" +msgstr "E900: Geçersiz kanal kimliği" + +msgid "E900: Invalid channel id: not a job" +msgstr "E900: Geçersiz kanal kimliği: Bir iş değil" + +msgid "E901: Job table is full" +msgstr "E901: İş tablosu dolu" #, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld satır kıvrıldı " -msgstr[1] "+--%3ld satır kıvrıldı " +msgid "E903: Process failed to start: %s: \"%s\"" +msgstr "E903: Süreç başlatılamadı: %s: \"%s\"" + +msgid "E904: channel is not a pty" +msgstr "E904: Kanal bir pty değil" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld satır: " -msgstr[1] "+-%s%3ld satır: " +msgid "E905: Couldn't open stdio channel: %s" +msgstr "E905: stdio kanalı açılamadı: %s" -msgid "E222: Add to read buffer" -msgstr "E222: Okuma arabelleğine ekle" +msgid "E906: invalid stream for channel" +msgstr "E906: Kanal için geçersiz akış" -msgid "E223: recursive mapping" -msgstr "E223: Özyineli eşlemleme" +msgid "E906: invalid stream for rpc channel, use 'rpc'" +msgstr "E906: rpc kanalı için geçersiz akış, 'rpc' kullanın" -msgid "E851: Failed to create a new process for the GUI" -msgstr "E851: Grafik arabirim için yeni bir işlem yaratılamadı" +#, c-format +msgid "" +"E5210: dict key '%s' already set for buffered stream in channel %<PRIu64>" +msgstr "E5210: '%s' sözlük anahtarı, %<PRIu64> kanalında halihazırda " +"arabelleklenmiş akış için ayarlandı" -msgid "E852: The child process failed to start the GUI" -msgstr "E852: Alt işlem grafik arabirimini başlatamadı" +#, c-format +msgid "E364: Library call failed for \"%s()\"" +msgstr "E364: \"%s()\" için kitaplık çağrısı başarısız oldu" -msgid "E229: Cannot start the GUI" -msgstr "E229: Grafik arabirimi başlatılamıyor" +#, c-format +msgid "E667: Fsync failed: %s" +msgstr "E667: Fsync başarısız oldu: %s" #, c-format -msgid "E230: Cannot read from \"%s\"" -msgstr "E230: \"%s\" okunamıyor" +msgid "E739: Cannot create directory %s: %s" +msgstr "E739: Dizin oluşturulamıyor: %s: %s" + +msgid "E19: Mark has invalid line number" +msgstr "E19: İm satır numarası geçersiz" -msgid "E665: Cannot start GUI, no valid font found" -msgstr "E665: Grafik arabirim başlatılamıyor, geçerli bir font bulunamadı" +msgid "E20: Mark not set" +msgstr "E20: İm ayarlanmamış" -msgid "E231: 'guifontwide' invalid" -msgstr "E231: 'guifontwide' geçersiz" +msgid "E21: Cannot make changes, 'modifiable' is off" +msgstr "E21: Değişiklik yapılamıyor, 'modifiable' kapalı" -msgid "E599: Value of 'imactivatekey' is invalid" -msgstr "E599: 'imactivatekey' değeri geçersiz" +msgid "E22: Scripts nested too deep" +msgstr "E22: Betikler çok iç içe geçmiş" -msgid "No match at cursor, finding next" -msgstr "İmleç konumunda eşleşme bulunamadı, sonraki bulunuyor" +msgid "E23: No alternate file" +msgstr "E23: Başka bir dosya yok" -msgid "<cannot open> " -msgstr "<açılamıyor> " +msgid "E24: No such abbreviation" +msgstr "E24: Böyle bir kısaltma yok" + +msgid "E477: No ! allowed" +msgstr "E477: ! imine izin verilmiyor" #, c-format -msgid "E616: vim_SelFile: can't get font %s" -msgstr "E616: vim_SelFile: %s fontu bulunamıyor" +msgid "E28: No such highlight group name: %s" +msgstr "E28: Böyle bir vurgulama grup adı yok: %s" -msgid "E614: vim_SelFile: can't return to current directory" -msgstr "E614: vim_SelFile: Şu anki dizine dönülemiyor" +msgid "E29: No inserted text yet" +msgstr "E29: Henüz bir metin eklenmedi" -msgid "Pathname:" -msgstr "Yol adı:" +msgid "E30: No previous command line" +msgstr "E30: Öncesinde komut satırı yok" -msgid "E615: vim_SelFile: can't get current directory" -msgstr "E615: vim_SelFile: Şu anki dizin bulunamıyor" +msgid "E31: No such mapping" +msgstr "E31: Böyle bir eşlem yok" -msgid "OK" -msgstr "Tamam" +msgid "E479: No match" +msgstr "E479: Eşleşme yok" -msgid "Cancel" -msgstr "İptal" +#, c-format +msgid "E480: No match: %s" +msgstr "E480: Eşleşme yok: %s" -msgid "Scrollbar Widget: Could not get geometry of thumb pixmap." -msgstr "Kaydırma Parçacığı: Kenar piksel haritası geometrisi bulunamadı" +msgid "E32: No file name" +msgstr "E32: Dosya adı yok" -msgid "Vim dialog" -msgstr "Vim" +msgid "E33: No previous substitute regular expression" +msgstr "E33: Öncesinde yerine geçen bir düzenli ifade yok" -msgid "E232: Cannot create BalloonEval with both message and callback" -msgstr "E232: Hem ileti hem de geri çağırma ile BallonEval oluşturulamıyor" +msgid "E34: No previous command" +msgstr "E34: Öncesinde komut yok" -msgid "_Save" -msgstr "_Kaydet" +msgid "E35: No previous regular expression" +msgstr "E35: Öncesinde düzenli ifade yok" -msgid "_Open" -msgstr "_Aç" +msgid "E481: No range allowed" +msgstr "E481: Erime izin verilmiyor" -msgid "_Cancel" -msgstr "İ_ptal" +msgid "E36: Not enough room" +msgstr "E36: Yeterli alan yok" -msgid "_OK" -msgstr "_Tamam" +msgid "E483: Can't get temp file name" +msgstr "E483: Geçici dosya adı alınamıyor" -msgid "" -"&Yes\n" -"&No\n" -"&Cancel" -msgstr "" -"&Evet\n" -"&Hayır\n" -"İ&ptal" +#, c-format +msgid "E484: Can't open file %s" +msgstr "E484: %s dosyası açılamıyor" -msgid "Yes" -msgstr "Evet" +#, c-format +msgid "E484: Can't open file %s: %s" +msgstr "E484: %s dosyası açılamıyor: %s" -msgid "No" -msgstr "Hayır" +#, c-format +msgid "E485: Can't read file %s" +msgstr "E485: %s dosyası okunamıyor" -msgid "Input _Methods" -msgstr "Giriş _Yöntemleri" +msgid "E38: Null argument" +msgstr "E38: Anlamsız argüman" -msgid "VIM - Search and Replace..." -msgstr "VİM - Ara ve Değiştir..." +msgid "E39: Number expected" +msgstr "E39: Sayı bekleniyordu" -msgid "VIM - Search..." -msgstr "VİM - Ara..." +#, c-format +msgid "E40: Can't open errorfile %s" +msgstr "E40: Hata dosyası %s açılamıyor" -msgid "Find what:" -msgstr "Bulunacak nesne:" +msgid "E41: Out of memory!" +msgstr "E41: Bellek tükendi!" -msgid "Replace with:" -msgstr "Şununla değiştir:" +msgid "Pattern not found" +msgstr "Dizgi bulunamadı" -msgid "Match whole word only" -msgstr "Tam sözcükleri ara" +#, c-format +msgid "E486: Pattern not found: %s" +msgstr "E486: Dizgi bulunamadı: %s" -msgid "Match case" -msgstr "BÜYÜK/küçük harf duyarlı" +msgid "E487: Argument must be positive" +msgstr "E487: Değişken pozitif olmalı" -msgid "Direction" -msgstr "Yön" +msgid "E459: Cannot go back to previous directory" +msgstr "E459: Bir önceki dizine gidilemiyor" -msgid "Up" -msgstr "Yukarı" +msgid "E42: No Errors" +msgstr "E42: Hata yok" -msgid "Down" -msgstr "Aşağı" +msgid "E776: No location list" +msgstr "E776: Konum listesi yok" -msgid "Find Next" -msgstr "Sonrakini Bul" +msgid "E43: Damaged match string" +msgstr "E43: Hasarlı eşleşme dizisi" -msgid "Replace" -msgstr "Değiştir" +msgid "E44: Corrupted regexp program" +msgstr "E44: Bozulmuş regexp programı" -msgid "Replace All" -msgstr "Tümünü Değiştir" +msgid "E45: 'readonly' option is set (add ! to override)" +msgstr "E45: 'readonly' seçeneği ayarlanmış (geçersiz kılmak için ! ekleyin)" -msgid "_Close" -msgstr "K_apat" +#, c-format +msgid "E46: Cannot change read-only variable \"%.*s\"" +msgstr "E46: Saltokunur değişken \"%.*s\" değiştirilemiyor" -msgid "Vim: Received \"die\" request from session manager\n" -msgstr "Vim: Oturum yöneticisinden işi sonlandırma isteği geldi\n" +msgid "E928: String required" +msgstr "E928: Dizi gerekiyor" -msgid "Close tab" -msgstr "Sekmeyi kapat" +msgid "E715: Dictionary required" +msgstr "E715: Sözlük gerekiyor" -msgid "New tab" -msgstr "Yeni sekme" +#, c-format +msgid "E979: Blob index out of range: %<PRId64>" +msgstr "E979: İkili geniş nesne indeksi erimin dışında: %<PRId64>" -msgid "Open Tab..." -msgstr "Sekme Aç..." +msgid "E978: Invalid operation for Blob" +msgstr "E978: İkili geniş nesne için geçersiz işlem" -msgid "Vim: Main window unexpectedly destroyed\n" -msgstr "Vim: Ana pencere beklenmedik bir biçimde gitti\n" +#, c-format +msgid "E118: Too many arguments for function: %s" +msgstr "E118: İşlev için pek fazla argüman: %s" -msgid "&Filter" -msgstr "&Süz" +#, c-format +msgid "E716: Key not present in Dictionary: \"%s\"" +msgstr "E716: Anahtar sözlükte mevcut değil: \"%s\"" -msgid "&Cancel" -msgstr "İ&ptal" +msgid "E714: List required" +msgstr "E714: Liste gerekiyor" -msgid "Directories" -msgstr "Dizinler" +msgid "E897: List or Blob required" +msgstr "E897: Liste veya ikili geniş nesne gerekiyor" -msgid "Filter" -msgstr "Süzgeç" +#, c-format +msgid "E712: Argument of %s must be a List or Dictionary" +msgstr "E712: %s ögesinin argümanı bir liste veya sözlük olmalıdır" -msgid "&Help" -msgstr "&Yardım" +#, c-format +msgid "E896: Argument of %s must be a List, Dictionary or Blob" +msgstr "E896: %s argümanı bir liste, sözlük veya ikili geniş nesne olmalıdır" -msgid "Files" -msgstr "Dosyalar" +msgid "E47: Error while reading errorfile" +msgstr "E47: Hata dosyası okunurken hata" -msgid "&OK" -msgstr "&Tamam" +msgid "E48: Not allowed in sandbox" +msgstr "E48: Kum havuzunda izin verilmiyor" -msgid "Selection" -msgstr "Seçim" +msgid "E523: Not allowed here" +msgstr "E523: Burada izin verilmiyor" -msgid "Find &Next" -msgstr "Sonrakini &Bul" +msgid "E359: Screen mode setting not supported" +msgstr "E359: Ekran kipi ayarı desteklenmiyor" -msgid "&Replace" -msgstr "&Değiştir" +msgid "E49: Invalid scroll size" +msgstr "E49: Geçersiz kaydırma boyutu" -msgid "Replace &All" -msgstr "Tümünü D&eğiştir" +msgid "E91: 'shell' option is empty" +msgstr "E91: 'shell' seçeneği boş" -msgid "&Undo" -msgstr "&Geri al" +msgid "E255: Couldn't read in sign data!" +msgstr "E255: İşaret verisinde okunamadı!" -msgid "Open tab..." -msgstr "Sekme aç..." +msgid "E72: Close error on swap file" +msgstr "E72: Takas dosyasında kapatma hatası" -msgid "Find string" -msgstr "Dizi bul" +msgid "E73: tag stack empty" +msgstr "E73: Etiket yığını boş" -msgid "Find & Replace" -msgstr "Bul ve Değiştir" +msgid "E74: Command too complex" +msgstr "E74: Komut çok karmaşık" -msgid "Not Used" -msgstr "Kullanılmıyor" +msgid "E75: Name too long" +msgstr "E75: Ad çok uzun" -msgid "Directory\t*.nothing\n" -msgstr "Directory\t*.hiçbir şey\n" +msgid "E76: Too many [" +msgstr "E76: Çok fazla [" -#, c-format -msgid "E671: Cannot find window title \"%s\"" -msgstr "E671: Pencere başlığı \"%s\" bulunamıyor" +msgid "E77: Too many file names" +msgstr "E77: Çok fazla dosya adı" + +msgid "E488: Trailing characters" +msgstr "E488: Sonda fazladan karakterler" #, c-format -msgid "E243: Argument not supported: \"-%s\"; Use the OLE version." -msgstr "E243: \"-%s\" argümanı desteklenmiyor; OLE sürümünü kullanın." +msgid "E488: Trailing characters: %s" +msgstr "E488: Sonda fazladan karakterler: %s" -msgid "E988: GUI cannot be used. Cannot execute gvim.exe." -msgstr "E988: Grafik arabirim kullanılamaz. gvim.exe çalıştırılamadı." +msgid "E78: Unknown mark" +msgstr "E78: Bilinmeyen im" -msgid "E672: Unable to open window inside MDI application" -msgstr "E672: MDI uygulaması içinde pencere açılamıyor" +msgid "E79: Cannot expand wildcards" +msgstr "E79: Joker karakterleri genişletilemiyor" -msgid "Vim E458: Cannot allocate colormap entry, some colors may be incorrect" -msgstr "" -"Vim E458: Renk eşlemi girdisi ayrılamadı, bazı renkler hatalı görünebilir" +msgid "E591: 'winheight' cannot be smaller than 'winminheight'" +msgstr "E591: 'winheight' değeri 'winminheight' değerinden küçük olamaz" -#, c-format -msgid "E250: Fonts for the following charsets are missing in fontset %s:" -msgstr "E250: %s yazıtipi seti içinde şu karakter setleri için fontlar eksik:" +msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" +msgstr "E592: 'winwidth' değeri 'winminwidth' değerinden küçük olamaz" -#, c-format -msgid "E252: Fontset name: %s" -msgstr "E252: Yazıtipi seti adı: %s" +msgid "E80: Error while writing" +msgstr "E80: Yazma sırasında hata" -#, c-format -msgid "Font '%s' is not fixed-width" -msgstr "'%s' yazıtipi sabit aralıklı değil" +msgid "E939: Positive count required" +msgstr "E939: Pozitif sayım gerekiyor" -#, c-format -msgid "E253: Fontset name: %s" -msgstr "E253: Yazıtipi seti adı: %s" +msgid "E81: Using <SID> not in a script context" +msgstr "E81: <SID> bir betik bağlamında kullanılmıyor" #, c-format -msgid "Font0: %s" -msgstr "Yazıtipi0: %s" +msgid "E107: Missing parentheses: %s" +msgstr "E107: Ayraç eksik: %s" + +msgid "E363: pattern uses more memory than 'maxmempattern'" +msgstr "E363: Dizgi 'maxmempattern' ögesinden daha fazla bellek kullanıyor" + +msgid "E749: empty buffer" +msgstr "E749: Boş arabellek" #, c-format -msgid "Font%d: %s" -msgstr "Yazıtipi%d: %s" +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: Arabellek %<PRId64> mevcut değil" + +msgid "E682: Invalid search pattern or delimiter" +msgstr "E682: Geçersiz arama dizgisi veya sınırlandırıcısı" + +msgid "E139: File is loaded in another buffer" +msgstr "E139: Dosya başka bir arabellekte yüklü" #, c-format -msgid "Font%d width is not twice that of font0" -msgstr "Yazıtipi%d genişliği yazıtipi0 genişliğinin iki katı olmalıdır" +msgid "E764: Option '%s' is not set" +msgstr "E764: '%s' seçeneği ayarlanmamış" + +msgid "E850: Invalid register name" +msgstr "E850: Geçersiz yazmaç adı" #, c-format -msgid "Font0 width: %d" -msgstr "Yazıtipi0 genişliği: %d" +msgid "E919: Directory not found in '%s': \"%s\"" +msgstr "E919: '%s' içinde dizin bulunamadı: \"%s\"" + +msgid "E952: Autocommand caused recursive behavior" +msgstr "E952: Otokomut özyineli davranışa neden oldu" + +msgid "E813: Cannot close autocmd window" +msgstr "E813: Otokomut penceresi kapatılamıyor" #, c-format -msgid "Font%d width: %d" -msgstr "Yazıtipi%d genişliği: %d" +msgid "E686: Argument of %s must be a List" +msgstr "E686: %s argümanı bir liste olmalı" + +msgid "E519: Option not supported" +msgstr "E519: Özellik desteklenmiyor" -msgid "E284: Cannot set IC values" -msgstr "E284: Girdi bağlamı değerleri ayarlanamıyor" +msgid "E856: Filename too long" +msgstr "E856: Dosya adı pek uzun" -msgid "E285: Failed to create input context" -msgstr "E285: Girdi bağlamı oluşturulamadı" +msgid "E806: using Float as a String" +msgstr "E806: Kayan Noktalı Değer, bir Dizi yerine kullanılıyor" -msgid "E286: Failed to open input method" -msgstr "E286: Giriş yöntemi açılamadı" +#, c-format +msgid "E5500: autocmd has thrown an exception: %s" +msgstr "E5500: Otokomut, bir istisna attı: %s" -msgid "E287: Warning: Could not set destroy callback to IM" -msgstr "E287: Uyarı: Giriş yöntemine yok etme geri çağırması ayarlanamadı" +msgid "E5520: <Cmd> mapping must end with <CR>" +msgstr "E5520: <Cmd> eşlemlemesi <CR> ile bitmelidir" -msgid "E288: input method doesn't support any style" -msgstr "E288: Giriş yöntemi herhangi bir biçemi desteklemiyor" +msgid "E5521: <Cmd> mapping must end with <CR> before second <Cmd>" +msgstr "E5521: <Cmd> eşlemlemesi ikinci <Cmd>'den önce <CR> ile bitmelidir" -msgid "E289: input method doesn't support my preedit type" -msgstr "E289: Giriş yöntemi benim ön düzenleme türümü desteklemiyor" +#, c-format +msgid "E5522: <Cmd> mapping must not include %s key" +msgstr "E5522: <Cmd> eşlemlemesi %s anahtarını içermemelidir" -msgid "Invalid font specification" -msgstr "Geçersiz yazıtipi belirtimi" +#, c-format +msgid "E5555: API call: %s" +msgstr "E5555: API çağrısı: %s" -msgid "&Dismiss" -msgstr "So&nlandır" +#, c-format +msgid "E5560: %s must not be called in a lua loop callback" +msgstr "E5560: %s, bir lua döngü geri çağrısında çağrılmamalıdır" -msgid "no specific match" -msgstr "belirli bir eşleşme yok" +msgid "E5601: Cannot close window, only floating window would remain" +msgstr "E5601: Pencere kapatılamıyor, yalnızca yüzen pencere açık kalır" -msgid "Vim - Font Selector" -msgstr "Vim - Yazıtipi Seçicisi" +msgid "E5602: Cannot exchange or rotate float" +msgstr "E5602: Kayan noktalı değer değiştirilemiyor veya döndürülemiyor" -msgid "Name:" -msgstr "Ad:" +msgid "E1142: Non-empty string required" +msgstr "E1142: Boş olmayan dizi gerekiyor" -msgid "Show size in Points" -msgstr "Büyüklüğü puntolarla göster" +msgid "E1155: Cannot define autocommands for ALL events" +msgstr "E1155: Otokomutlar TÜM olaylar için tanımlanamıyor" -msgid "Encoding:" -msgstr "Kodlama:" +msgid "E1240: Resulting text too long" +msgstr "E1240: Ortaya çıkan metin pek uzun" -msgid "Font:" -msgstr "Yazıtipi:" +msgid "E1247: Line number out of range" +msgstr "E1247: Satır numarası erim dışında" -msgid "Style:" -msgstr "Biçem:" +msgid "E1249: Highlight group name too long" +msgstr "E1249: Vurgulama grubu adı pek uzun" -msgid "Size:" -msgstr "Büyüklük:" +msgid "search hit TOP, continuing at BOTTOM" +msgstr "Arama dosyanın BAŞINI geçti, dosyanın SONUNDAN sürüyor" + +msgid "search hit BOTTOM, continuing at TOP" +msgstr "Arama dosyanın SONUNU geçti, dosyanın BAŞINDAN sürüyor" + +msgid " line " +msgstr " satır " msgid "E550: Missing colon" msgstr "E550: İki nokta eksik" @@ -1927,8 +2927,8 @@ msgid "No text to be printed" msgstr "Yazdırılacak metin yok" #, c-format -msgid "Printing page %d (%d%%)" -msgstr "Sayfa yazdırılıyor: %d (%d%%)" +msgid "Printing page %d (%zu%%)" +msgstr "Sayfa %d yazdırılıyor (%%%zu)" #, c-format msgid " Copy %d of %d" @@ -1950,28 +2950,28 @@ msgstr "E624: \"%s\" dosyası açılamıyor" #, c-format msgid "E457: Can't read PostScript resource file \"%s\"" -msgstr "E457: PostScript kaynak dosyası \"%s\" okunamıyor" +msgstr "E457: PostScript özkaynak dosyası \"%s\" okunamıyor" #, c-format msgid "E618: file \"%s\" is not a PostScript resource file" -msgstr "E618: \"%s\" dosyası bir PostScript kaynak dosyası değil" +msgstr "E618: \"%s\" dosyası bir PostScript özkaynak dosyası değil" #, c-format msgid "E619: file \"%s\" is not a supported PostScript resource file" -msgstr "E619: \"%s\" dosyası desteklenen bir PostScript kaynak dosyası değil" +msgstr "E619: \"%s\" dosyası desteklenen bir PostScript özkaynak dosyası değil" #, c-format msgid "E621: \"%s\" resource file has wrong version" -msgstr "E621: \"%s\" kaynak dosyası sürümü hatalı" +msgstr "E621: \"%s\" özkaynak dosyası sürümü hatalı" msgid "E673: Incompatible multi-byte encoding and character set." -msgstr "E673: Uyumsuz çoklu bit kodlaması ve karakter seti." +msgstr "E673: Uyumsuz çoklu bayt kodlaması ve karakter kümesi." msgid "E674: printmbcharset cannot be empty with multi-byte encoding." -msgstr "E674: printmbcharset çoklu bit kodlamada boş olamaz" +msgstr "E674: printmbcharset çoklu bayt kodlamada boş olamaz." msgid "E675: No default font specified for multi-byte printing." -msgstr "E675: Çoklu bit yazdırma için öntanımlı yazıtipi ayarlanmamış." +msgstr "E675: Çoklu bayt yazdırma için öntanımlı yazıtipi ayarlanmamış." msgid "E324: Can't open PostScript output file" msgstr "E324: PostScript çıktı dosyası açılamıyor" @@ -1981,14 +2981,14 @@ msgid "E456: Can't open file \"%s\"" msgstr "E456: \"%s\" dosyası açılamıyor" msgid "E456: Can't find PostScript resource file \"prolog.ps\"" -msgstr "E456: PostScript kaynak dosyası \"prolog.ps\" bulunamıyor" +msgstr "E456: PostScript özkaynak dosyası \"prolog.ps\" bulunamıyor" msgid "E456: Can't find PostScript resource file \"cidfont.ps\"" -msgstr "E456: PostScript kaynak dosyası \"cidfont.ps\" bulunamıyor" +msgstr "E456: PostScript özkaynak dosyası \"cidfont.ps\" bulunamıyor" #, c-format msgid "E456: Can't find PostScript resource file \"%s.ps\"" -msgstr "E456: PostScript kaynak dosyası \"%s.ps\" bulunamıyor" +msgstr "E456: PostScript özkaynak dosyası \"%s.ps\" bulunamıyor" #, c-format msgid "E620: Unable to convert to print encoding \"%s\"" @@ -2001,49 +3001,10 @@ msgid "E365: Failed to print PostScript file" msgstr "E365: PostScript dosyası yazdırılamadı" msgid "Print job sent." -msgstr "Yazdırma işi gönderildi" - -msgid "E478: Don't panic!" -msgstr "E478: Panik yok!" - -#, c-format -msgid "E661: Sorry, no '%s' help for %s" -msgstr "E661: Üzgünüm, '%s' yardımı %s için mevcut değil" - -#, c-format -msgid "E149: Sorry, no help for %s" -msgstr "E149: Üzgünüm, %s için yardım mevcut değil" - -#, c-format -msgid "Sorry, help file \"%s\" not found" -msgstr "Üzgünüm, \"%s\" yardım dosyası bulunamadı" - -#, c-format -msgid "E151: No match: %s" -msgstr "E151: Eşleşme bulunamadı: %s" - -#, c-format -msgid "E152: Cannot open %s for writing" -msgstr "E152: %s yazma için açılamıyor" +msgstr "Yazdırma işi gönderildi." -#, c-format -msgid "E153: Unable to open %s for reading" -msgstr "E153: %s okuma için açılamıyor" - -#, c-format -msgid "E670: Mix of help file encodings within a language: %s" -msgstr "E670: Bir dilde yardım dosyası kodlamaları karıştı: %s" - -#, c-format -msgid "E154: Duplicate tag \"%s\" in file %s/%s" -msgstr "E154: Şu dosyada yinelenen \"%s\" etiketi: %s/%s" - -#, c-format -msgid "E150: Not a directory: %s" -msgstr "E150: %s bir dizin değil" - -msgid "E679: recursive loop loading syncolor.vim" -msgstr "E679: syncolor.vim yüklenirken özyineli döngü" +msgid "E424: Too many different highlighting attributes in use" +msgstr "E424: Çok fazla değişik vurgulama kuralları kullanılıyor" #, c-format msgid "E411: highlight group not found: %s" @@ -2082,24 +3043,14 @@ msgstr "E419: Bilinmeyen metin rengi" msgid "E420: BG color unknown" msgstr "E420: Bilinmeyen ardalan rengi" -msgid "E453: UL color unknown" -msgstr "E453: Bilinmeyen alt çizme rengi" - #, c-format msgid "E421: Color name or number not recognized: %s" msgstr "E421: Renk adı veya numarası tanımlanamadı: %s" #, c-format -msgid "E422: terminal code too long: %s" -msgstr "E422: Uçbirim kodu çok uzun: %s" - -#, c-format msgid "E423: Illegal argument: %s" msgstr "E423: İzin verilmeyen argüman: %s" -msgid "E424: Too many different highlighting attributes in use" -msgstr "E424: Çok fazla değişik vurgulama kuralları kullanılıyor" - msgid "E669: Unprintable character in group name" msgstr "E669: Grup adında yazdırılamayan karakter" @@ -2144,20 +3095,17 @@ msgstr "E257: cstag: Etiket bulunamadı" msgid "E563: stat(%s) error: %d" msgstr "E563: stat(%s) hatası: %d" -msgid "E563: stat error" -msgstr "E563: stat hatası" - #, c-format msgid "E564: %s is not a directory or a valid cscope database" -msgstr "E564: %s bir dizin veya geçerli bir cscope veritabanı değil" +msgstr "E564: %s, bir dizin veya geçerli bir cscope veritabanı değil" #, c-format msgid "Added cscope database %s" msgstr "cscope veritabanı %s eklendi" #, c-format -msgid "E262: error reading cscope connection %d" -msgstr "E262: cscope bağlantısı %d okunurken hata" +msgid "E262: error reading cscope connection %<PRIu64>" +msgstr "E262: cscope bağlantısı %<PRIu64> okunurken hata" msgid "E561: unknown cscope search type" msgstr "E561: Bilinmeyen cscope arama türü" @@ -2181,7 +3129,7 @@ msgid "cs_create_connection: fdopen for fr_fp failed" msgstr "cs_create_connection: fr_fp için fdopen başarısız oldu" msgid "E623: Could not spawn cscope process" -msgstr "E623: cscope işlemi ortaya çıkarılamadı" +msgstr "E623: cscope süreci ortaya çıkarılamadı" msgid "E567: no cscope connections" msgstr "E567: cscope bağlantıları yok" @@ -2224,13 +3172,6 @@ msgstr "" " s: Bu \"C\" sembolünü bul\n" " t: Bu metin dizisini bul\n" -#, c-format -msgid "E625: cannot open cscope database: %s" -msgstr "E625: cscope veritabanı açılamıyor: %s" - -msgid "E626: cannot get cscope database information" -msgstr "E626: cscope veritabanı bilgisi alınamıyor" - msgid "E568: duplicate cscope database not added" msgstr "E568: Yinelenen cscope veritabanı eklenmemiş" @@ -2272,389 +3213,109 @@ msgstr "cscope bağlantısı yok\n" msgid " # pid database name prepend path\n" msgstr " # pid veritabanı adı başlangıç yolu\n" -msgid "Lua library cannot be loaded." -msgstr "Lua kitaplığı yüklenemedi." - -msgid "cannot save undo information" -msgstr "Geri al bilgisi kaydedilemiyor" - -msgid "" -"E815: Sorry, this command is disabled, the MzScheme libraries could not be " -"loaded." -msgstr "" -"E815: Üzgünüm, bu komut etkin değil, MzScheme kitaplıkları yüklenemedi." - -msgid "" -"E895: Sorry, this command is disabled, the MzScheme's racket/base module " -"could not be loaded." +msgid "Type number and <Enter> or click with the mouse (q or empty cancels): " msgstr "" -"E895: Üzgünüm, bu komut etkin değil, MzScheme'in racket/base birimi " -"yüklenemedi." - -msgid "invalid expression" -msgstr "geçersiz ifade" - -msgid "expressions disabled at compile time" -msgstr "ifadeler derleme aşamasında kapatılmış" - -msgid "hidden option" -msgstr "gizli seçenek" - -msgid "unknown option" -msgstr "bilinmeyen seçenek" - -msgid "window index is out of range" -msgstr "pencere sırası erimin dışında" - -msgid "couldn't open buffer" -msgstr "arabellek açılamadı" - -msgid "cannot delete line" -msgstr "satır silinemiyor" - -msgid "cannot replace line" -msgstr "satır değiştirilemiyor" - -msgid "cannot insert line" -msgstr "satır eklenemiyor" - -msgid "string cannot contain newlines" -msgstr "dizi \"yeni satır\" imi içeremez" - -msgid "error converting Scheme values to Vim" -msgstr "Scheme değerlerini Vim değerlerine dönüştürürken hata" - -msgid "Vim error: ~a" -msgstr "Vim hatası: ~a" - -msgid "Vim error" -msgstr "Vim hatası" - -msgid "buffer is invalid" -msgstr "arabellek geçersiz" - -msgid "window is invalid" -msgstr "pencere geçersiz" - -msgid "linenr out of range" -msgstr "linenr erimin dışında" - -msgid "not allowed in the Vim sandbox" -msgstr "Vim kum havuzunda izin verilmiyor" +"Sayı girip <Enter>'a veya fare düğmesine basın (q veya boş iptal eder): " -msgid "E836: This Vim cannot execute :python after using :py3" -msgstr "E836: Bu Vim :py3 komutundan sonra :python komutunu çalıştıramaz" +msgid "Type number and <Enter> (q or empty cancels): " +msgstr "Sayı girip <Enter>'a basın (q veya boş iptal eder): " -msgid "" -"E263: Sorry, this command is disabled, the Python library could not be " -"loaded." -msgstr "E263: Üzgünüm, bu komut etkin değil, Python kitaplığı yüklenemedi" +#, c-format +msgid "E1502: Lua failed to grow stack to %i" +msgstr "E1502: Lua, yığını %i olarak büyütemedi" msgid "" -"E887: Sorry, this command is disabled, the Python's site module could not be " -"loaded." +"E5100: Cannot convert given lua table: table should either have a sequence " +"of positive integer keys or contain only string keys" msgstr "" -"E887: Üzgünüm, bu komut etkin değil, Python'un site birimi yüklenemedi." - -msgid "E659: Cannot invoke Python recursively" -msgstr "E659: Python özyineli olarak çalıştırılamıyor" - -msgid "E837: This Vim cannot execute :py3 after using :python" -msgstr "E837: Bu Vim :python komutundan sonra :py3 komutunu çalıştıramaz" +"E5100: Verilen lua tablosu dönüştürülemiyor: Tabloda ya bir tamsayı anahtar " +"sıramalası olmalı veya yalnızca dizi anahtarlar içermeli" -msgid "E265: $_ must be an instance of String" -msgstr "E265: $_ bir dizi örneği olmalıdır" - -msgid "" -"E266: Sorry, this command is disabled, the Ruby library could not be loaded." -msgstr "E266: Üzgünüm, bu komut etkin değil, Ruby kitaplığı yüklenemedi." - -msgid "E267: unexpected return" -msgstr "E267: Beklenmeyen dönüş" - -msgid "E268: unexpected next" -msgstr "E268: Beklenmeyen sonraki" - -msgid "E269: unexpected break" -msgstr "E269: Beklenmeyen kesme" - -msgid "E270: unexpected redo" -msgstr "E270: Beklenmeyen yinele komutu" - -msgid "E271: retry outside of rescue clause" -msgstr "E271: retry, rescue işlecinin dışında" - -msgid "E272: unhandled exception" -msgstr "E272: İşletilemeyen kural dışı durum" +msgid "E5101: Cannot convert given lua type" +msgstr "E5101: Verilen lua türü dönüştürülemiyor" #, c-format -msgid "E273: unknown longjmp status %d" -msgstr "E273: Bilinmeyen longjmp durumu %d" - -msgid "invalid buffer number" -msgstr "Geçersiz arabellek numarası" - -msgid "not implemented yet" -msgstr "henüz uygulanmadı" - -msgid "cannot set line(s)" -msgstr "satır(lar) ayarlanamıyor" - -msgid "invalid mark name" -msgstr "geçersiz im adı" - -msgid "mark not set" -msgstr "im ayarlanmamış" +msgid "E5102: Lua failed to grow stack to %i" +msgstr "E5102: Lua, yığını %i olarak büyütemedi" #, c-format -msgid "row %d column %d" -msgstr "satır %d sütun %d" - -msgid "cannot insert/append line" -msgstr "satır eklenemiyor/iliştirilemiyor" +msgid "Error executing vim.schedule lua callback: %.*s" +msgstr "vim.schedule lua geri çağrısı çalıştırılırken hata: %.*s" -msgid "line number out of range" -msgstr "satır numarası erimin dışında" +msgid "E970: Failed to initialize lua interpreter\n" +msgstr "E970: lua yorumlayıcısı ilklendirilemedi\n" -msgid "unknown flag: " -msgstr "geçersiz bayrak: " - -msgid "unknown vimOption" -msgstr "geçersiz vimOption" - -msgid "keyboard interrupt" -msgstr "klavye araya girdi" - -msgid "cannot create buffer/window command: object is being deleted" -msgstr "arabellek/pencere komutu oluşturulamadı: öge şu anda siliniyor" - -msgid "" -"cannot register callback command: buffer/window is already being deleted" -msgstr "geri çağırma komutu kaydedilemiyor: arabellek/pencere zaten siliniyor" - -msgid "" -"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim." -"org" -msgstr "" -"E280: ONULMAZ TCL HATASI: Başvuru listesi hasar görmüş! Lütfen bunu vim-" -"dev@vim.org adresine bildirin" - -msgid "cannot register callback command: buffer/window reference not found" -msgstr "" -"geri çağırma komutu kaydedilemiyor: arabellek/pencere başvurusu bulunamadı" - -msgid "" -"E571: Sorry, this command is disabled: the Tcl library could not be loaded." -msgstr "E571: Üzgünüm, bu komut etkin değil: Tcl kitaplığı yüklenemedi." +msgid "E970: Failed to initialize builtin lua modules\n" +msgstr "E970: Gömülü lua modülleri ilklendirilemedi\n" #, c-format -msgid "E572: exit code %d" -msgstr "E572: %d çıkış kodu" - -msgid "cannot get line" -msgstr "satır alınamıyor" - -msgid "Unable to register a command server name" -msgstr "Bir komut sunucusu adı kaydedilemiyor" - -msgid "E248: Failed to send command to the destination program" -msgstr "E248: Hedef programa komut gönderimi başarısız oldu" +msgid "E5114: Error while converting print argument #%i: %.*s" +msgstr "E5114: Yazdırma argümanı #%i dönüştürülürken hata: %.*s" #, c-format -msgid "E573: Invalid server id used: %s" -msgstr "E573: Geçersiz sunucu kimliği kullanıldı: %s" - -msgid "E251: VIM instance registry property is badly formed. Deleted!" -msgstr "E251: VİM oturumu kayıt değeri düzgün oluşturulmamış. Silindi!" +msgid "E5115: Error while loading debug string: %.*s" +msgstr "E5115: Hata ayıklama dizisi yüklenirken hata: %.*s" #, c-format -msgid "%ld lines to indent... " -msgstr "girintilenecek %ld satır kaldı... " +msgid "E5116: Error while calling debug string: %.*s" +msgstr "E5116: Hata ayıklama dizisi çağrılırken hata: %.*s" #, c-format -msgid "%ld line indented " -msgid_plural "%ld lines indented " -msgstr[0] "%ld satır girintilendi " -msgstr[1] "%ld satır girintilendi" - -msgid " Keyword completion (^N^P)" -msgstr " Anahtar sözcük tamamlaması (^N^P)" - -msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" -msgstr " ^X kipi (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" - -msgid " Whole line completion (^L^N^P)" -msgstr " Tam satır tamamlaması (^L^N^P)" - -msgid " File name completion (^F^N^P)" -msgstr " Dosya adı tamamlaması (^F^N^P)" - -msgid " Tag completion (^]^N^P)" -msgstr " Etiket tamamlaması (^]^N^P)" - -msgid " Path pattern completion (^N^P)" -msgstr " Yol dizgisi tamamlaması (^N^P)" - -msgid " Definition completion (^D^N^P)" -msgstr " Tanım tamamlaması (^D^N^P)" - -msgid " Dictionary completion (^K^N^P)" -msgstr " Sözlük tamamlaması (^K^N^P)" - -msgid " Thesaurus completion (^T^N^P)" -msgstr " Eşanlamlılar sözlüğü tamamlaması (^T^N^P)" - -msgid " Command-line completion (^V^N^P)" -msgstr " Komut satırı tamamlaması (^V^N^P)" - -msgid " User defined completion (^U^N^P)" -msgstr " Kullanıcı tanımlı tamamlamalar (^U^N^P)" - -msgid " Omni completion (^O^N^P)" -msgstr " Omni tamamlaması (^O^N^P)" - -msgid " Spelling suggestion (s^N^P)" -msgstr " Yazım önerisi (s^N^P)" - -msgid " Keyword Local completion (^N^P)" -msgstr " Dahili anahtar sözcük tamamlaması (^N^P)" - -msgid "Hit end of paragraph" -msgstr "Paragrafın sonuna varıldı" - -msgid "E840: Completion function deleted text" -msgstr "E840: Tamamlama işlevi metni sildi" - -msgid "'dictionary' option is empty" -msgstr "'dictionary' seçeneği boş" - -msgid "'thesaurus' option is empty" -msgstr "'thesaurus' seçeneği boş" +msgid "E5108: Error executing Lua function: %.*s" +msgstr "E5108: Lua işlevi çalıştırılırken hata: %.*s" #, c-format -msgid "Scanning dictionary: %s" -msgstr "Sözlük taranıyor: %s" - -msgid " (insert) Scroll (^E/^Y)" -msgstr " (ekle) Kaydır (^E/^Y)" - -msgid " (replace) Scroll (^E/^Y)" -msgstr " (değiştir) Kaydır (^E/^Y)" - -msgid "E785: complete() can only be used in Insert mode" -msgstr "E785: complete() yalnızca Ekleme kipinde kullanılabilir" +msgid "E5107: Error loading lua %.*s" +msgstr "E5107: lua %.*s yüklenirken hata" #, c-format -msgid "Scanning: %s" -msgstr "Taranıyor: %s" +msgid "E5108: Error executing lua %.*s" +msgstr "E5108: lua %.*s çalıştırılırken hata" -msgid "Scanning tags." -msgstr "Etiketler taranıyor..." - -msgid "match in file" -msgstr "dosya içinde eşleşme" - -msgid " Adding" -msgstr " Ekleniyor" - -msgid "-- Searching..." -msgstr "-- Aranıyor..." - -msgid "Back at original" -msgstr "Başlangıca geri dönüldü" - -msgid "Word from other line" -msgstr "Sözcük diğer satırdan" +#, c-format +msgid "Error executing lua callback: %.*s" +msgstr "lua geri çağrısı çalıştırılırken hata: %.*s" -msgid "The only match" -msgstr "Tek eşleşen" +msgid "cannot save undo information" +msgstr "Geri al bilgisi kaydedilemiyor" #, c-format -msgid "match %d of %d" -msgstr "eşleşme %d/%d" +msgid "E5109: Error loading lua: %.*s" +msgstr "E5109: lua yüklenirken hata: %.*s" #, c-format -msgid "match %d" -msgstr "eşleşme %d" - -msgid "E920: _io file requires _name to be set" -msgstr "E920: _io dosyası _name ayarlı olmasını gerektirir" - -msgid "E915: in_io buffer requires in_buf or in_name to be set" -msgstr "E915: in_io arabelleği in_buf veya in_name ayarlı olmasını gerektirir" +msgid "E5110: Error executing lua: %.*s" +msgstr "E5110: lua çalıştırılırken hata: %.*s" #, c-format -msgid "E918: buffer must be loaded: %s" -msgstr "E918: Arabellek yüklenmiş olmalıdır: %s" - -msgid "E916: not a valid job" -msgstr "E916: Geçerli bir iş değil" +msgid "E5111: Error calling lua: %.*s" +msgstr "E5111: lua çağrılırken hata: %.*s" #, c-format -msgid "E491: json decode error at '%s'" -msgstr "E491: '%s' konumunda json çözümü hatası" +msgid "E5112: Error while creating lua chunk: %.*s" +msgstr "E5112: lua parçası oluşturulurken hata: %.*s" #, c-format -msgid "E938: Duplicate key in JSON: \"%s\"" -msgstr "E938: JSON'da yinelenmiş anahtar: \"%s\"" +msgid "E5113: Error while calling lua chunk: %.*s" +msgstr "E5113: lua parçası çağrılırken hata: %.*s" #, c-format -msgid "E899: Argument of %s must be a List or Blob" -msgstr "E899: %s argümanı bir liste veya ikili geniş nesne olmalıdır" - -msgid "E900: maxdepth must be non-negative number" -msgstr "E900: maxdepth negatif olmayan bir sayı olmalı" - -msgid "flatten() argument" -msgstr "flatten() argümanı" +msgid "Error executing vim._expand_pat: %.*s" +msgstr "vim._expand_pat çalıştırılırken hata: %.*s" #, c-format -msgid "E696: Missing comma in List: %s" -msgstr "E696: Listede virgül eksik: %s" - -msgid "sort() argument" -msgstr "sort() argümanı" - -msgid "uniq() argument" -msgstr "uniq() argümanı" - -msgid "E702: Sort compare function failed" -msgstr "E702: Sıralayıp karşılaştırma işlevi başarısız oldu" - -msgid "E882: Uniq compare function failed" -msgstr "E882: Benzersizlik karşılaştırma işlevi başarısız oldu" - -msgid "map() argument" -msgstr "map() argümanı" - -msgid "mapnew() argument" -msgstr "mapnew() argümanı" - -msgid "filter() argument" -msgstr "filter() argümanı" - -msgid "add() argument" -msgstr "add() argümanı" - -msgid "extendnew() argument" -msgstr "extendnew() argümanı" - -msgid "insert() argument" -msgstr "insert() argümanı" - -msgid "remove() argument" -msgstr "remove() argümanı" - -msgid "reverse() argument" -msgstr "reverse() argümanı" +msgid "Error executing vim.on_key Lua callback: %.*s" +msgstr "vim.on_key Lua geri çağrısı çalıştırılırken hata: %.*s" #, c-format -msgid "Current %slanguage: \"%s\"" -msgstr "Şu anki %sdil: \"%s\"" +msgid "Error executing Lua callback: %.*s" +msgstr "Lua geri çağrısı çalıştırılırken hata: %.*s" -#, c-format -msgid "E197: Cannot set language to \"%s\"" -msgstr "E197: \"%s\" diline ayarlanamıyor" +msgid "Argument missing after" +msgstr "Şundan sonra argüman eksik:" + +msgid "Garbage after option argument" +msgstr "Seçenek argümanından sonra anlamsız veri" msgid "Unknown option argument" msgstr "Bilinmeyen seçenek argümanı" @@ -2662,440 +3323,202 @@ msgstr "Bilinmeyen seçenek argümanı" msgid "Too many edit arguments" msgstr "Çok fazla düzenleme argümanı" -msgid "Argument missing after" -msgstr "Şundan sonra argüman eksik:" - -msgid "Garbage after option argument" -msgstr "Seçenek argümanından sonra anlamsız veri" - msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" msgstr "Çok fazla \"+komut\", \"-c komut\" veya \"--cmd komut\" argümanı" -msgid "Invalid argument for" -msgstr "Şunun için geçersiz argüman:" - #, c-format -msgid "%d files to edit\n" -msgstr "%d dosya düzenleniyor\n" +msgid "E5421: Failed to open stdin: %s" +msgstr "E5421: stdin açılamadı: %s" -msgid "netbeans is not supported with this GUI\n" -msgstr "NetBeans bu grafik arabirimde desteklenmiyor\n" - -msgid "'-nb' cannot be used: not enabled at compile time\n" -msgstr "'-nb' kullanılamaz: Derleme sırasında etkinleştirilmemiş\n" - -msgid "This Vim was not compiled with the diff feature." -msgstr "Bu Vim karşılaştırma özelliği ile derlenmemiş" - -msgid "Attempt to open script file again: \"" -msgstr "Betik dosyası yeniden açılmaya çalışılıyor: \"" +#, c-format +msgid "Attempt to open script file again: \"%s %s\"\n" +msgstr "Betik dosyası yeniden açılmaya çalışılıyor: \"%s %s\"\n" -msgid "Cannot open for reading: \"" -msgstr "Okuma için açılamıyor: \"" +#, c-format +msgid "Cannot open for reading: \"%s\": %s\n" +msgstr "Okuma için açılamıyor: \"%s\": %s\n" msgid "Cannot open for script output: \"" msgstr "Betik çıktısı için açılamıyor: \"" -msgid "Vim: Error: Failure to start gvim from NetBeans\n" -msgstr "Vim: Hata: gvim'i NetBeans içinden başlatma başarısız oldu\n" - -msgid "Vim: Error: This version of Vim does not run in a Cygwin terminal\n" -msgstr "Vim: Hata: Vim'in bu sürümü bir Cygwin uçbirimi içinde çalışmaz\n" - -msgid "Vim: Warning: Output is not to a terminal\n" -msgstr "Vim: Uyarı: Çıktı bir uçbirime değil\n" - -msgid "Vim: Warning: Input is not from a terminal\n" -msgstr "Vim: Uyarı: Girdi bir uçbirimden değil\n" +msgid "--embed conflicts with -es/-Es" +msgstr "--embed, -es/-Es ile çakışıyor" msgid "pre-vimrc command line" msgstr "vimrc uygulanma öncesi komut satırı" #, c-format +msgid "E5422: Conflicting configs: \"%s\" \"%s\"" +msgstr "E5422: Çakışan yapılandırmalar: \"%s\" \"%s\"" + +#, c-format msgid "E282: Cannot read from \"%s\"" msgstr "E282: Şuradan okunamıyor: \"%s\"" msgid "" "\n" -"More info with: \"vim -h\"\n" +"More info with \"" msgstr "" "\n" -"Daha fazla bilgi için: \"vim -h\"\n" - -msgid "[file ..] edit specified file(s)" -msgstr "[dosya ..] belirlenen dosyaları düzenle" - -msgid "- read text from stdin" -msgstr "- stdin'den metni oku" +"Daha fazla bilgi için: \"" -msgid "-t tag edit file where tag is defined" -msgstr "-t etiket etiket tanımlanan dosyaları düzenle" +msgid "Usage:\n" +msgstr "Kullanım:\n" -msgid "-q [errorfile] edit file with first error" -msgstr "-q [hatalıd.] hata içeren ilk dosyayı düzenle" - -msgid "" -"\n" -"\n" -"Usage:" +msgid " nvim [options] [file ...] Edit file(s)\n" msgstr "" -"\n" -"\n" -"Kullanım:" - -msgid " vim [arguments] " -msgstr " vim [argümanlar] " +"nvim [seçenekler] [dosya ...] Dosyaları düzenle\n" -msgid "" -"\n" -" or:" +msgid " nvim [options] -t <tag> Edit file where tag is defined\n" msgstr "" -"\n" -" veya:" +"nvim [seçenekler] -t <etiket> Etiketin tanımlandığı dosyayı düzenle\n" -msgid "" -"\n" -"Where case is ignored prepend / to make flag upper case" +msgid " nvim [options] -q [errorfile] Edit file with first error\n" msgstr "" -"\n" -"BÜYÜK/küçük harfin yok sayıldığı yerde bayrağı BÜYÜK harfli yapmak için " -"başına / koyun" +"nvim [seçenekler] -q [hatadosyası] İlk hatalı dosyayı düzenle\n" msgid "" "\n" -"\n" -"Arguments:\n" +"Options:\n" msgstr "" "\n" -"\n" -"Değişkenler:\n" - -msgid "--\t\t\tOnly file names after this" -msgstr "--\t\t\tBundan sonra yalnızca dosya adları" - -msgid "--literal\t\tDon't expand wildcards" -msgstr "--literal\t\tJoker karakterleri genişletme!" - -msgid "-register\t\tRegister this gvim for OLE" -msgstr "-register\t\tBu gvim'i OLE için kaydet" - -msgid "-unregister\t\tUnregister gvim for OLE" -msgstr "-unregister\t\tgvim'in OLE kaydını sil" - -msgid "-g\t\t\tRun using GUI (like \"gvim\")" -msgstr "-g\t\t\tGrafik arabirim kullanarak çalıştır (\"gvim\" gibi)" - -msgid "-f or --nofork\tForeground: Don't fork when starting GUI" -msgstr "-f veya --nofork\tÖnalan: Grafik arabirim başlatılırken çatallama!" - -msgid "-v\t\t\tVi mode (like \"vi\")" -msgstr "-v\t\t\tVi kipi (\"vi\" gibi)" - -msgid "-e\t\t\tEx mode (like \"ex\")" -msgstr "-e\t\t\tEx kipi (\"ex\" gibi)" - -msgid "-E\t\t\tImproved Ex mode" -msgstr "-E\t\t\tGeliştirilmiş Ex kipi" - -msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")" -msgstr "-s\t\t\tSessiz (toplu iş) kipi (yalnızca \"ex\" için)" - -msgid "-d\t\t\tDiff mode (like \"vimdiff\")" -msgstr "-d\t\t\tKarşılaştırma kipi (like \"vimdiff\")" - -msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" -msgstr "-y\t\t\tKolay kip (\"evim\" gibi, kipsiz)" - -msgid "-R\t\t\tReadonly mode (like \"view\")" -msgstr "-R\t\t\tSaltokunur kip (\"view\" gibi)" - -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tKısıtlanmış kip (\"rvim\" gibi)" - -msgid "-m\t\t\tModifications (writing files) not allowed" -msgstr "-m\t\t\tDeğişikliklere (dosya yazma) izin verilmez" - -msgid "-M\t\t\tModifications in text not allowed" -msgstr "-M\t\t\tMetinde değişikliklere izin verilmez" - -msgid "-b\t\t\tBinary mode" -msgstr "-b\t\t\tİkili kip" - -msgid "-l\t\t\tLisp mode" -msgstr "-l\t\t\tLisp kipi" - -msgid "-C\t\t\tCompatible with Vi: 'compatible'" -msgstr "-C\t\t\tVi ile uyumlu: 'compatible'" +"Seçenekler:\n" -msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'" -msgstr "-N\t\t\tTümüyle Vi uyumlu değil: 'nocompatible'" - -msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" -msgstr "-V[N][dosya]\t\tAyrıntılı bilgi ver [N düzeyi] [iletileri dosyaya yaz]" - -msgid "-D\t\t\tDebugging mode" -msgstr "-D\t\t\tHata ayıklama kipi" - -msgid "-n\t\t\tNo swap file, use memory only" -msgstr "-n\t\t\tTakas dosyası kullanma, yalnızca belleğe yaz" - -msgid "-r\t\t\tList swap files and exit" -msgstr "-r\t\t\tTakas dosyalarını listele ve çık" - -msgid "-r (with file name)\tRecover crashed session" -msgstr "-r (dosya adı ile)\tÇöken oturumu kurtar" - -msgid "-L\t\t\tSame as -r" -msgstr "-L\t\t\t-r ile aynı" - -msgid "-f\t\t\tDon't use newcli to open window" -msgstr "-f\t\t\tPencere açmak için yeni komut satırı arabirimi kullanma" - -msgid "-dev <device>\t\tUse <device> for I/O" -msgstr "-dev <aygıt>\t\tGirdi/Çıktı için <aygıt>'ı kullan" - -msgid "-A\t\t\tStart in Arabic mode" -msgstr "-A\t\t\tArapça kipinde başla" - -msgid "-H\t\t\tStart in Hebrew mode" -msgstr "-H\t\t\tİbranca kipinde başla" - -msgid "-T <terminal>\tSet terminal type to <terminal>" -msgstr "-T <uçbirim>\t\tUçbirim türünü <uçbirim>'e ayarla" - -msgid "--not-a-term\t\tSkip warning for input/output not being a terminal" -msgstr "--not-a-term\t\tGirdi/Çıktının bir uçbirime olmadığı uyarısını atla" - -msgid "--ttyfail\t\tExit if input or output is not a terminal" -msgstr "--ttyfail\t\tGirdi veya çıktı bir uçbirime değilse çık" - -msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" -msgstr "-u <vimrc>\t\tHerhangi bir .vimrc yerine <vimrc> kullan" - -msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc" -msgstr "-U <gvimrc>\t\tHerhangi bir .gvimrc yerine <gvimrc> kullan" - -msgid "--noplugin\t\tDon't load plugin scripts" -msgstr "--noplugin\t\tEklenti betiklerini yükleme!" - -msgid "-p[N]\t\tOpen N tab pages (default: one for each file)" -msgstr "-p[N]\t\tN sekme sayfası aç (öntanımlı: her dosya için bir sekme)" - -msgid "-o[N]\t\tOpen N windows (default: one for each file)" -msgstr "-o[N]\t\tN pencere aç (öntanımlı: her dosya için bir pencere)" - -msgid "-O[N]\t\tLike -o but split vertically" -msgstr "-O[N]\t\t-o gibi, yalnızca dikey bölerek" - -msgid "+\t\t\tStart at end of file" -msgstr "+\t\t\tDosyanın sonunda başlat" - -msgid "+<lnum>\t\tStart at line <lnum>" -msgstr "+<satırno>\t\t<satırno> numaralı satırda başlat" +msgid " -- Only file names after this\n" +msgstr "" +"-- Yalnızca bundan sonraki dosya adları\n" -msgid "--cmd <command>\tExecute <command> before loading any vimrc file" -msgstr "--cmd <komut>\tHerhangi bir vimrc dosyası yüklemeden <komut> çalıştır" +msgid " + Start at end of file\n" +msgstr "" +"+ Dosyanın sonunda başlat\n" -msgid "-c <command>\t\tExecute <command> after loading the first file" -msgstr "-c <komut>\t\tİlk dosyayı yükleyip <komut> komutunu çalıştır" +msgid " --cmd <cmd> Execute <cmd> before any config\n" +msgstr "" +"--cmd <komut> Herhangi bir yapılandırma öncesi <komut> çalıştır\n" -msgid "-S <session>\t\tSource file <session> after loading the first file" -msgstr "-S <oturum>\t\tİlk dosyayı yükleyip <oturum> dosyasını kaynak al" +msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n" +msgstr "" +"+<komut>, -c <komut> Yapılandırma ve ilk dosya sonrası <komut> çalıştır\n" -msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>" -msgstr "-s <betikgir>\tNormal kip komutlarını <betikgir> dosyasından oku" +msgid " -b Binary mode\n" +msgstr "" +"-b İkili kip\n" -msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>" -msgstr "-w <betikçık>\tGirilen tüm komutları <betikçık> dosyasına iliştir" +msgid " -d Diff mode\n" +msgstr "" +"-d Diff kipi\n" -msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>" -msgstr "-W <betikçık>\tGirilen tüm komutları <betikçık> dosyasına yaz" +msgid " -e, -E Ex mode\n" +msgstr "" +"-e, -E Ex kipi\n" -msgid "-x\t\t\tEdit encrypted files" -msgstr "-x\t\t\tŞifrelenmiş dosyaları düzenle" +msgid " -es, -Es Silent (batch) mode\n" +msgstr "" +"-es, -Es Sessiz (toplu iş) kipi\n" -msgid "-display <display>\tConnect Vim to this particular X-server" -msgstr "-display <ekran>\tVim'i bu belirtilen X sunucusuna bağla" +msgid " -h, --help Print this help message\n" +msgstr "" +"-h, --help Bu yardım iletisini yazdır\n" -msgid "-X\t\t\tDo not connect to X server" -msgstr "-X\t\t\tX sunucusuna bağlama" +msgid " -i <shada> Use this shada file\n" +msgstr "" +"-i <shada> Bu shada (paylaşılan veri) dosyasını kullan\n" -msgid "--remote <files>\tEdit <files> in a Vim server if possible" -msgstr "--remote <dosya>\tOlanaklıysa bir Vim sunucusuda <dosya> düzenler" +msgid " -m Modifications (writing files) not allowed\n" +msgstr "" +"-m Değişikliklere (dosya yazmaya) izin verme\n" -msgid "--remote-silent <files> Same, don't complain if there is no server" -msgstr "--remote-silent <dosya> Aynısı, yalnızca sunucu yoksa şikayet etmez" +msgid " -M Modifications in text not allowed\n" +msgstr "" +"-M Metinde değişikliklere izin verme\n" -msgid "" -"--remote-wait <files> As --remote but wait for files to have been edited" +msgid " -n No swap file, use memory only\n" msgstr "" -"--remote-wait <dosya> ---remote gibi, yalnızca düzenlenme bitişini bekle" +"-n Takas dosyası yok, yalnızca belleği kullan\n" -msgid "" -"--remote-wait-silent <files> Same, don't complain if there is no server" +msgid " -o[N] Open N windows (default: one per file)\n" msgstr "" -"--remote-wait-silent <dosya> Aynısı, yalnızca sunucu yoksa şikayet etmez" +"-o[N] N pencere aç (öntanımlı: dosya başına bir tane)\n" msgid "" -"--remote-tab[-wait][-silent] <files> As --remote but use tab page per file" +" -O[N] Open N vertical windows (default: one per file)\n" msgstr "" -"--remote-tab[-wait][-silent] <dosya> --remote, artı sekme sayfası kullanır" +"-O[N] N dikey pencere aç (öntanımlı: dosya başına bir tane)\n" -msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit" +msgid " -p[N] Open N tab pages (default: one per file)\n" msgstr "" -"--remote-send <anahtar>\tBir Vim sunucusuna <anahtar> gönderir ve çıkar" +"-p[N] N sekme sayfası aç (öntanımlı: dosya başına bir tane)\n" -msgid "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result" +msgid " -r, -L List swap files\n" msgstr "" -"--remote-expr <ifade>\t<ifade>'leri bir Vim sunucusunda değerlendirir ve " -"sonuçları yazdırır" - -msgid "--serverlist\t\tList available Vim server names and exit" -msgstr "--serverlist\t\tMevcut Vim sunucu adlarını listeler ve çıkar" - -msgid "--servername <name>\tSend to/become the Vim server <name>" -msgstr "--servername <ad>\t<ad> Vim sunucusuna gönder veya sunucu ol" +"-r, -L Takas dosyalarını listele\n" -msgid "--startuptime <file>\tWrite startup timing messages to <file>" -msgstr "--startuptime <dsy>\tBaşlangıç zamanlama iletilerini <dsy>'ya yaz" - -msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" -msgstr "-i <viminfo>\t\t.viminfo yerine <viminfo> kullan" - -msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo" -msgstr "--clean\t\t'nocompatible', Vim öntanımlıları, eklenti-viminfo yok" - -msgid "-h or --help\tPrint Help (this message) and exit" -msgstr "-h veya --help\tYardımı (bu iletiyi) yazdırır ve çıkar" - -msgid "--version\t\tPrint version information and exit" -msgstr "--version\t\tSürüm bilgisini yazdırır ve çıkar" - -msgid "" -"\n" -"Arguments recognised by gvim (Motif version):\n" +msgid " -r <file> Recover edit state for this file\n" msgstr "" -"\n" -"gvim tarafından tanınan argümanlar (Motif sürümü):\n" +"-r <dosya> Bu dosyanın düzenleme durumunu kurtar\n" -msgid "" -"\n" -"Arguments recognised by gvim (neXtaw version):\n" +msgid " -R Read-only mode\n" msgstr "" -"\n" -"gvim tarafından tanınan argümanlar (neXtaw sürümü):\n" +"-R Saltokunur kip\n" -msgid "" -"\n" -"Arguments recognised by gvim (Athena version):\n" +msgid " -S <session> Source <session> after loading the first file\n" msgstr "" -"\n" -"gvim tarafından tanınan argümanlar (Athena sürümü):\n" - -msgid "-display <display>\tRun Vim on <display>" -msgstr "-display <ekran>\tVim'i <ekran>'da çalıştır" +"-S <oturum> İlk dosyayı yükledikten sonra <oturum>'u kaynak al\n" -msgid "-iconic\t\tStart Vim iconified" -msgstr "-iconic\t\tVim'i simge durumunda başlat" - -msgid "-background <color>\tUse <color> for the background (also: -bg)" -msgstr "-background <renk>\tArdalanı <renk> yap (kısa: -bg)" +msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n" +msgstr "" +"-s <betikgir> Normal kip komutlarını <betikgir>'den oku\n" -msgid "-foreground <color>\tUse <color> for normal text (also: -fg)" -msgstr "-foreground <renk>\tNormal metin için <renk> kullan (kısa: -fg)" +msgid " -u <config> Use this config file\n" +msgstr "" +"-u <yapılandırma> Bu yapılandırma dosyasını kullan\n" -msgid "-font <font>\t\tUse <font> for normal text (also: -fn)" -msgstr "-font <font>\t\tNormal metin için <font> yazıtipini kullan (kısa: -fn)" +msgid " -v, --version Print version information\n" +msgstr "" +"-v, --version Sürüm bilgisini yazdır\n" -msgid "-boldfont <font>\tUse <font> for bold text" -msgstr "-boldfont <font>\tKalın metin için <font> yazıtipini kullan" +msgid " -V[N][file] Verbose [level][file]\n" +msgstr "" +"-V[N][dosya] Ayrıntılı bilgi ver [düzey][dosya]\n" -msgid "-italicfont <font>\tUse <font> for italic text" -msgstr "-italicfont <font>\tEğik metin için <font> yazıtipini kullan" +msgid " --api-info Write msgpack-encoded API metadata to stdout\n" +msgstr "" +"--api-info stdout'a msgpack kodlu API üstverisi yaz\n" -msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)" -msgstr "-geometry <geom>\tBaşlangıç boyutları için <geom> kullan (kısa -geom)" +msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n" +msgstr "" +"--embed stdin/stdout'u msgpack-rpc kanalı olarak kullan\n" -msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)" -msgstr "-borderwidth <gnşlk>\t<gnşlk> kenar genişliği kullan (kısa: -bw)" +msgid " --headless Don't start a user interface\n" +msgstr "" +"--headless Bir kullanıcı arayüzü başlatma\n" -msgid "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)" +msgid " --listen <address> Serve RPC API from this address\n" msgstr "" -"-scrollbarwidth <gnşlk> Kaydırma çubuğu için <gnşlk> genişlik (kısa: -sw)" +"--listen <adres> Bu adresten RPC API'si sun\n" -msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)" +msgid " --noplugin Don't load plugins\n" msgstr "" -"-menuheight <yükseklik>\t<yükseklik> menü çubuğu yüksekliği (kısa: -mh)" +"--noplugin Eklentileri yükleme\n" -msgid "-reverse\t\tUse reverse video (also: -rv)" -msgstr "-reverse\t\tTersine dönmüş video kullan (kısa: -rv)" +msgid " --remote[-subcommand] Execute commands remotely on a server\n" +msgstr "" +"--remote[-subcommand] Bir sunucuda komutları uzaktan çalıştır\n" -msgid "+reverse\t\tDon't use reverse video (also: +rv)" -msgstr "+reverse\t\tTersine dönmüş video kullanma (kısa: +rv)" +msgid " --server <address> Specify RPC server to send commands to\n" +msgstr "" +"--server <adres> Komut gönderilecek RPC sunucusunu belirt\n" -msgid "-xrm <resource>\tSet the specified resource" -msgstr "-xrm <kaynak>\tBelirtilen kaynağı ayarla" +msgid " --startuptime <file> Write startup timing messages to <file>\n" +msgstr "" +"--startuptime <dosya> Başlangıç zamanlama iletilerini <dosya>'ya yaz\n" msgid "" "\n" -"Arguments recognised by gvim (GTK+ version):\n" +"See \":help startup-options\" for all options.\n" msgstr "" "\n" -"gvim tarafından tanınan argümanlar (GTK+ sürümü):\n" - -msgid "-display <display>\tRun Vim on <display> (also: --display)" -msgstr "-display <ekran>\tVim'i <ekran>'da çalıştır (veya: --display)" - -msgid "--role <role>\tSet a unique role to identify the main window" -msgstr "--role <rol>\tAna pencereyi tanımlamak için eşsiz bir rol ayarla" - -msgid "--socketid <xid>\tOpen Vim inside another GTK widget" -msgstr "--socketid <xid>\tBaşka bir GTK parçacığında Vim'i aç" - -msgid "--echo-wid\t\tMake gvim echo the Window ID on stdout" -msgstr "--echo-wid\t\tgvim'in pencere kimliğini stdout'ta echo yapmasını sağla" - -msgid "-P <parent title>\tOpen Vim inside parent application" -msgstr "-P <üst başlık>\tVim'i üst uygulama içinde aç" - -msgid "--windowid <HWND>\tOpen Vim inside another win32 widget" -msgstr "--windowid <HWND>\tVim'i başka bir win32 parçacığı içinde aç" - -#, c-format -msgid "E224: global abbreviation already exists for %s" -msgstr "E224: %s için global kısaltma hâlihazırda var" - -#, c-format -msgid "E225: global mapping already exists for %s" -msgstr "E225: %s için global eşlemleme hâlihazırda var " - -#, c-format -msgid "E226: abbreviation already exists for %s" -msgstr "E226: %s için kısaltma hâlihazırda var" - -#, c-format -msgid "E227: mapping already exists for %s" -msgstr "E227: %s için eşlemleme hâlihazırda var" - -msgid "No abbreviation found" -msgstr "Kısaltma bulunamadı" - -msgid "No mapping found" -msgstr "Eşlemleme bulunamadı" - -msgid "E228: makemap: Illegal mode" -msgstr "E228: makemap: İzin verilmeyen kip" - -msgid "E460: entries missing in mapset() dict argument" -msgstr "E460: mapset() sözlük argümanında eksik girdiler" - -#, c-format -msgid "E357: 'langmap': Matching character missing for %s" -msgstr "E357: 'langmap': %s için eşleşen karakter eksik" - -#, c-format -msgid "E358: 'langmap': Extra characters after semicolon: %s" -msgstr "E358: 'langmap': Noktalı virgülden sonra ek karakterler: %s" +"Tüm seçenekler için \":help startup-options\" yazın.\n" msgid "No marks set" msgstr "İm ayarlanmamış" @@ -3126,30 +3549,44 @@ msgstr "" "dğşklk satr stn metin" #, c-format -msgid "E799: Invalid ID: %d (must be greater than or equal to 1)" -msgstr "E799: Geçersiz ID: %d (1'e eşit veya 1'den büyük olmalıdır)" +msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)" +msgstr "E799: Geçersiz kimlik: %<PRId64> (1'e eşit veya 1'den büyük olmalıdır)" #, c-format -msgid "E801: ID already taken: %d" -msgstr "E801: Kullanımda olan ID: %d" +msgid "E801: ID already taken: %<PRId64>" +msgstr "E801: Kimlik halihazırda kullanımda: %<PRId64>" -msgid "E290: List or number required" -msgstr "E290: Liste veya numara gerekiyor" +#, c-format +msgid "E5030: Empty list at position %d" +msgstr "E5030: %d konumunda boş liste" #, c-format -msgid "E802: Invalid ID: %d (must be greater than or equal to 1)" -msgstr "E802: Geçersiz ID: %d (1'e eşit veya 1'den büyük olmalıdır)" +msgid "E5031: List or number required at position %d" +msgstr "E5031: %d konumunda liste veya numara gerekiyor" #, c-format -msgid "E803: ID not found: %d" -msgstr "E803: ID bulunamadı: %d" +msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)" +msgstr "E802: Geçersiz kimlik: %<PRId64> (1'e eşit veya 1'den büyük olmalıdır)" #, c-format -msgid "E798: ID is reserved for \":match\": %d" -msgstr "E798: ID, \":match\" için ayrılmış: %d" +msgid "E803: ID not found: %<PRId64>" +msgstr "E803: Kimlik bulunamadı: %<PRId64>" -msgid "E543: Not a valid codepage" -msgstr "E543: Geçerli bir kod sayfası değil" +#, c-format +msgid "E474: List item %d is either not a dictionary or an empty one" +msgstr "E474: Liste ögesi %d, ya bir sözlük değil ya da boş bir sözlük" + +#, c-format +msgid "E474: List item %d is missing one of the required keys" +msgstr "E474: Liste ögesi %d, gerekli anahtarlardan birini içermiyor" + +#, c-format +msgid "E798: ID is reserved for \":match\": %<PRId64>" +msgstr "E798: Kimlik, \":match\" için ayrılmış: %<PRId64>" + +#, c-format +msgid "E798: ID is reserved for \"match\": %<PRId64>" +msgstr "E798: Kimlik, \":match\" için ayrılmış: %<PRId64>" msgid "E293: block was not locked" msgstr "E293: Blok kilitlenmemişti" @@ -3178,9 +3615,6 @@ msgstr "E298: 1 numaralı blok alınmadı mı?" msgid "E298: Didn't get block nr 2?" msgstr "E298: 2 numaralı blok alınmadı mı?" -msgid "E843: Error while updating swap file crypt" -msgstr "E843: Takas dosyası şifrelemesi güncellenirken hata" - msgid "E301: Oops, lost the swap file!!!" msgstr "E301: Hay aksi, takas dosyasını kaybettik!" @@ -3213,7 +3647,7 @@ msgid "" "Maybe no changes were made or Vim did not update the swap file." msgstr "" "\n" -"Herhangi bir değişiklik yapılmadı veya Vim takas dosyasını güncellemedi" +"Herhangi bir değişiklik yapılmadı veya Vim takas dosyasını güncellemedi." msgid " cannot be used with this version of Vim.\n" msgstr " Vim'in bu sürümüyle kullanılamaz.\n" @@ -3236,12 +3670,7 @@ msgid "" "or the file has been damaged." msgstr "" ",\n" -"veya dosya zarar görmüş" - -#, c-format -msgid "" -"E833: %s is encrypted and this version of Vim does not support encryption" -msgstr "E833: %s şifrelenmiş ve Vim'in bu sürümü şifrelemeyi desteklemiyor" +"veya dosya zarar görmüş." msgid " has been damaged (page size is smaller than minimum value).\n" msgstr " hasar görmüş (sayfa boyutu olabilecek en az değerden daha küçük).\n" @@ -3258,39 +3687,6 @@ msgid "E308: Warning: Original file may have been changed" msgstr "E308: Uyarı: Orijinal dosya değiştirilmiş olabilir" #, c-format -msgid "Swap file is encrypted: \"%s\"" -msgstr "Takas dosyası şifrelenmiş: \"%s\"" - -msgid "" -"\n" -"If you entered a new crypt key but did not write the text file," -msgstr "" -"\n" -"Yeni bir şifreleme anahtarı girmiş, fakat metin dosyasını yazmamışsanız," - -msgid "" -"\n" -"enter the new crypt key." -msgstr "" -"\n" -"yeni şifreleme anahtarını girin" - -msgid "" -"\n" -"If you wrote the text file after changing the crypt key press enter" -msgstr "" -"\n" -"Metin dosyasını şifreleme anahtarını değiştirdikten sonra yazdıysanız " -"Enter'a basın" - -msgid "" -"\n" -"to use the same key for text file and swap file" -msgstr "" -"\n" -"metin dosyası ve takas dosyası için aynı anahtarı kullanmak için" - -#, c-format msgid "E309: Unable to read block 1 from %s" msgstr "E309: Blok 1 %s içinden okunamıyor" @@ -3352,27 +3748,17 @@ msgstr "Kurtarma tamamlandı. Arabellek içeriği dosya içeriğine eşit." msgid "" "\n" -"You may want to delete the .swp file now." -msgstr "" -"\n" -"Bu .swp dosyasını silmeniz iyi olur." - -msgid "" +"You may want to delete the .swp file now.\n" "\n" -"Note: process STILL RUNNING: " msgstr "" "\n" -"Not: İşlem HÂLÂ ÇALIŞIYOR: " - -msgid "Using crypt key from swap file for the text file.\n" -msgstr "" -"Metin dosyası için takas dosyasındaki şifreleme anahtarı kullanılıyor.\n" +"Bu .swp dosyasını silmeniz iyi olur.\n" msgid "Swap files found:" msgstr "Takas dosyası bulundu:" msgid " In current directory:\n" -msgstr " Şu anki dizinde:\n" +msgstr " Geçerli dizinde:\n" msgid " Using specified name:\n" msgstr " Belirtilen şu adla:\n" @@ -3398,6 +3784,9 @@ msgstr " [Vim 3.0 sürümünden itibaren]" msgid " [does not look like a Vim swap file]" msgstr " [bir Vim takas dosyasına benzemiyor]" +msgid " [garbled strings (not nul terminated)]" +msgstr " [karışmış diziler (nul ile sonlandırılmamış)]" + msgid " file name: " msgstr " dosya adı: " @@ -3436,20 +3825,13 @@ msgid "" " process ID: " msgstr "" "\n" -" işlem kimliği: " +" süreç kimliği: " msgid " (STILL RUNNING)" msgstr " (HÅLÅ ÇALIŞIYOR)" msgid "" "\n" -" [not usable with this version of Vim]" -msgstr "" -"\n" -" [Vim'in bu sürümüyle kullanılamaz]" - -msgid "" -"\n" " [not usable on this computer]" msgstr "" "\n" @@ -3471,12 +3853,12 @@ msgid "E314: Preserve failed" msgstr "E314: Koruma başarısız oldu" #, c-format -msgid "E315: ml_get: invalid lnum: %ld" -msgstr "E315: ml_get: geçersiz satır numarası: %ld" +msgid "E315: ml_get: invalid lnum: %<PRId64>" +msgstr "E315: ml_get: Geçersiz satır numarası: %<PRId64>" #, c-format -msgid "E316: ml_get: cannot find line %ld in buffer %d %s" -msgstr "E316: ml_get: %ld. satır %d %s arabelleğinde bulunamıyor" +msgid "E316: ml_get: cannot find line %<PRId64> in buffer %d %s" +msgstr "E316: ml_get: %<PRId64>. satır %d %s arabelleğinde bulunamıyor" msgid "E317: pointer block id wrong 3" msgstr "E317: Blok 3 gösterge kimliği yanlış" @@ -3494,8 +3876,8 @@ msgid "deleted block 1?" msgstr "Blok 1 mi silindi?" #, c-format -msgid "E320: Cannot find line %ld" -msgstr "E320: %ld. satır bulunamıyor" +msgid "E320: Cannot find line %<PRId64>" +msgstr "E320: %<PRId64>. satır bulunamıyor" msgid "E317: pointer block id wrong" msgstr "E317: Gösterge blok kimliği yanlış" @@ -3504,12 +3886,12 @@ msgid "pe_line_count is zero" msgstr "pe_line_count sıfır" #, c-format -msgid "E322: line number out of range: %ld past the end" -msgstr "E322: satır numarası erimin dışında: %ld en sonuncuyu geçmiş" +msgid "E322: line number out of range: %<PRId64> past the end" +msgstr "E322: Satır numarası erimin dışında: %<PRId64> en sonuncuyu geçmiş" #, c-format -msgid "E323: line count wrong in block %ld" -msgstr "E323: %ld. blokta satır sayısı yanlış" +msgid "E323: line count wrong in block %<PRId64>" +msgstr "E323: %<PRId64>. blokta satır sayısı yanlış" msgid "Stack size increases" msgstr "Yığın boyutu artıyor" @@ -3547,7 +3929,7 @@ msgid "" " file when making changes. Quit, or continue with caution.\n" msgstr "" "\n" -"(1) Bu dosya başka bir programda da açık olabilir. Eğer öyleyse, aynı\n" +"(1) Bu dosya başka bir programda da açık olabilir. Eğer öyleyse aynı\n" " dosyanın iki ayrı örneğiyle karşılaşmamak için değişiklik yaparken\n" " lütfen dikkatli olun. Ya programdan çıkın ya da dikkatli ilerleyin.\n" @@ -3555,7 +3937,7 @@ msgid "(2) An edit session for this file crashed.\n" msgstr "(2) Bu dosya düzenleme oturumu çöktü.\n" msgid " If this is the case, use \":recover\" or \"vim -r " -msgstr " Durum buysa, \":recover\" veya \"vim -r " +msgstr " Durum buysa \":recover\" veya \"vim -r " msgid "" "\"\n" @@ -3565,7 +3947,7 @@ msgstr "" " yapıp değişiklikleri kurtarın (ek bilgi için \":help recovery\").\n" msgid " If you did this already, delete the swap file \"" -msgstr " Eğer bunu yaptıysanız, bu iletiyi bir daha görmemek için \"" +msgstr " Eğer bunu yaptıysanız bu iletiyi bir daha görmemek için \"" msgid "" "\"\n" @@ -3586,9 +3968,6 @@ msgstr "\" zaten var!" msgid "VIM - ATTENTION" msgstr "VİM - DİKKAT" -msgid "Swap file already exists!" -msgstr "Takas dosyası hâlihazırda var!" - msgid "" "&Open Read-Only\n" "&Edit anyway\n" @@ -3620,9 +3999,26 @@ msgstr "" msgid "E326: Too many swap files found" msgstr "E326: Çok fazla takas dosyası bulundu" +#, c-format +msgid "" +"E303: Unable to create directory \"%s\" for swap file, recovery impossible: " +"%s" +msgstr "E303: Takas dosyası için \"%s\" dizini oluşturulamadı, kurtarma " +"olanaksız: %s" + +msgid "Vim: Data too large to fit into virtual memory space\n" +msgstr "Vim: Veri, sanal bellek alanına sığmak için çok büyük\n" + +#, c-format +msgid "E342: Out of memory! (allocating %<PRIu64> bytes)" +msgstr "E342: Bellek tükendi!! (%<PRIu64> bayt ayrılıyor)" + msgid "E327: Part of menu-item path is not sub-menu" msgstr "E327: Menü öge yolunun bir kısmı alt menü değil" +msgid "E328: Menu only exists in another mode" +msgstr "E328: Menü yalnızca başka bir kipte mevcut" + #, c-format msgid "E329: No menu \"%s\"" msgstr "E329: Menü \"%s\" yok" @@ -3646,9 +4042,6 @@ msgstr "" "\n" "--- Menüler ---" -msgid "Tear off this menu" -msgstr "Bu menüyü dışarıya al" - #, c-format msgid "E335: Menu not defined for %s mode" msgstr "E335: Menü %s kipi için tanımlanmamış" @@ -3658,17 +4051,7 @@ msgstr "E333: Menü yolu bir menü ögesine çıkmalı" #, c-format msgid "E334: Menu not found: %s" -msgstr "E334: Menü bulunamadı %s" - -msgid "E336: Menu path must lead to a sub-menu" -msgstr "E336: Menü yolu bir alt menüye çıkmalı" - -msgid "E337: Menu not found - check menu names" -msgstr "E337: Menü bulunamadı - menü adlarını denetle" - -#, c-format -msgid "Error detected while compiling %s:" -msgstr "%s derlenirken hata tespit edildi:" +msgstr "E334: Menü bulunamadı: %s" #, c-format msgid "Error detected while processing %s:" @@ -3682,18 +4065,18 @@ msgstr "satır %4ld:" msgid "E354: Invalid register name: '%s'" msgstr "E354: Geçersiz yazmaç adı: '%s'" -msgid "Messages maintainer: Bram Moolenaar <Bram@vim.org>" -msgstr "Türkçeye çeviren: Emir SARI <bitigchi@me.com>" - msgid "Interrupt: " msgstr "Yarıda kes: " msgid "Press ENTER or type command to continue" msgstr "Sürdürmek için ENTER'a basın veya komut girin" +msgid " (Interrupted)" +msgstr " (Yarıda kesildi)" + #, c-format -msgid "%s line %ld" -msgstr "%s %ld. satır" +msgid "%s line %<PRId64>" +msgstr "%s %<PRId64>. satır" msgid "-- More --" msgstr "-- Daha fazla --" @@ -3714,6 +4097,15 @@ msgstr "" msgid "" "&Yes\n" "&No\n" +"&Cancel" +msgstr "" +"&Evet\n" +"&Hayır\n" +"İ&ptal" + +msgid "" +"&Yes\n" +"&No\n" "Save &All\n" "&Discard All\n" "&Cancel" @@ -3724,113 +4116,9 @@ msgstr "" "&Tümünü At\n" "İ&ptal" -msgid "E766: Insufficient arguments for printf()" -msgstr "E766: printf() için yetersiz argüman" - -msgid "E807: Expected Float argument for printf()" -msgstr "E807: printf() için kayan noktalı değer türünde argüman bekleniyordu" - -msgid "E767: Too many arguments to printf()" -msgstr "E767: printf() için çok fazla argüman" - -msgid "Type number and <Enter> or click with the mouse (q or empty cancels): " -msgstr "" -"Sayı girip <Enter>'a veya fare düğmesine basın (q veya boş iptal eder): " - -msgid "Type number and <Enter> (q or empty cancels): " -msgstr "Sayı girip <Enter>'a basın (q veya boş iptal eder): " - -#, c-format -msgid "%ld more line" -msgid_plural "%ld more lines" -msgstr[0] "%ld daha fazla satır" -msgstr[1] "%ld daha fazla satır" - -#, c-format -msgid "%ld line less" -msgid_plural "%ld fewer lines" -msgstr[0] "%ld daha az satır" -msgstr[1] "%ld daha az satır" - -msgid " (Interrupted)" -msgstr " (Yarıda kesildi)" - -msgid "Beep!" -msgstr "Bip!" - -msgid "E677: Error writing temp file" -msgstr "E677: Geçici dosya yazılırken hata" - -msgid "ERROR: " -msgstr "HATA: " - -#, c-format -msgid "" -"\n" -"[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n" -msgstr "" -"\n" -"[bitler] toplam ayrılan/boşaltılan %lu-%lu, kullanımda %lu, doruk nokt. %lu\n" - -#, c-format -msgid "" -"[calls] total re/malloc()'s %lu, total free()'s %lu\n" -"\n" -msgstr "" -"[çağrılar] toplam re/malloc()'lar %lu, toplam free()'ler %lu\n" -"\n" - -msgid "E341: Internal error: lalloc(0, )" -msgstr "E341: İç hata: lalloc(0, )" - -#, c-format -msgid "E342: Out of memory! (allocating %lu bytes)" -msgstr "E342: Yetersiz bellek! (%lu bit ayrılıyor)" - -#, c-format -msgid "Calling shell to execute: \"%s\"" -msgstr "Çalıştırmak için çağrılan kabuk: \"%s\"" - -msgid "E545: Missing colon" -msgstr "E545: İki nokta eksik" - -msgid "E546: Illegal mode" -msgstr "E546: İzin verilmeyen kip" - -msgid "E547: Illegal mouseshape" -msgstr "E547: İzin verilmeyen fare imleci türü" - -msgid "E548: digit expected" -msgstr "E548: Basamak bekleniyordu" - -msgid "E549: Illegal percentage" -msgstr "E549: İzin verilmeyen yüzde" - -#, c-format -msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\"" -msgstr "" -"E668: NetBeans bağlantı bilgisi dosyası için yanlış erişim kipi: \"%s\"" - -#, c-format -msgid "E658: NetBeans connection lost for buffer %d" -msgstr "E658: Arabellek %d için NetBeans bağlantısı koptu" - -msgid "E838: netbeans is not supported with this GUI" -msgstr "E838: NetBeans bu grafik arabirimde desteklenmiyor" - -msgid "E511: netbeans already connected" -msgstr "E511: NetBeans hâlihazırda bağlı" - -#, c-format -msgid "E505: %s is read-only (add ! to override)" -msgstr "E505: %s saltokunur (geçersiz kılmak için ! ekleyin)" - msgid "E349: No identifier under cursor" msgstr "E349: İmleç altında bir tanımlayıcı yok" -msgid "Warning: terminal cannot highlight" -msgstr "Uyarı: Uçbirim vurgulama yapamıyor" - msgid "E348: No string under cursor" msgstr "E348: İmleç altında bir dizi yok" @@ -3846,83 +4134,85 @@ msgstr "E662: Değişiklik listesinin başında" msgid "E663: At end of changelist" msgstr "E663: Değişiklik listesinin sonunda" -msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Nvim" msgstr "" -"Değişikliklerden vazgeçip Vim'den çıkmak için :qa! yazıp <Enter>'a basın" +"Değişikliklerden vazgeçip Nvim'den çıkmak için :qa! yazıp <Enter>'a basın" -msgid "Type :qa and press <Enter> to exit Vim" -msgstr "Vim'den çıkmak için :qa yazıp <Enter>'a basın" +msgid "Type :qa and press <Enter> to exit Nvim" +msgstr "Nvim'den çıkmak için :qa yazıp <Enter>'a basın" #, c-format -msgid "%ld line %sed %d time" -msgid_plural "%ld line %sed %d times" -msgstr[0] "%ld satır, %s %d kez" -msgstr[1] "%ld satır, %s %d kez" +msgid "%<PRId64> lines to indent... " +msgstr "girintilenecek %<PRId64> satır kaldı... " -#, c-format -msgid "%ld lines %sed %d time" -msgid_plural "%ld lines %sed %d times" -msgstr[0] "%ld satır, %s %d kez" -msgstr[1] "%ld satır, %s %d kez" - -msgid "cannot yank; delete anyway" -msgstr "kopyalanamıyor, silindi" +msgid "E748: No previously used register" +msgstr "E748: Daha önce kullanılan bir yazmaç yok" #, c-format -msgid "%ld line changed" -msgid_plural "%ld lines changed" -msgstr[0] "%ld satır değiştirildi" -msgstr[1] "%ld satır değiştirildi" +msgid " into \"%c" +msgstr " \"%c" #, c-format -msgid "%d line changed" -msgid_plural "%d lines changed" -msgstr[0] "%d satır değiştirildi" -msgstr[1] "%d satır değiştirildi" +msgid "E353: Nothing in register %s" +msgstr "E353: Yazmaç %s boş" + +msgid "" +"\n" +"Type Name Content" +msgstr "" +"\n" +"Tür Ad İçerik" + +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "" +"E883: Arama dizgisi ve ifade yazmacı iki veya daha fazla satır içeremez" #, c-format -msgid "%ld Cols; " -msgstr "%ld Sütun; " +msgid "%<PRId64> Cols; " +msgstr "%<PRId64> Sütun; " #, c-format -msgid "Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Bytes" -msgstr "%s%ld/%ld satır; %lld/%lld sözcük; %lld/%lld bit seçildi" +msgid "" +"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " +"%<PRId64> of %<PRId64> Bytes" +msgstr "%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> bayt seçildi" #, c-format msgid "" -"Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Chars; %lld of " -"%lld Bytes" +"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " +"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes" msgstr "" -"%s%ld/%ld satır; %lld/%lld sözcük; %lld/%lld karakter; %lld/%lld bit seçildi" +"%s%<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> karakter; %<PRId64>/%<PRId64> bayt seçildi" #, c-format -msgid "Col %s of %s; Line %ld of %ld; Word %lld of %lld; Byte %lld of %lld" -msgstr "Sütun %s/%s; Satır %ld/%ld; Sözcük %lld/%lld; Bit %lld/%lld" +msgid "" +"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte " +"%<PRId64> of %<PRId64>" +msgstr "%s/%s sütun; %<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> bayt" #, c-format msgid "" -"Col %s of %s; Line %ld of %ld; Word %lld of %lld; Char %lld of %lld; Byte " -"%lld of %lld" +"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char " +"%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>" msgstr "" -"Sütun %s/%s; Satır %ld/%ld; Sözcük %lld/%lld; Karakter %lld/%lld; Bit %lld/" -"%lld" +"%s/%s sütun; %<PRId64>/%<PRId64> satır; %<PRId64>/%<PRId64> sözcük; " +"%<PRId64>/%<PRId64> karakter; %<PRId64>/%<PRId64> bayt" #, c-format -msgid "(+%lld for BOM)" -msgstr "(BOM için +%lld)" +msgid "(+%<PRId64> for BOM)" +msgstr "(BOM için +%<PRId64>)" msgid "E774: 'operatorfunc' is empty" msgstr "E774: 'operatorfunc' boş" -msgid "E775: Eval feature not available" -msgstr "E775: Eval özelliği mevcut değil" - msgid "E518: Unknown option" msgstr "E518: Bilinmeyen seçenek" -msgid "E519: Option not supported" -msgstr "E519: Özellik desteklenmiyor" - msgid "E520: Not allowed in a modeline" msgstr "E520: Bir kip satırında izin verilmiyor" @@ -3935,68 +4225,6 @@ msgstr "E846: Anahtar kodu ayarlanmamış" msgid "E521: Number required after =" msgstr "E521: = sonrası sayı gerekiyor" -msgid "E522: Not found in termcap" -msgstr "E522: termcap içinde bulunamadı" - -msgid "E946: Cannot make a terminal with running job modifiable" -msgstr "E946: Uçbirim bir iş çalışırken değiştirilebilir yapılamaz" - -msgid "E590: A preview window already exists" -msgstr "E590: Bir önizleme penceresi hâlihazırda mevcut" - -msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'" -msgstr "W17: Arapça UTF-8 gerektirir, ':set encoding=utf-8' yapın" - -msgid "E954: 24-bit colors are not supported on this environment" -msgstr "E954: 24 bit renkler bu ortamda desteklenmiyor" - -#, c-format -msgid "E593: Need at least %d lines" -msgstr "E593: En azından %d satır gerekli" - -#, c-format -msgid "E594: Need at least %d columns" -msgstr "E594: En azından %d sütun gerekli" - -#, c-format -msgid "E355: Unknown option: %s" -msgstr "E355: Bilinmeyen seçenek: %s" - -#, c-format -msgid "E521: Number required: &%s = '%s'" -msgstr "E521: Sayı gerekiyor: &%s = '%s'" - -msgid "" -"\n" -"--- Terminal codes ---" -msgstr "" -"\n" -"--- Uçbirim kodları ---" - -msgid "" -"\n" -"--- Global option values ---" -msgstr "" -"\n" -"--- Global seçenek değerleri ---" - -msgid "" -"\n" -"--- Local option values ---" -msgstr "" -"\n" -"--- Yerel seçenek değerleri ---" - -msgid "" -"\n" -"--- Options ---" -msgstr "" -"\n" -"--- Seçenekler ---" - -msgid "E356: get_varp ERROR" -msgstr "E356: get_varp HATASI" - #, c-format msgid "E539: Illegal character <%s>" msgstr "E539: İzin verilmeyen karakter <%s>" @@ -4005,36 +4233,14 @@ msgstr "E539: İzin verilmeyen karakter <%s>" msgid "For option %s" msgstr "%s seçeneği için" -msgid "E540: Unclosed expression sequence" -msgstr "E540: Kapatılmamış ifade sıralaması" - -msgid "E542: unbalanced groups" -msgstr "E542: Dengelenmemiş gruplar" - -msgid "E529: Cannot set 'term' to empty string" -msgstr "E529: Boş dizi için 'term' ayarlanamıyor" - -msgid "E530: Cannot change term in GUI" -msgstr "E530: Grafik arabirimde uçbirim değiştirilemez" - -msgid "E531: Use \":gui\" to start the GUI" -msgstr "E531: Grafik arabirimi başlatmak için \":gui\" yazın" - msgid "E589: 'backupext' and 'patchmode' are equal" msgstr "E589: 'backupext' ve 'patchmode' birbirine eşit" -msgid "E835: Conflicts with value of 'fillchars'" -msgstr "E835: 'fillchars' değeriyle çakışmalar var" - msgid "E834: Conflicts with value of 'listchars'" msgstr "E834: 'listchars' değeriyle çakışmalar var" -msgid "E617: Cannot be changed in the GTK+ 2 GUI" -msgstr "E617: GTK+ 2 grafik arabiriminde değiştirilemez" - -#, c-format -msgid "E950: Cannot convert between %s and %s" -msgstr "E950: %s ve %s arasında dönüştürme yapılamıyor" +msgid "E835: Conflicts with value of 'fillchars'" +msgstr "E835: 'fillchars' değeriyle çakışmalar var" msgid "E524: Missing colon" msgstr "E524: İki nokta eksik" @@ -4044,7 +4250,7 @@ msgstr "E525: Sıfır uzunlukta dizi" #, c-format msgid "E526: Missing number after <%s>" -msgstr "E526: <%s> sonrasında sayı eksik" +msgstr "E526: <%s> sonrası sayı eksik" msgid "E527: Missing comma" msgstr "E527: Virgül eksik" @@ -4055,21 +4261,6 @@ msgstr "E528: Bir ' değeri belirtmeli" msgid "E595: 'showbreak' contains unprintable or wide character" msgstr "E595: 'showbreak' yazdırılamaz veya geniş karakter içeriyor" -msgid "E596: Invalid font(s)" -msgstr "E596: Geçersiz font(lar)" - -msgid "E597: can't select fontset" -msgstr "E597: Yazıtipi seti seçilemiyor" - -msgid "E598: Invalid fontset" -msgstr "E598: Geçersiz yazıtipi seti" - -msgid "E533: can't select wide font" -msgstr "E533: Geniş yazıtipi seçilemiyor" - -msgid "E534: Invalid wide font" -msgstr "E534: Geçersiz geniş yazıtipi" - #, c-format msgid "E535: Illegal character after <%c>" msgstr "E535: <%c> sonrası izin verilmeyen karakter" @@ -4081,253 +4272,101 @@ msgstr "E536: Virgül gerekiyor" msgid "E537: 'commentstring' must be empty or contain %s" msgstr "E537: 'commentstring' boş olmalı veya %s içermeli" -msgid "cannot open " -msgstr "Açılamıyor: " - -msgid "VIM: Can't open window!\n" -msgstr "VİM: Pencere açılamıyor!\n" - -msgid "Need Amigados version 2.04 or later\n" -msgstr "AmigaDOS 2.04 sürümü veya sonrası gerekli\n" - -#, c-format -msgid "Need %s version %ld\n" -msgstr "%s %ld sürümü gerekli\n" - -msgid "Cannot open NIL:\n" -msgstr "NIL açılamıyor:\n" - -msgid "Cannot create " -msgstr "Oluşturulamıyor: " - -#, c-format -msgid "Vim exiting with %d\n" -msgstr "Vim %d ile çıkıyor\n" - -msgid "cannot change console mode ?!\n" -msgstr "Konsol kipi değiştirilemiyor?!\n" - -msgid "mch_get_shellsize: not a console??\n" -msgstr "mch_get_shellsize: Bir konsol değil??\n" - -msgid "E360: Cannot execute shell with -f option" -msgstr "E360: Kabuk -f seçeneği ile çalıştırılamıyor" - -msgid "Cannot execute " -msgstr "Çalıştırılamıyor: " - -msgid "shell " -msgstr "kabuk " - -msgid " returned\n" -msgstr " döndürüldü\n" - -msgid "ANCHOR_BUF_SIZE too small." -msgstr "ANCHOR_BUF_SIZE çok küçük." - -msgid "I/O ERROR" -msgstr "GİRDİ/ÇIKTI HATASI" - -msgid "Message" -msgstr "İleti" - -msgid "E237: Printer selection failed" -msgstr "E237: Yazıcı seçimi başarısız oldu" - -#, c-format -msgid "to %s on %s" -msgstr "%s, %s üzerinde" +msgid "E540: Unclosed expression sequence" +msgstr "E540: Kapatılmamış ifade sıralaması" -#, c-format -msgid "E613: Unknown printer font: %s" -msgstr "E613: Bilinmeyen yazıcı fontu: %s" +msgid "E542: unbalanced groups" +msgstr "E542: Dengelenmemiş gruplar" -#, c-format -msgid "E238: Print error: %s" -msgstr "E238: Yazdırma hatası: %s" +msgid "E590: A preview window already exists" +msgstr "E590: Bir önizleme penceresi hâlihazırda mevcut" -#, c-format -msgid "Printing '%s'" -msgstr "'%s' yazdırılıyor" +msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'" +msgstr "W17: Arapça, UTF-8 gerektirir; ':set encoding=utf-8' yapın" #, c-format -msgid "E244: Illegal charset name \"%s\" in font name \"%s\"" -msgstr "E244: Geçersiz karakter adı \"%s\", bulunduğu yazıtipi: \"%s\"" +msgid "E593: Need at least %d lines" +msgstr "E593: En azından %d satır gerekli" #, c-format -msgid "E244: Illegal quality name \"%s\" in font name \"%s\"" -msgstr "E244: İzin verilmeyen nitelik adı: \"%s\", bulunduğu yazıtipi: \"%s\"" +msgid "E594: Need at least %d columns" +msgstr "E594: En azından %d sütun gerekli" #, c-format -msgid "E245: Illegal char '%c' in font name \"%s\"" -msgstr "" -"E245: İzin verilmeyen karakter '%c', bulunduğu yer: \"%s\" yazıtipi adı" +msgid "E355: Unknown option: %s" +msgstr "E355: Bilinmeyen seçenek: %s" #, c-format -msgid "Opening the X display took %ld msec" -msgstr "X ekranını açma %ld milisaniye sürdü" +msgid "E521: Number required: &%s = '%s'" +msgstr "E521: Sayı gerekiyor: &%s = '%s'" msgid "" "\n" -"Vim: Got X error\n" +"--- Global option values ---" msgstr "" "\n" -"Vim: X hatası alındı\n" - -#, c-format -msgid "restoring display %s" -msgstr "%s ekranı geri getiriliyor" - -msgid "Testing the X display failed" -msgstr "X ekran testi başarısız oldu" - -msgid "Opening the X display timed out" -msgstr "X ekran açılması zaman aşımına uğradı" +"--- Global seçenek değerleri ---" msgid "" "\n" -"Could not get security context for " +"--- Local option values ---" msgstr "" "\n" -"Şunun için güvenlik bağlamı alınamadı: " +"--- Yerel seçenek değerleri ---" msgid "" "\n" -"Could not set security context for " +"--- Options ---" msgstr "" "\n" -"Şunun için güvenlik bağlamı alınamadı: " +"--- Seçenekler ---" -#, c-format -msgid "Could not set security context %s for %s" -msgstr "Güvenlik bağlamı %s %s için alınamadı" +msgid "E356: get_varp ERROR" +msgstr "E356: get_varp HATASI" #, c-format -msgid "Could not get security context %s for %s. Removing it!" -msgstr "Güvenlik bağlamı %s %s için alınamadı. Kaldırılıyor!" +msgid "E357: 'langmap': Matching character missing for %s" +msgstr "E357: 'langmap': %s için eşleşen karakter eksik" -msgid "" -"\n" -"Cannot execute shell sh\n" -msgstr "" -"\n" -"sh kabuğu çalıştırılamıyor\n" +#, c-format +msgid "E358: 'langmap': Extra characters after semicolon: %s" +msgstr "E358: 'langmap': Noktalı virgülden sonra ek karakterler: %s" -msgid "" -"\n" -"shell returned " -msgstr "" -"\n" -"Program çıktı: " +#, c-format +msgid "dlerror = \"%s\"" +msgstr "dlerror = \"%s\"" -msgid "" -"\n" -"Cannot create pipes\n" -msgstr "" -"\n" -"Veri yolları oluşturulamıyor\n" +#, c-format +msgid "E5420: Failed to write to file: %s" +msgstr "E5420: Dosyaya yazılamadı: %s" -msgid "" -"\n" -"Cannot fork\n" -msgstr "" -"\n" -"Çatallanamıyor\n" +msgid "Vim: Error reading input, exiting...\n" +msgstr "Vim: Girdi okunurken hata, çıkılıyor...\n" msgid "" "\n" -"Cannot execute shell " +"shell returned " msgstr "" "\n" -"Kabuk çalıştırılamıyor " +"Program çıktı: " msgid "" "\n" -"Command terminated\n" +"shell failed to start: " msgstr "" "\n" -"Komut sonlandırıldı\n" - -msgid "XSMP lost ICE connection" -msgstr "XSMP, ICE bağlantısını kopardı" - -#, c-format -msgid "dlerror = \"%s\"" -msgstr "dlerror = \"%s\"" - -msgid "Opening the X display failed" -msgstr "X ekran açılışı başarısız oldu" - -msgid "XSMP handling save-yourself request" -msgstr "Kendini kurtarma isteği XSMP tarafından gerçekleştiriliyor" - -msgid "XSMP opening connection" -msgstr "XSMP bağlantıyı açıyor" - -msgid "XSMP ICE connection watch failed" -msgstr "XSMP ICE bağlantı izlemesi başarısız oldu" - -#, c-format -msgid "XSMP SmcOpenConnection failed: %s" -msgstr "XSMP SmcOpenConnection başarısız oldu: %s" - -msgid "At line" -msgstr "Satırda" - -#, c-format -msgid "Vim: Caught %s event\n" -msgstr "Vim: %s olayı yakalandı\n" - -msgid "close" -msgstr "kapat" - -msgid "logoff" -msgstr "oturumu kapat" - -msgid "shutdown" -msgstr "kapat" - -msgid "E371: Command not found" -msgstr "E371: Komut bulunamadı" - -msgid "" -"VIMRUN.EXE not found in your $PATH.\n" -"External commands will not pause after completion.\n" -"See :help win32-vimrun for more information." -msgstr "" -"VIMRUN.EXE $PATH üzerinde bulunamadı.\n" -"Dış komutlar tamamlandıktan sonra duraklamayacak.\n" -"Ek bilgi için :help win32-vimrun yazın." - -msgid "Vim Warning" -msgstr "Vim - Uyarı" +"kabuk başlatılamad: " #, c-format -msgid "shell returned %d" -msgstr "Program %d numaralı kod ile çıktı" - -msgid "E861: Cannot open a second popup with a terminal" -msgstr "E861: Bir uçbirimle ikinci bir açılır pencere açılamıyor" +msgid "E5677: Error writing input to shell-command: %s" +msgstr "E5677: Girdi, kabuk komutuna yazılırken hata: %s" -msgid "E450: buffer number, text or a list required" -msgstr "E450: Arabellek numarası, metin veya liste gerekiyor" - -#, c-format -msgid "E997: Tabpage not found: %d" -msgstr "E997: Sekme bulunamadı: %d" +msgid "%a %b %d %H:%M:%S %Y" +msgstr "%a %b %d %H:%M:%S %Y" #, c-format -msgid "E993: window %d is not a popup window" -msgstr "E993: %d penceresi bir açılır pencere değil" - -msgid "E994: Not allowed in a popup window" -msgstr "E994: Açılır pencere içinde izin verilmiyor" - -msgid "E863: Not allowed for a terminal in a popup window" -msgstr "E863: Açılır pencere içinde uçbirime izin verilmiyor" - -msgid "E750: First use \":profile start {fname}\"" -msgstr "E750: İlk kullanım \":profile start {dosyaadı}\"" +msgid "E447: Can't find file \"%s\" in path" +msgstr "E447: \"%s\" dosyası yol içinde bulunamadı" msgid "E553: No more items" msgstr "E553: Öge yok" @@ -4340,7 +4379,7 @@ msgstr "E926: Geçerli konum listesi değiştirildi" #, c-format msgid "E372: Too many %%%c in format string" -msgstr "E372: Biçim dizisinde çok fazla %%%c" +msgstr "E372: Biçim dizisinde pek fazla %%%c" #, c-format msgid "E373: Unexpected %%%c in format string" @@ -4390,9 +4429,6 @@ msgstr "E381: Hızlı düzelt yığınının en tepesinde" msgid "No entries" msgstr "Girdi yok" -msgid "Error file" -msgstr "Hata dosyası" - msgid "E683: File name missing or invalid pattern" msgstr "E683: Dosya adı eksik veya geçersiz dizgi" @@ -4410,16 +4446,12 @@ msgid "E777: String or List expected" msgstr "E777: Dizi veya liste bekleniyordu" #, c-format -msgid "E927: Invalid action: '%s'" -msgstr "E927: Geçersiz eylem: '%s'" - -#, c-format msgid "E369: invalid item in %s%%[]" msgstr "E369: %s%%[] içinde geçersiz öge" #, c-format msgid "E769: Missing ] after %s[" -msgstr "E769: %s[ sonrasında ] eksik" +msgstr "E769: %s[ sonrası ] eksik" msgid "E944: Reverse range in character class" msgstr "E944: Karakter sınıfında geriye dönük erim" @@ -4457,8 +4489,8 @@ msgid "E956: Cannot use pattern recursively" msgstr "E956: Dizgi özyineli olarak kullanılamaz" #, c-format -msgid "E654: missing delimiter after search pattern: %s" -msgstr "E654: Arama dizgisi sonrasında eksik sınırlandırıcı: %s" +msgid "E1204: No Number allowed after .: '\\%%%c'" +msgstr "E1204: . sonrası Sayıya izin verilmiyor: '\\%%%c'" #, c-format msgid "E554: Syntax error in %s{...}" @@ -4466,175 +4498,40 @@ msgstr "E554: %s{...} içinde sözdizimi hatası" #, c-format msgid "E888: (NFA regexp) cannot repeat %s" -msgstr "E888: (NFA regexp) %s tekrar edemiyor" +msgstr "E888: (BSO düzenli ifadesi) %s tekrar edemiyor" msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " "used " msgstr "" -"E864: \\%#= sonrasında yalnızca 0, 1, veya 2 gelebilir. Otomatik motor " +"E864: \\%#= sonrası yalnızca 0, 1, veya 2 gelebilir. Otomatik işletke " "kullanılacak " msgid "Switching to backtracking RE engine for pattern: " msgstr "Şu dizgi için düzenli ifade iz sürme motoruna geçiliyor: " -msgid "E65: Illegal back reference" -msgstr "E65: Geçersiz dönüş başvurusu" - -msgid "E63: invalid use of \\_" -msgstr "E63: Geçersiz kullanım: \\_" - -#, c-format -msgid "E64: %s%c follows nothing" -msgstr "E64: %s%c tek başına kullanılıyor" - -msgid "E68: Invalid character after \\z" -msgstr "E68: \\z sonrası geçersiz karakter" - #, c-format -msgid "E678: Invalid character after %s%%[dxouU]" -msgstr "E678: %s%%[dxouU] sonrası geçersiz karakter" - -#, c-format -msgid "E71: Invalid character after %s%%" -msgstr "E71: %s%% sonrası geçersiz karakter" - -#, c-format -msgid "E59: invalid character after %s@" -msgstr "E59: %s@ sonrası geçersiz karakter" - -#, c-format -msgid "E60: Too many complex %s{...}s" -msgstr "E60: Çok fazla karmaşık %s{...}(lar)" - -#, c-format -msgid "E61: Nested %s*" -msgstr "E61: İç içe geçmiş %s*" - -#, c-format -msgid "E62: Nested %s%c" -msgstr "E62: İç içe geçmiş %s%c" - -msgid "E50: Too many \\z(" -msgstr "E50: Çok fazla \\z(" - -#, c-format -msgid "E51: Too many %s(" -msgstr "E51: Çok fazla %s(" - -msgid "E52: Unmatched \\z(" -msgstr "E52: Eşleşmemiş \\z(" - -msgid "E339: Pattern too long" -msgstr "E339: Dizgi çok uzun" - -msgid "External submatches:\n" -msgstr "Dış alteşleşmeler:\n" - -msgid "E865: (NFA) Regexp end encountered prematurely" -msgstr "E865: (BSO) Düzenli ifade sonu çok erken geldi" - -#, c-format -msgid "E866: (NFA regexp) Misplaced %c" -msgstr "E866: (BSO düzenli ifadesi) Yanlış yere koyulmuş %c" - -#, c-format -msgid "E877: (NFA regexp) Invalid character class: %d" -msgstr "E877: (BSO düzenli ifadesi) Geçersiz karakter sınıfı: %d" - -msgid "E951: \\% value too large" -msgstr "E951: \\% çok büyük bir değer" - -#, c-format -msgid "E867: (NFA) Unknown operator '\\z%c'" -msgstr "E867: (BSO) Bilinmeyen işleç '\\z%c'" - -#, c-format -msgid "E867: (NFA) Unknown operator '\\%%%c'" -msgstr "E867: (BSO) Bilinmeyen işleç '\\%%%c'" - -msgid "E868: Error building NFA with equivalence class!" -msgstr "E868: Eşdeğerli sınıf ile BSO inşa ederken hata!" - -#, c-format -msgid "E869: (NFA) Unknown operator '\\@%c'" -msgstr "E869: (BSO) Bilinmeyen işleç '\\@%c'" - -msgid "E870: (NFA regexp) Error reading repetition limits" -msgstr "E870: (BSO düzenli ifadesi) Yineleme sınırlarımı okurken hata" - -msgid "E871: (NFA regexp) Can't have a multi follow a multi" -msgstr "E871: (BSO düzenli ifadesi) Bir çoklunun ardından çoklu gelemez" - -msgid "E872: (NFA regexp) Too many '('" -msgstr "E872: (BSO düzenli ifadesi) Çok fazla '('" - -msgid "E879: (NFA regexp) Too many \\z(" -msgstr "E879: (BSO düzenli ifadesi) Çok fazla \\z(" - -msgid "E873: (NFA regexp) proper termination error" -msgstr "E873: (BSO düzenli ifadesi) Düzgün sonlandırma hatası" - -msgid "Could not open temporary log file for writing, displaying on stderr... " -msgstr "" -"Geçici günlük dosyası yazma için açılamıyor, stderr'de görüntüleniyor..." - -msgid "E874: (NFA) Could not pop the stack!" -msgstr "E874: (BSO) Yığın çıkartılamadı!" - -msgid "" -"E875: (NFA regexp) (While converting from postfix to NFA), too many states " -"left on stack" -msgstr "" -"E875: (BSO düzenli ifadesi) (Art takı'dan BSO'ya çevirirken), yığında çok " -"fazla durum bırakıldı" - -msgid "E876: (NFA regexp) Not enough space to store the whole NFA " -msgstr "" -"E876: (BSO düzenli ifadesi) Tüm BSO'yu depolamak için yeterli alan yok " - -msgid "E878: (NFA) Could not allocate memory for branch traversal!" -msgstr "E878: (BSO) Dal gezinmesi için bellek ayrılamadı!" - -msgid "E748: No previously used register" -msgstr "E748: Daha önce kullanılan bir yazmaç yok" +msgid "Searching for \"%s\" in \"%s\"" +msgstr "\"%s\", \"%s\" içinde aranıyor" #, c-format -msgid "freeing %ld lines" -msgstr "%ld satırlık yer açılıyor" +msgid "Searching for \"%s\"" +msgstr "\"%s\" aranıyor" #, c-format -msgid " into \"%c" -msgstr " \"%c" +msgid "not found in '%s': \"%s\"" +msgstr "'%s' içinde bulunamadı: \"%s\"" #, c-format -msgid "block of %ld line yanked%s" -msgid_plural "block of %ld lines yanked%s" -msgstr[0] "%ld satırlık blok şuraya kopyalandı:%s" -msgstr[1] "%ld satırlık blok şuraya kopyalandı:%s" +msgid "Searching for \"%s\" in runtime path" +msgstr "\"%s\", çalışma zamanı yolu içinde aranıyor" #, c-format -msgid "%ld line yanked%s" -msgid_plural "%ld lines yanked%s" -msgstr[0] "%ld satır şuraya kopyalandı:%s" -msgstr[1] "%ld satır şuraya kopyalandı:%s" +msgid "not found in runtime path: \"%s\"" +msgstr "çalışma zamanı yolu içinde bulunamadı: \"%s\"" -#, c-format -msgid "E353: Nothing in register %s" -msgstr "E353: Yazmaç %s boş" - -msgid "" -"\n" -"Type Name Content" -msgstr "" -"\n" -"Tür Ad İçerik" - -msgid "" -"E883: search pattern and expression register may not contain two or more " -"lines" -msgstr "" -"E883: Arama dizgisi ve ifade yazmacı iki veya daha fazla satır içeremez" +msgid " TERMINAL" +msgstr " UÇBİRİM" msgid " VREPLACE" msgstr " SANAL DEĞİŞTİR" @@ -4688,146 +4585,264 @@ msgid "recording" msgstr "kaydediliyor" #, c-format -msgid "Searching for \"%s\" in \"%s\"" -msgstr "\"%s\", \"%s\" içinde aranıyor" +msgid "E383: Invalid search string: %s" +msgstr "E383: Geçersiz arama dizisi: %s" #, c-format -msgid "Searching for \"%s\"" -msgstr "\"%s\" aranıyor" +msgid "E384: search hit TOP without match for: %s" +msgstr "E384: Arama dosyanın BAŞINA vardı, %s bulunamadı" #, c-format -msgid "not found in '%s': \"%s\"" -msgstr "'%s' içinde bulunamadı: \"%s\"" +msgid "E385: search hit BOTTOM without match for: %s" +msgstr "E385: Arama dosyanın SONUNA vardı, %s bulunamadı" + +msgid "E386: Expected '?' or '/' after ';'" +msgstr "E386: ';' sonrasında '?' veya '/' bekleniyordu" + +msgid " (includes previously listed match)" +msgstr " (daha önce listelenen eşleşmeyi içerir)" -msgid "Source Vim script" -msgstr "Vim betiği kaynak al" +msgid "--- Included files " +msgstr "--- İçerilen dosyalar " + +msgid "not found " +msgstr "bulunamadı " + +msgid "in path ---\n" +msgstr "yolda ---\n" + +msgid " (Already listed)" +msgstr " (Hâlihazırda listelenmiş)" + +msgid " NOT FOUND" +msgstr " BULUNAMADI" #, c-format -msgid "Cannot source a directory: \"%s\"" -msgstr "Dizin kaynak alınamıyor: \"%s\"" +msgid "Scanning included file: %s" +msgstr "İçerilen dosya taranıyor: %s" #, c-format -msgid "could not source \"%s\"" -msgstr "\"%s\" kaynak alınamadı" +msgid "Searching included file %s" +msgstr "İçerilen dosya %s aranıyor" + +msgid "E387: Match is on current line" +msgstr "E387: Eşleşme şu anda bulunulan satırda" + +msgid "All included files were found" +msgstr "Tüm içerilen dosyalar bulundu" + +msgid "No included files" +msgstr "İçerilen dosya yok" + +msgid "E388: Couldn't find definition" +msgstr "E388: Tanım bulunamadı" + +msgid "E389: Couldn't find pattern" +msgstr "E389: Dizgi bulunamadı" + +msgid "too few bytes read" +msgstr "pek az bayt okundu" #, c-format -msgid "line %ld: could not source \"%s\"" -msgstr "%ld. satır: \"%s\" kaynak alınamadı" +msgid "System error while skipping in ShaDa file: %s" +msgstr "Paylaşılan veri dosyası içinde atlanırken sistem hatası: %s" #, c-format -msgid "sourcing \"%s\"" -msgstr "\"%s\" kaynak alınıyor" +msgid "" +"Error while reading ShaDa file: last entry specified that it occupies " +"%<PRIu64> bytes, but file ended earlier" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: Son girdi, %<PRIu64> bayt tuttuğunu " +"belirtti; ancak dosya daha erken sonlandı" #, c-format -msgid "line %ld: sourcing \"%s\"" -msgstr "%ld. satır: \"%s\" kaynak alınıyor" +msgid "System error while closing ShaDa file: %s" +msgstr "Paylaşılan veri dosyası kapatılırken sistem hatası: %s" #, c-format -msgid "finished sourcing %s" -msgstr "%s kaynak alınması bitti" +msgid "System error while writing ShaDa file: %s" +msgstr "Paylaşılan veri dosyası yazılırken sistem hatası: %s" #, c-format -msgid "continuing in %s" -msgstr "%s içinde sürdürülüyor" +msgid "Reading ShaDa file \"%s\"%s%s%s%s" +msgstr "Paylaşılan veri dosyası okunuyor: \"%s\"%s%s%s%s" -msgid "modeline" -msgstr "kip satırı" +msgid " info" +msgstr " bilgiler-" -msgid "--cmd argument" -msgstr "--cmd argümanı" +msgid " marks" +msgstr " imler-" -msgid "-c argument" -msgstr "-c argümanı" +msgid " oldfiles" +msgstr " düzenleme geçmişi" -msgid "environment variable" -msgstr "ortam değişkeni" +msgid " FAILED" +msgstr " BAŞARISIZ" -msgid "error handler" -msgstr "hata işleyicisi" +#, c-format +msgid "System error while opening ShaDa file %s for reading: %s" +msgstr "%s paylaşılan veri dosyası açılırken sistem hatası: %s" -msgid "changed window size" -msgstr "değiştirilen pencere boyutu" +msgid "additional elements of ShaDa " +msgstr "paylaşılan verinin ek ögeleri " -msgid "W15: Warning: Wrong line separator, ^M may be missing" -msgstr "W15: Uyarı: Yanlış satır ayırıcısı, ^M eksik olabilir" +msgid "additional data of ShaDa " +msgstr "paylaşılan verinin ek verisi" -msgid "E167: :scriptencoding used outside of a sourced file" -msgstr "E167: :scriptencoding kaynak alınmış bir dosyanın dışında kullanıldı" +#, c-format +msgid "Failed to write variable %s" +msgstr "%s değişkeni yazılamadı" -msgid "E984: :scriptversion used outside of a sourced file" -msgstr "E984: :scriptversion kaynak alınmış bir dosyanın dışında kullanıldı" +#, c-format +msgid "" +"Failed to parse ShaDa file due to a msgpack parser error at position " +"%<PRIu64>" +msgstr "" +"%<PRIu64> konumundaki bir msgpack ayrıştırıcı hatası nedeniyle paylaşılan " +"veri dosyası ayrıştırılamadı" #, c-format -msgid "E999: scriptversion not supported: %d" -msgstr "E999: desteklenmeyen scriptversion: %d" +msgid "" +"Failed to parse ShaDa file: incomplete msgpack string at position %<PRIu64>" +msgstr "" +"Paylaşılan veri dosyası ayrıştırılamadı: %<PRIu64> konumunda tam olmayan " +"msgpack dizisi" -msgid "E168: :finish used outside of a sourced file" -msgstr "E168: :finish kaynak alınmış bir dosyanın dışında kullanıldı" +#, c-format +msgid "" +"Failed to parse ShaDa file: extra bytes in msgpack string at position " +"%<PRIu64>" +msgstr "" +"Paylaşılan veri dosyası ayrıştırılamadı: %<PRIu64> konumundaki msgpack " +"dizisinde ek baytlar" #, c-format -msgid "E383: Invalid search string: %s" -msgstr "E383: Geçersiz arama dizisi: %s" +msgid "" +"System error while opening ShaDa file %s for reading to merge before writing " +"it: %s" +msgstr "" +"%s paylaşılan veri dosyasını yazmadan önce birleştirmek için okumak için " +"açarken sistem hatası: %s" #, c-format -msgid "E384: search hit TOP without match for: %s" -msgstr "E384: Arama dosyanın BAŞINA vardı, %s bulunamadı" +msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!" +msgstr "E138: Tüm %s.tmp.X dosyaları var, paylaşılan veri dosyası yazılamıyor!" #, c-format -msgid "E385: search hit BOTTOM without match for: %s" -msgstr "E385: Arama dosyanın SONUNA vardı, %s bulunamadı" +msgid "System error while opening temporary ShaDa file %s for writing: %s" +msgstr "" +"%s geçici paylaşılan veri dosyası yazma için açılırken sistem hatası: %s" -msgid "E386: Expected '?' or '/' after ';'" -msgstr "E386: ';' sonrasında '?' veya '/' bekleniyordu" +#, c-format +msgid "Failed to create directory %s for writing ShaDa file: %s" +msgstr "Paylaşılan veri dosyasını yazma için %s dizini oluşturulamadı: %s" -msgid " (includes previously listed match)" -msgstr " (daha önce listelenen eşleşmeyi içerir)" +#, c-format +msgid "System error while opening ShaDa file %s for writing: %s" +msgstr "%s paylaşılan veri dosyasını yazma için açarken sistem hatası: %s" -msgid "--- Included files " -msgstr "--- İçerilen dosyalar " +#, c-format +msgid "Writing ShaDa file \"%s\"" +msgstr "Paylaşılan veri dosyası \"%s\" yazılıyor" -msgid "not found " -msgstr "bulunamadı " +#, c-format +msgid "Failed setting uid and gid for file %s: %s" +msgstr "%s dosyası için uid ve gid ayarlanamadı: %s" -msgid "in path ---\n" -msgstr "yolda ---\n" +#, c-format +msgid "E137: ShaDa file is not writable: %s" +msgstr "E137: Paylaşılan veri dosyası yazılabilir değil: %s" -msgid " (Already listed)" -msgstr " (Hâlihazırda listelenmiş)" +#, c-format +msgid "Can't rename ShaDa file from %s to %s!" +msgstr "Paylaşılan veri dosyası %s -> %s olarak yeniden adlandırılamıyor!" -msgid " NOT FOUND" -msgstr " BULUNAMADI" +#, c-format +msgid "Did not rename %s because %s does not look like a ShaDa file" +msgstr "" +"%s yeniden adlandırılmadı; çünkü %s bir paylaşılan veri dosyasına benzemiyor" #, c-format -msgid "Scanning included file: %s" -msgstr "İçerilen dosya taranıyor: %s" +msgid "Did not rename %s to %s because there were errors during writing it" +msgstr "%s dosyasını %s olarak yeniden adlandırmanızın nedeni yazma sırasında " +"hatalar olması mı?" #, c-format -msgid "Searching included file %s" -msgstr "İçerilen dosya %s aranıyor" +msgid "Do not forget to remove %s or rename it manually to %s." +msgstr "%s dosyasını kaldırmayı veya el ile %s olarak yeniden adlandırmayı " +"unutmayın." -msgid "E387: Match is on current line" -msgstr "E387: Eşleşme şu anda bulunulan satırda" +#, c-format +msgid "System error while reading ShaDa file: %s" +msgstr "Paylaşılan veri dosyası okunurken sistem hatası: %s" -msgid "All included files were found" -msgstr "Tüm içerilen dosyalar bulundu" +#, c-format +msgid "System error while reading integer from ShaDa file: %s" +msgstr "Paylaşılan veri dosyasından tamsayı okunurken sistem hatası: %s" -msgid "No included files" -msgstr "İçerilen dosya yok" +#, c-format +msgid "" +"Error while reading ShaDa file: expected positive integer at position " +"%<PRIu64>, but got nothing" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda pozitif tamsayı " +"bekleniyordu; ancak hiçbir şey alınmadı" -msgid "E388: Couldn't find definition" -msgstr "E388: Tanım bulunamadı" +#, c-format +msgid "" +"Error while reading ShaDa file: expected positive integer at position " +"%<PRIu64>" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda pozitif tamsayı " +"bekleniyordu" -msgid "E389: Couldn't find pattern" -msgstr "E389: Dizgi bulunamadı" +#, c-format +msgid "" +"Error while reading ShaDa file: there is an item at position %<PRIu64> that " +"is stated to be too long" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda bir öge var; ancak" +" pek uzun olduğu belirtildi" -msgid "Save View" -msgstr "Görünümü Kaydet" +#, c-format +msgid "" +"Error while reading ShaDa file: there is an item at position %<PRIu64> that " +"must not be there: Missing items are for internal uses only" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumunda orada olmaması " +"gereken bir öge var: Eksik ögeler yalnızca iç kullanım içindir" + +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry that is not a dictionary" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi bir sözlük olmayan bir girdi içeriyor" + +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry with invalid line number" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi geçersiz satır numaralı bir girdi içeriyor" -msgid "Save Session" -msgstr "Oturumu Kaydet" +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry with invalid column number" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi geçersiz sütun numaralı bir girdi içeriyor" -msgid "Save Setup" -msgstr "Kurulumu Kaydet" +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry that does not have a file name" +msgstr "" +"Paylaşılan veri dosyası okunurken hata: %<PRIu64> konumundaki arabellek " +"listesi bir dosya adına iye olmayan bir girdi içeriyor" msgid "[Deleted]" msgstr "[Silindi]" @@ -4845,7 +4860,7 @@ msgstr "%s için işaretler:" #, c-format msgid " group=%s" -msgstr " grup=%s" +msgstr " grup=%s" #, c-format msgid " line=%ld id=%d%s name=%s priority=%d" @@ -4870,8 +4885,8 @@ msgid "E159: Missing sign number" msgstr "E159: İşaret numarası eksik" #, c-format -msgid "E157: Invalid sign ID: %d" -msgstr "E157: Geçersiz işaret kimliği: %d" +msgid "E157: Invalid sign ID: %<PRId64>" +msgstr "E157: Geçersiz işaret kimliği: %<PRId64>" msgid "E934: Cannot jump to a buffer that does not have a name" msgstr "E934: Adı olmayan bir arabelleğe atlamak olanaklı değil" @@ -4883,15 +4898,11 @@ msgstr "E160: Bilinmeyen işaret komutu: %s" msgid "E156: Missing sign name" msgstr "E156: İşaret adı eksik" -msgid " (NOT FOUND)" -msgstr " (BULUNAMADI)" - msgid " (not supported)" msgstr " (desteklenmiyor)" -#, c-format -msgid "Warning: Cannot find word list \"%s_%s.spl\" or \"%s_ascii.spl\"" -msgstr "Uyarı: Sözcük listesi \"%s_%s.spl\" veya \"%s_ascii.spl\" bulunamıyor" +msgid "E759: Format error in spell file" +msgstr "E759: Takas dosyasında biçim hatası" #, c-format msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" @@ -4904,6 +4915,21 @@ msgstr "E797: SpellFileMissing otokomutu arabelleği sildi" msgid "Warning: region %s not supported" msgstr "Uyarı: %s bölgesi desteklenmiyor" +msgid "Sorry, no suggestions" +msgstr "Üzgünüm, şu an için bir önerim yok" + +#, c-format +msgid "Sorry, only %<PRId64> suggestions" +msgstr "Üzgünüm, yalnızca %<PRId64> öneri" + +#, c-format +msgid "Change \"%.*s\" to:" +msgstr "\"%.*s\" şuna değiştirilecek:" + +#, c-format +msgid " < \"%.*s\"" +msgstr " < \"%.*s\"" + msgid "E752: No previous spell replacement" msgstr "E752: Öncesinde düzeltilmiş bir yazım yok" @@ -4922,12 +4948,6 @@ msgstr "%s içinde %d. satır ucunda fazladan metin: %s" msgid "Affix name too long in %s line %d: %s" msgstr "%s içinde %d. satırda ek adı çok uzun: %s" -msgid "E761: Format error in affix file FOL, LOW or UPP" -msgstr "E761: Ekler dosyası FOL, LOW veya UPP içinde biçimlendirme hatası" - -msgid "E762: Character in FOL, LOW or UPP is out of range" -msgstr "E762: FOL, LOW veya UPP içindeki karakterler erimin dışında" - msgid "Compressing word tree..." msgstr "Sözcük soyağacı sıkıştırılıyor..." @@ -4938,8 +4958,12 @@ msgstr "Yazım dosyası \"%s\" okunuyor" msgid "E757: This does not look like a spell file" msgstr "E757: Bu bir yazım dosyasına benzemiyor" +#, c-format +msgid "E5042: Failed to read spell file %s: %s" +msgstr "E5042: %s yazım dosyası okunamadı: %s" + msgid "E771: Old spell file, needs to be updated" -msgstr "E771: Eski yazım dosyası, güncellenmesi gerekiyor" +msgstr "E771: Eski yazım dosyası; güncellenmesi gerekiyor" msgid "E772: Spell file is for newer version of Vim" msgstr "E772: Yazım dosyası Vim'in daha yeni bir sürümü için" @@ -5034,7 +5058,7 @@ msgstr "%s içinde %d. satırda yinelenen ek: %s" #, c-format msgid "" -"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s " +"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGESTin %s " "line %d: %s" msgstr "" "Ek aynı zamanda %s içinde %d. satırda BAD/RARE/KEEPCASE/NEEDAFFIX/" @@ -5064,10 +5088,6 @@ msgstr "%s içinde %d. satırda MAP içinde yinelenen karakter" msgid "Unrecognized or duplicate item in %s line %d: %s" msgstr "%s içinde %d. satırda yinelenen veya tanınmayan öge: %s" -#, c-format -msgid "Missing FOL/LOW/UPP line in %s" -msgstr "%s içinde FOL/LOW/UPP satırı eksik" - msgid "COMPOUNDSYLMAX used without SYLLABLE" msgstr "COMPOUNDSYLMAX, SYLLABLE olmadan kullanılmış" @@ -5151,7 +5171,7 @@ msgstr "%s içinde %ld. satırda yinelenen /regions= satırı yok sayıldı: %s" #, c-format msgid "Too many regions in %s line %ld: %s" -msgstr "%s içinde %ld. satırda çok fazla bölge: %s" +msgstr "%s içinde %ld. satırda pek fazla bölge: %s" #, c-format msgid "/ line ignored in %s line %ld: %s" @@ -5169,12 +5189,9 @@ msgstr "%s içinde %ld. satırda tanınmayan bayraklar: %s" msgid "Ignored %d words with non-ASCII characters" msgstr "ASCII olmayan karakter içeren %d sözcük yok sayıldı" -msgid "E845: Insufficient memory, word list will be incomplete" -msgstr "E845: Yetersiz bellek, sözcük listesi tam olmayacak" - #, c-format -msgid "Compressed %s: %ld of %ld nodes; %ld (%ld%%) remaining" -msgstr "%s sıkıştırılıyor: %ld/%ld uç sıkıştırıldı; %ld (%%%ld) kalan" +msgid "Compressed %s of %ld nodes; %ld (%ld%%) remaining" +msgstr "%s/%ld uç sıkıştırıldı; %ld (%%%ld) kalan" msgid "Reading back spell file..." msgstr "Yazım dosyası yeniden okunuyor..." @@ -5183,8 +5200,8 @@ msgid "Performing soundfolding..." msgstr "Sesler evriştiriliyor..." #, c-format -msgid "Number of words after soundfolding: %ld" -msgstr "Ses evriştirme sonrası sözcük sayısı: %ld" +msgid "Number of words after soundfolding: %<PRId64>" +msgstr "Ses evriştirme sonrası sözcük sayısı: %<PRId64>" #, c-format msgid "Total number of words: %d" @@ -5196,7 +5213,7 @@ msgstr "Öneriler dosyası %s yazılıyor..." #, c-format msgid "Estimated runtime memory use: %d bytes" -msgstr "Tahmini çalıştırılan bellek kullanımı: %d bit" +msgstr "Tahmini çalışma bellek kullanımı: %d bayt" msgid "E751: Output file name must not have region name" msgstr "E751: Çıktı dosyası bir bölge adı içermemelidir" @@ -5220,8 +5237,8 @@ msgid "Done!" msgstr "Yapıldı!" #, c-format -msgid "E765: 'spellfile' does not have %d entries" -msgstr "E765: 'spellfile' içinde %d adet girdi yok" +msgid "E765: 'spellfile' does not have %<PRId64> entries" +msgstr "E765: 'spellfile' içinde %<PRId64> adet girdi yok" #, c-format msgid "Word '%.*s' removed from %s" @@ -5240,20 +5257,14 @@ msgstr "E763: Sözcük karakterleri yazım dosyaları arasında ayrım gösteriy msgid "E783: duplicate char in MAP entry" msgstr "E783: MAP girdisinde yinelenen karakter" -msgid "Sorry, no suggestions" -msgstr "Üzgünüm, şu an için bir önerim yok" - -#, c-format -msgid "Sorry, only %ld suggestions" -msgstr "Üzgünüm, yalnızca %ld öneri" +msgid "E766: Insufficient arguments for printf()" +msgstr "E766: printf() için yetersiz argüman" -#, c-format -msgid "Change \"%.*s\" to:" -msgstr "\"%.*s\" şuna değiştirilecek:" +msgid "E807: Expected Float argument for printf()" +msgstr "E807: printf() için kayan noktalı değer türünde argüman bekleniyordu" -#, c-format -msgid " < \"%.*s\"" -msgstr " < \"%.*s\"" +msgid "E767: Too many arguments to printf()" +msgstr "E767: printf() için pek fazla argüman" #, c-format msgid "E390: Illegal argument: %s" @@ -5265,36 +5276,6 @@ msgstr "Bu arabellek için sözdizim ögeleri tanımlanmamış" msgid "'redrawtime' exceeded, syntax highlighting disabled" msgstr "'redrawtime' aşıldı, sözdizim vurgulaması kapatıldı" -msgid "syntax conceal on" -msgstr "sözdizim gizlemesi açık" - -msgid "syntax conceal off" -msgstr "sözdizim gizlemesi kapalı" - -msgid "syntax case ignore" -msgstr "sözdizim BÜYÜK/küçük harf yok say" - -msgid "syntax case match" -msgstr "sözdizim BÜYÜK/küçük harfe duyarlı" - -msgid "syntax foldlevel start" -msgstr "sözdizim kıvırma düzeyi başlangıcı" - -msgid "syntax foldlevel minimum" -msgstr "sözdizim kıvırma düzeyi an az" - -msgid "syntax spell toplevel" -msgstr "bir sözdizim içinde olmayan metinde yazım denetimi yap" - -msgid "syntax spell notoplevel" -msgstr "bir sözdizim içinde olmayan metinde yazım denetimi yapma" - -msgid "syntax spell default" -msgstr "@Spell kümesi varsa yazım denetimi yapma (öntanımlı)" - -msgid "syntax iskeyword " -msgstr "sözdizim anahtar sözcük" - msgid "syntax iskeyword not set" msgstr "sözdizim anahtar sözcük ayarlanmamış" @@ -5479,7 +5460,7 @@ msgid " or more" msgstr " veya daha fazla" msgid " Using tag with different case!" -msgstr " Etiket değişik bir durumla kullanılıyor!" +msgstr " Etiket değişik bir durumla kullanılıyor!" #, c-format msgid "E429: File \"%s\" does not exist" @@ -5503,16 +5484,12 @@ msgid "Searching tags file %s" msgstr "Etiket dosyası %s aranıyor" #, c-format -msgid "E430: Tag file path truncated for %s\n" -msgstr "E430: %s için etiket dosyası yolu kırpıldı\n" - -#, c-format msgid "E431: Format error in tags file \"%s\"" msgstr "E431: Etiket dosyası \"%s\" içinde biçim hatası" #, c-format -msgid "Before byte %ld" -msgstr "%ld bitinden önce" +msgid "Before byte %<PRId64>" +msgstr "%<PRId64> baytından önce" #, c-format msgid "E432: Tags file not sorted: %s" @@ -5521,9 +5498,6 @@ msgstr "E432: Etiket dosyası sıralanmadı: %s" msgid "E433: No tags file" msgstr "E433: Etiket dosyası yok" -msgid "Ignoring long line in tags file" -msgstr "Etiket dosyasındaki uzun satır yok sayılıyor" - msgid "E434: Can't find tag pattern" msgstr "E434: Etiket dizgisi bulunamıyor" @@ -5534,237 +5508,21 @@ msgstr "E435: Etiket bulunamadı, tahmin ediliyor!" msgid "Duplicate field name: %s" msgstr "Yinelenen alan adı: %s" -msgid "' not known. Available builtin terminals are:" -msgstr "' bilinmiyor. Kullanılabilir uçbirimler şunlar:" - -msgid "defaulting to '" -msgstr "öntanımlı olarak '" - -msgid "E557: Cannot open termcap file" -msgstr "E557: termcap dosyası açılamıyor" - -msgid "E558: Terminal entry not found in terminfo" -msgstr "E558: terminfo içinde uçbirim girdisi bulunamadı" - -msgid "E559: Terminal entry not found in termcap" -msgstr "E559: termcap içinde uçbirim bilgisi bulunamadı" - -#, c-format -msgid "E436: No \"%s\" entry in termcap" -msgstr "E436: termcap içinde \"%s\" girdisi yok" - -msgid "E437: terminal capability \"cm\" required" -msgstr "E437: \"cm\" uçbirim yeteneği gerekiyor" - -msgid "" -"\n" -"--- Terminal keys ---" -msgstr "" -"\n" -"--- Uçbirim düğmeleri ---" - -#, c-format -msgid "E181: Invalid attribute: %s" -msgstr "E181: Geçersiz öznitelik: %s" - -msgid "E279: Sorry, ++shell is not supported on this system" -msgstr "E279: Üzgünüm, ++shell bu sistemde desteklenmiyor" - -#, c-format -msgid "Kill job in \"%s\"?" -msgstr "\"%s\" içindeki iş sonlandırılsın mı?" - -msgid "Terminal" -msgstr "Uçbirim" - -msgid "Terminal-finished" -msgstr "Uçbirim-bitti" - -msgid "active" -msgstr "etkin" - -msgid "running" -msgstr "çalışıyor" - -msgid "finished" -msgstr "bitti" - -msgid "E958: Job already finished" -msgstr "E958: İş bitti bile" - -#, c-format -msgid "E953: File exists: %s" -msgstr "E953: Dosya mevcut: %s" - -msgid "E955: Not a terminal buffer" -msgstr "E955: Bir uçbirim arabelleği değil" - -msgid "E982: ConPTY is not available" -msgstr "E982: ConPTY mevcut değil" - -#, c-format -msgid "E971: Property type %s does not exist" -msgstr "E971: Özellik türü %s mevcut değil" - -#, c-format -msgid "E964: Invalid column number: %ld" -msgstr "E964: Geçersiz sütun numarası: %ld" - -#, c-format -msgid "E966: Invalid line number: %ld" -msgstr "E966: Geçersiz satır numarası: %ld" - -msgid "E965: missing property type name" -msgstr "E965: Özellik tür adı eksik" - -msgid "E275: Cannot add text property to unloaded buffer" -msgstr "E275: Bellekten kaldırılmış arabelleğe metin özelliği eklenemiyor" - -msgid "E967: text property info corrupted" -msgstr "E967: Metin özellik bilgisi hasarlı" - -msgid "E968: Need at least one of 'id' or 'type'" -msgstr "E968: En azından bir 'id' veya 'type' gerekli" - -msgid "E860: Need 'id' and 'type' with 'both'" -msgstr "E860: 'both' ile 'id' ve 'type' gerekli" - -#, c-format -msgid "E969: Property type %s already defined" -msgstr "E969: Özellik türü %s hâlihazırda tanımlanmış" - -#, c-format -msgid "E970: Unknown highlight group name: '%s'" -msgstr "E970: Bilinmeyen vurgulama grup adı: '%s'" - -msgid "(Invalid)" -msgstr "(Geçersiz)" - -msgid "%a %b %d %H:%M:%S %Y" -msgstr "%a %b %d %H:%M:%S %Y" - -#, c-format -msgid "%ld second ago" -msgid_plural "%ld seconds ago" -msgstr[0] "%ld saniye önce" -msgstr[1] "%ld saniye önce" - -msgid "E805: Using a Float as a Number" -msgstr "E805: Bir Kayan Noktalı Değer, Sayı yerine kullanılıyor" - -msgid "E703: Using a Funcref as a Number" -msgstr "E703: Bir Funcref, Sayı yerine kullanılıyor" - -msgid "E745: Using a List as a Number" -msgstr "E745: Bir Liste, Sayı yerine kullanılıyor" - -msgid "E728: Using a Dictionary as a Number" -msgstr "E728: Bir Sözlük, Sayı yerine kullanılıyor" - -msgid "E611: Using a Special as a Number" -msgstr "E611: Bir Özel, Sayı yerine kullanılıyor" - -msgid "E910: Using a Job as a Number" -msgstr "E910: Bir İş, Sayı yerine kullanılıyor" - -msgid "E913: Using a Channel as a Number" -msgstr "E913: Bir Kanal, Sayı yerine kullanılıyor" - -msgid "E974: Using a Blob as a Number" -msgstr "E974: Bir İkili Geniş Nesne, Sayı yerine kullanılıyor" - -msgid "E891: Using a Funcref as a Float" -msgstr "E891: Bir Funcref, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E892: Using a String as a Float" -msgstr "E892: Bir Dizi, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E893: Using a List as a Float" -msgstr "E893: Bir Liste, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E894: Using a Dictionary as a Float" -msgstr "E894: Bir Sözlük, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E362: Using a boolean value as a Float" -msgstr "E362: Bir Boole Değeri, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E907: Using a special value as a Float" -msgstr "E907: Bir Özel Değer, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E911: Using a Job as a Float" -msgstr "E911: Bir İş, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E914: Using a Channel as a Float" -msgstr "E914: Bir Kanal, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E975: Using a Blob as a Float" -msgstr "E975: Bir İkili Geniş Nesne, Kayan Noktalı Değer yerine kullanılıyor" - -msgid "E729: Using a Funcref as a String" -msgstr "E729: Funcref bir Dizi yerine kullanılıyor" - -msgid "E730: Using a List as a String" -msgstr "E730: Liste bir Dizi yerine kullanılıyor" - -msgid "E731: Using a Dictionary as a String" -msgstr "E731: Sözlük bir Dizi yerine kullanılıyor" - -msgid "E976: Using a Blob as a String" -msgstr "E976: İkili Geniş Nesne bir Dizi yerine kullanılıyor" - -msgid "E977: Can only compare Blob with Blob" -msgstr "" -"E977: Bir ikili geniş öğe yalnızca kendinden bir başkası ile " -"karşılaştırılabilir" - -msgid "E691: Can only compare List with List" -msgstr "E691: Bir liste yalnızca başka bir liste ile karşılaştırılabilir" - -msgid "E692: Invalid operation for List" -msgstr "E692: Geçersiz liste işlemi" - -msgid "E735: Can only compare Dictionary with Dictionary" -msgstr "E735: Bir sözlük yalnızca başka bir sözlük ile karşılaştırılabilir" - -msgid "E736: Invalid operation for Dictionary" -msgstr "E736: Geçersiz sözlük işlemi" - -msgid "E694: Invalid operation for Funcrefs" -msgstr "E694: Geçersiz Funcref işlemi" - -#, c-format -msgid "E112: Option name missing: %s" -msgstr "E112: Seçenek adı eksik: %s" - -msgid "E973: Blob literal should have an even number of hex characters" -msgstr "" -"E973: İkili geniş nesne hazır bilgisi çift onalt. karakterlere iye olmalıdır" - -#, c-format -msgid "E114: Missing quote: %s" -msgstr "E114: Tırnak imi eksik: %s" - -#, c-format -msgid "E115: Missing quote: %s" -msgstr "E115: Tırnak imi eksik: %s" - -msgid "new shell started\n" -msgstr "yeni kabuk başlatıldı\n" - -msgid "Vim: Error reading input, exiting...\n" -msgstr "Vim: Girdi okunurken hata, çıkılıyor...\n" +msgid "Beep!" +msgstr "Bip!" msgid "E881: Line count changed unexpectedly" msgstr "E881: Satır sayısı beklenmeyen bir biçimde değişti" -msgid "No undo possible; continue anyway" -msgstr "Geri alma olanaklı değil; bekleme yapma" - #, c-format msgid "E828: Cannot open undo file for writing: %s" msgstr "E828: Geri al dosyası yazma için açılamıyor: %s" #, c-format +msgid "E5003: Unable to create directory \"%s\" for undo file: %s" +msgstr "E5003: \"%s\" dizini, geri al dosyası için oluşturulamadı: %s" + +#, c-format msgid "E825: Corrupted undo file (%s): %s" msgstr "E825: Hasarlı geri al dosyası (%s): %s" @@ -5807,18 +5565,6 @@ msgid "E823: Not an undo file: %s" msgstr "E823: Bir geri al dosyası değil: %s" #, c-format -msgid "E832: Non-encrypted file has encrypted undo file: %s" -msgstr "E832: Şifrelenmemiş dosyanın şifrelenmiş bir geri al dosyası var: %s" - -#, c-format -msgid "E826: Undo file decryption failed: %s" -msgstr "E826: Geri al dosyası şifre çözümü başarısız oldu: %s" - -#, c-format -msgid "E827: Undo file is encrypted: %s" -msgstr "E827: Geri al dosyası şifrelenmiş: %s" - -#, c-format msgid "E824: Incompatible undo file: %s" msgstr "E824: Uyumsuz geri al dosyası: %s" @@ -5836,8 +5582,8 @@ msgid "Already at newest change" msgstr "Hâlihazırda en yeni değişiklik üzerinde" #, c-format -msgid "E830: Undo number %ld not found" -msgstr "E830: %ld numaralı geri alma bulunamadı" +msgid "E830: Undo number %<PRId64> not found" +msgstr "E830: %<PRId64> numaralı geri alma bulunamadı" msgid "E438: u_undo: line numbers wrong" msgstr "E438: u_undo: Satır numaraları yanlış" @@ -5861,15 +5607,15 @@ msgid "changes" msgstr "değişiklik" #, c-format -msgid "%ld %s; %s #%ld %s" -msgstr "%ld %s; %s #%ld %s" - -msgid "before" -msgstr "şundan önce:" +msgid "%<PRId64> %s; %s #%<PRId64> %s" +msgstr "%<PRId64> %s; %s #%<PRId64> %s" msgid "after" msgstr "şundan sonra:" +msgid "before" +msgstr "şundan önce:" + msgid "Nothing to undo" msgstr "Geri alınacak bir şey yok" @@ -5887,275 +5633,12 @@ msgstr "E440: Geri al satırı eksik" msgid "" "\n" -" Name Args Address Complete Definition" -msgstr "" "\n" -" Ad Dğkl Adres Tam Tanım" - -msgid "No user-defined commands found" -msgstr "Kullanıcı tanımlı bir komut bulunamadı" - -#, c-format -msgid "E180: Invalid address type value: %s" -msgstr "E180: Geçersiz adres türü değeri: %s" - -#, c-format -msgid "E180: Invalid complete value: %s" -msgstr "E180: Geçersiz tam değer: %s" - -msgid "E468: Completion argument only allowed for custom completion" -msgstr "E468: Tamamlama argümanına yalnızca özel tamamlamalarda izin verilir" - -msgid "E467: Custom completion requires a function argument" -msgstr "E467: Özel tamamlama bir işlev argümanı gerektirir" - -msgid "E175: No attribute specified" -msgstr "E175: Bir öznitelik belirtilmemiş" - -msgid "E176: Invalid number of arguments" -msgstr "E176: Geçersiz argüman sayısı" - -msgid "E177: Count cannot be specified twice" -msgstr "E177: Sayım iki defa belirtilemez" - -msgid "E178: Invalid default value for count" -msgstr "E178: Sayım için geçersiz öntanımlı değer" - -msgid "E179: argument required for -complete" -msgstr "E179: -complete için argüman gerekiyor" - -msgid "E179: argument required for -addr" -msgstr "E179: -addr için argüman gerekiyor" - -#, c-format -msgid "E174: Command already exists: add ! to replace it: %s" -msgstr "E174: Komut zaten mevcut: değiştirmek için ! ekleyin: %s" - -msgid "E182: Invalid command name" -msgstr "E182: Geçersiz komut adı" - -msgid "E183: User defined commands must start with an uppercase letter" -msgstr "E183: Kullanıcı tanımlı komutlar BÜYÜK harfle başlamalıdır" - -msgid "E841: Reserved name, cannot be used for user defined command" -msgstr "E841: Ayrılmış ad, kullanıcı tanımlı komut için kullanılamaz" - -#, c-format -msgid "E184: No such user-defined command: %s" -msgstr "E184: Böyle bir kullanıcı tanımlı komut yok: %s" - -#, c-format -msgid "E122: Function %s already exists, add ! to replace it" -msgstr "E122: %s işlevi hâlihazırda mevcut, değiştirmek için ! ekleyin" - -msgid "E717: Dictionary entry already exists" -msgstr "E717: Sözlük girdisi hâlihazırda mevcut" - -msgid "E718: Funcref required" -msgstr "E718: Funcref gerekiyor" - -#, c-format -msgid "E130: Unknown function: %s" -msgstr "E130: Bilinmeyen işlev: %s" - -#, c-format -msgid "E125: Illegal argument: %s" -msgstr "E125: İzin verilmeyen argüman: %s" - -#, c-format -msgid "E853: Duplicate argument name: %s" -msgstr "E853: Yinelenen argüman adı: %s" - -msgid "E989: Non-default argument follows default argument" -msgstr "E989: Öntanımlı olmayan argüman öntanımlı argümandan sonra" - -msgid "E126: Missing :endfunction" -msgstr "E126: :endfunction eksik" - -#, c-format -msgid "W22: Text found after :endfunction: %s" -msgstr "W22: :endfunction sonrası metin bulundu: %s" - -#, c-format -msgid "E451: Expected }: %s" -msgstr "E451: } bekleniyordu: %s" - -#, c-format -msgid "E740: Too many arguments for function %s" -msgstr "E740: %s işlevi için çok fazla argüman" - -#, c-format -msgid "E116: Invalid arguments for function %s" -msgstr "E116: %s işlevi için geçersiz argümanlar" - -msgid "E132: Function call depth is higher than 'maxfuncdepth'" -msgstr "E132: İşlevin çağırdığı derinlik 'maxfuncdepth'ten daha yüksek" - -#, c-format -msgid "calling %s" -msgstr "%s çağrılıyor" - -#, c-format -msgid "%s aborted" -msgstr "%s durduruldu" - -#, c-format -msgid "%s returning #%ld" -msgstr "%s, #%ld döndürüyor" - -#, c-format -msgid "%s returning %s" -msgstr "%s, %s döndürüyor" - -msgid "E699: Too many arguments" -msgstr "E699: Çok fazla argüman" - -#, c-format -msgid "E276: Cannot use function as a method: %s" -msgstr "E276: İşlev bir yöntem olarak kullanılamaz: %s" - -#, c-format -msgid "E120: Using <SID> not in a script context: %s" -msgstr "E120: <SID> bir betik bağlamında kullanılmıyor: %s" - -#, c-format -msgid "E725: Calling dict function without Dictionary: %s" -msgstr "E725: dic işlevi bir sözlük olmadan çağrılıyor: %s" - -msgid "E129: Function name required" -msgstr "E129: İşlev adı gerekiyor" - -#, c-format -msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: İşlev adı bir BÜYÜK harfle veya \"s:\" ile başlamalı: %s" - -#, c-format -msgid "E884: Function name cannot contain a colon: %s" -msgstr "E884: İşlev adı iki nokta içeremez: %s" - -msgid "E454: function list was modified" -msgstr "E454: İşlev listesi değiştirilmiş" - -#, c-format -msgid "E123: Undefined function: %s" -msgstr "E123: Tanımlanmamış işlev: %s" - -#, c-format -msgid "E124: Missing '(': %s" -msgstr "E124: '(' eksik: %s" - -msgid "E862: Cannot use g: here" -msgstr "E862: g: burada kullanılamaz" - -#, c-format -msgid "E932: Closure function should not be at top level: %s" -msgstr "E932: Kapatma işlevi en üst düzeyde olmamalıdır: %s" - -#, c-format -msgid "E707: Function name conflicts with variable: %s" -msgstr "E707: İşlev adı şu değişken ile çakışıyor: %s" - -#, c-format -msgid "E127: Cannot redefine function %s: It is in use" -msgstr "E127: %s işlevi yeniden tanımlanamıyor: Şu an kullanımda" - -#, c-format -msgid "E746: Function name does not match script file name: %s" -msgstr "E746: İşlev adı betik dosyası adına eşleşmiyor: %s" - -#, c-format -msgid "E131: Cannot delete function %s: It is in use" -msgstr "E131: %s işlevi silinemiyor: Şu an kullanımda" - -msgid "E133: :return not inside a function" -msgstr "E133: :return bir işlev içinde değil" - -#, c-format -msgid "%s (%s, compiled %s)" -msgstr "%s (%s, %s tarihinde derlendi)" - -msgid "" -"\n" -"MS-Windows 64-bit GUI/console version" -msgstr "" -"\n" -"MS-Windows 64-bit grafik arabirim/konsol sürümü" - -msgid "" -"\n" -"MS-Windows 32-bit GUI/console version" -msgstr "" -"\n" -"MS-Windows 32-bit grafik arabirim/konsol sürümü" - -msgid "" -"\n" -"MS-Windows 64-bit GUI version" -msgstr "" -"\n" -"MS-Windows 64-bit grafik arabirim sürümü" - -msgid "" -"\n" -"MS-Windows 32-bit GUI version" -msgstr "" -"\n" -"MS-Windows 32-bit grafik arabirim sürümü" - -msgid " with OLE support" -msgstr ", OLE desteği ile" - -msgid "" -"\n" -"MS-Windows 64-bit console version" -msgstr "" -"\n" -"MS-Windows 64-bit konsol sürümü" - -msgid "" -"\n" -"MS-Windows 32-bit console version" -msgstr "" -"\n" -"MS-Windows 32-bit konsol sürümü" - -msgid "" -"\n" -"macOS version" -msgstr "" -"\n" -"macOS sürümü" - -msgid "" -"\n" -"macOS version w/o darwin feat." +"Features: " msgstr "" "\n" -"Darwin özellikleri olmayan macOS sürümü" - -msgid "" -"\n" -"OpenVMS version" -msgstr "" "\n" -"OpenVMS sürümü" - -msgid "" -"\n" -"Included patches: " -msgstr "" -"\n" -"İçerilen yamalar: " - -msgid "" -"\n" -"Extra patches: " -msgstr "" -"\n" -"Ek yamalar: " - -msgid "Modified by " -msgstr "Değiştirme: " +"Özellikler: " msgid "" "\n" @@ -6167,181 +5650,38 @@ msgstr "" msgid "by " msgstr " " -msgid "" -"\n" -"Huge version " -msgstr "" -"\n" -"Dev sürüm " - -msgid "" -"\n" -"Big version " -msgstr "" -"\n" -"Büyük sürüm " - -msgid "" -"\n" -"Normal version " -msgstr "" -"\n" -"Orta boy sürüm " - -msgid "" -"\n" -"Small version " -msgstr "" -"\n" -"Küçük sürüm " - -msgid "" -"\n" -"Tiny version " -msgstr "" -"\n" -"Ufak sürüm " - -msgid "without GUI." -msgstr "(grafik arabirim içermez)." - -msgid "with GTK3 GUI." -msgstr "(GTK3 grafik arabirim ile)." - -msgid "with GTK2-GNOME GUI." -msgstr "(GTK2-GNOME grafik arabirim ile)." - -msgid "with GTK2 GUI." -msgstr "(GTK2 grafik arabirim ile)." - -msgid "with X11-Motif GUI." -msgstr "(X11-Motif grafik arabirim ile)." - -msgid "with X11-neXtaw GUI." -msgstr "(X11-neXtaw grafik arabirim ile)." - -msgid "with X11-Athena GUI." -msgstr "(X11-Athena grafik arabirim ile)." - -msgid "with Haiku GUI." -msgstr "(Haiku grafik arabirimi ile)." - -msgid "with Photon GUI." -msgstr "(Photon grafik arabirim ile)." - -msgid "with GUI." -msgstr "(grafik arabirim ile)." - -msgid " Features included (+) or not (-):\n" -msgstr " İçerilen özellikler (+), içerilmeyenler (-) ile gösterilir:\n" - msgid " system vimrc file: \"" msgstr " sistem vimrc dosyası: \"" -msgid " user vimrc file: \"" -msgstr " kullanıcı vimrc dosyası: \"" - -msgid " 2nd user vimrc file: \"" -msgstr " kullanıcı 2. vimrc dosyası: \"" - -msgid " 3rd user vimrc file: \"" -msgstr " kullanıcı 3. vimrc dosyası: \"" - -msgid " user exrc file: \"" -msgstr " kullanıcı exrc dosyası: \"" - -msgid " 2nd user exrc file: \"" -msgstr " kullanıcı 2. exrc dosyası: \"" - -msgid " system gvimrc file: \"" -msgstr " sistem gvimrc dosyası: \"" - -msgid " user gvimrc file: \"" -msgstr " kullanıcı gvimrc dosyası: \"" - -msgid "2nd user gvimrc file: \"" -msgstr " kullanıcı 2. gvimrc dosyası: \"" - -msgid "3rd user gvimrc file: \"" -msgstr " kullanıcı 3. gvimrc dosyası: \"" - -msgid " defaults file: \"" -msgstr " öntanımlılar dosyası: \"" - -msgid " system menu file: \"" -msgstr " sistem menü dosyaları: \"" - msgid " fall-back for $VIM: \"" msgstr " $VIM öntanımlı konumu: \"" msgid " f-b for $VIMRUNTIME: \"" msgstr "$VIMRUNTIME öntanımlı konumu: \"" -msgid "Compilation: " -msgstr "Derleme: " - -msgid "Compiler: " -msgstr "Derleyici: " - -msgid "Linking: " -msgstr "Bağlama: " +msgid "Nvim is open source and freely distributable" +msgstr "Nvim açık kaynaklıdır ve özgürce dağıtılabilir" -msgid " DEBUG BUILD" -msgstr " HATA AYIKLAMA AMAÇLI SÜRÜM" +msgid "https://neovim.io/#chat" +msgstr "https://neovim.io/#chat" -msgid "VIM - Vi IMproved" -msgstr "VİM - Vi IMproved" +msgid "type :help nvim<Enter> if you are new! " +msgstr "eğer yeniyseniz :help nvim<Enter> " -msgid "version " -msgstr "sürüm: " +msgid "type :checkhealth<Enter> to optimize Nvim" +msgstr "Nvim'i eniyilemek için :help checkhealth<Enter>" -msgid "by Bram Moolenaar et al." -msgstr "geliştirme: Bram Moolenaar ve diğerleri" +msgid "type :q<Enter> to exit " +msgstr "çıkmak için :q<Enter> " -msgid "Vim is open source and freely distributable" -msgstr "Vim açık kaynaklıdır ve özgürce dağıtılabilir" +msgid "type :help<Enter> for help " +msgstr "yardım için :help<Enter> " msgid "Help poor children in Uganda!" msgstr "Uganda'daki yoksul çocuklara yardım edin!" msgid "type :help iccf<Enter> for information " -msgstr "ek bilgi için :help iccf<Enter> " - -msgid "type :q<Enter> to exit " -msgstr "çıkmak için :q<Enter> " - -msgid "type :help<Enter> or <F1> for on-line help" -msgstr "yardım belgeleri için :help<Enter> veya <F1> " - -msgid "type :help version8<Enter> for version info" -msgstr "sürüm bilgisi için :help version8<Enter> " - -msgid "Running in Vi compatible mode" -msgstr "Vi uyumlu kipte çalışıyor" - -msgid "type :set nocp<Enter> for Vim defaults" -msgstr "öntanımlı ayarlar için :set nocp<Enter> " - -msgid "type :help cp-default<Enter> for info on this" -msgstr "ek bilgi için :help cp-default<Enter>" - -msgid "menu Help->Orphans for information " -msgstr "bilgi için menü -> Yardım -> Yetimler" - -msgid "Running modeless, typed text is inserted" -msgstr "Kipsiz çalışıyor, girilen metin doğrudan eklenir" - -msgid "menu Edit->Global Settings->Toggle Insert Mode " -msgstr "menü -> Düzen -> Genel Ayarlar -> Ekleme Kipine Geç" - -msgid " for two modes " -msgstr " iki kip için " - -msgid "menu Edit->Global Settings->Toggle Vi Compatible" -msgstr "menü -> Düzen -> Genel Ayarlar -> Vi Uyumlu Kipi Aç/Kapat" - -msgid " for Vim defaults " -msgstr " Vim öntanımlıları için " +msgstr "ek bilgi için :help iccf<Enter> " msgid "Sponsor Vim development!" msgstr "Vim'in geliştirilmesine sponsor olun!" @@ -6350,3303 +5690,200 @@ msgid "Become a registered Vim user!" msgstr "Kayıtlı bir Vim kullanıcısı olun!" msgid "type :help sponsor<Enter> for information " -msgstr "bilgi için :help sponsor<Enter> " +msgstr "bilgi için :help sponsor<Enter> " msgid "type :help register<Enter> for information " -msgstr "bilgi için :help register<Enter> " +msgstr "bilgi için :help register<Enter> " msgid "menu Help->Sponsor/Register for information " -msgstr "bilgi için Yardım -> Sponsorluk/Kayıt" - -msgid "[end of lines]" -msgstr "[satırların sonu]" - -msgid "global" -msgstr "global" - -msgid "buffer" -msgstr "arabellek" - -msgid "window" -msgstr "pencere" - -msgid "tab" -msgstr "sekme" - -msgid "" -"\n" -"# Buffer list:\n" -msgstr "" -"\n" -"# Arabellek listesi:\n" - -#, c-format -msgid "" -"\n" -"# %s History (newest to oldest):\n" -msgstr "" -"\n" -"# %s Geçmişi (yeniden eskiye):\n" - -msgid "Command Line" -msgstr "Komut Satırı" - -msgid "Search String" -msgstr "Arama Dizisi" - -msgid "Expression" -msgstr "İfade" - -msgid "Input Line" -msgstr "Girdi Satırı" - -msgid "Debug Line" -msgstr "Hata Ayıklama Satırı" - -msgid "" -"\n" -"# Bar lines, copied verbatim:\n" -msgstr "" -"\n" -"# Tam sureti kopyalanan | satırları:\n" - -#, c-format -msgid "%sviminfo: %s in line: " -msgstr "%sviminfo: satırdaki %s: " - -msgid "E136: viminfo: Too many errors, skipping rest of file" -msgstr "E136: viminfo: Çok fazla hata, dosyanın geri kalanı atlanıyor" - -msgid "" -"\n" -"# global variables:\n" -msgstr "" -"\n" -"# global değişkenler:\n" - -msgid "" -"\n" -"# Last Substitute String:\n" -"$" -msgstr "" -"\n" -"# Son Değiştirilen Dizi:\n" -"$" - -#, c-format -msgid "" -"\n" -"# Last %sSearch Pattern:\n" -"~" -msgstr "" -"\n" -"# Son %sArama Dizgileri:\n" -"~" - -msgid "Substitute " -msgstr "Şunu değiştir: " - -msgid "Illegal register name" -msgstr "İzin verilmeyen yazmaç adı" - -msgid "" -"\n" -"# Registers:\n" -msgstr "" -"\n" -"# Yazmaçlar:\n" - -#, c-format -msgid "E574: Unknown register type %d" -msgstr "E574: Bilinmeyen yazmaç türü %d" - -msgid "" -"\n" -"# History of marks within files (newest to oldest):\n" -msgstr "" -"\n" -"# Dosyalardaki imlerin geçmişi (yeniden eskiye):\n" - -msgid "" -"\n" -"# File marks:\n" -msgstr "" -"\n" -"# Dosya imleri:\n" - -msgid "" -"\n" -"# Jumplist (newest first):\n" -msgstr "" -"\n" -"# Atlama listesi (önce en yeniler):\n" - -msgid "Missing '>'" -msgstr "'>' eksik" - -msgid "Illegal starting char" -msgstr "İzin verilmeyen başlangıç karakteri" - -#, c-format -msgid "# This viminfo file was generated by Vim %s.\n" -msgstr "# Bu viminfo dosyası Vim %s tarafından oluşturulmuştur.\n" - -msgid "" -"# You may edit it if you're careful!\n" -"\n" -msgstr "" -"# Yapabileceğinizi düşünüyorsanız bu dosyayı düzenleyebilirsiniz!\n" -"\n" - -msgid "# Value of 'encoding' when this file was written\n" -msgstr "# Bu dosya yazıldığı sırada mevcut 'encoding'in değeri\n" - -#, c-format -msgid "Reading viminfo file \"%s\"%s%s%s%s" -msgstr "\"%s\" viminfo dosyası okunuyor...%s%s%s%s" - -msgid " info" -msgstr " bilgiler-" - -msgid " marks" -msgstr " imler-" - -msgid " oldfiles" -msgstr " düzenleme geçmişi" - -msgid " FAILED" -msgstr " BAŞARISIZ" - -#, c-format -msgid "E137: Viminfo file is not writable: %s" -msgstr "E137: Viminfo dosyası yazılabilir değil: %s" - -#, c-format -msgid "E929: Too many viminfo temp files, like %s!" -msgstr "E929: Çok fazla viminfo geçici dosyası, örneğin %s!" - -#, c-format -msgid "E138: Can't write viminfo file %s!" -msgstr "E138: viminfo dosyası %s yazılamıyor!" - -#, c-format -msgid "Writing viminfo file \"%s\"" -msgstr "viminfo dosyası \"%s\" yazılıyor" - -#, c-format -msgid "E886: Can't rename viminfo file to %s!" -msgstr "E886: viminfo dosyasının adı %s olarak değiştirilemiyor!" - -msgid "E195: Cannot open viminfo file for reading" -msgstr "E195: viminfo dosyası okuma için açılamıyor" - -msgid "Already only one window" -msgstr "Zaten tek pencere" - -#, c-format -msgid "E92: Buffer %ld not found" -msgstr "E92: Arabellek %ld bulunamadı" - -msgid "E441: There is no preview window" -msgstr "E441: Önizleme penceresi yok" - -msgid "E242: Can't split a window while closing another" -msgstr "E242: Bir başka pencere kapatılırken pencere bölünemez" - -msgid "E442: Can't split topleft and botright at the same time" -msgstr "E442: Üst sol ve alt sağ pencereler aynı anda bölünemez" - -msgid "E443: Cannot rotate when another window is split" -msgstr "E443: Başka bir pencere bölünmüşken döndürme yapılamaz" - -msgid "E444: Cannot close last window" -msgstr "E444: Son pencere kapatılamıyor" - -msgid "E814: Cannot close window, only autocmd window would remain" -msgstr "E814: Pencere kapatılamıyor, yalnızca otokomut penceresi açık kalır" - -msgid "E445: Other window contains changes" -msgstr "E445: Diğer pencerede değişiklikler var" - -msgid "E366: Not allowed to enter a popup window" -msgstr "E366: Bir açılır pencereye girişe izin verilmiyor" - -#, c-format -msgid "E370: Could not load library %s" -msgstr "E370: %s kitaplığı yüklenemedi" - -msgid "Sorry, this command is disabled: the Perl library could not be loaded." -msgstr "Üzgünüm, bu komut etkin değil: Perl kitaplığı yüklenemedi." - -msgid "E299: Perl evaluation forbidden in sandbox without the Safe module" -msgstr "" -"E299: Güvenli modül olmadan kum havuzu içinde Perl değerlendirmesine izin " -"verilmiyor" - -msgid "Edit with &multiple Vims" -msgstr "Birden &fazla Vim ile düzenle" - -msgid "Edit with single &Vim" -msgstr "Tek bir &Vim ile düzenle" - -msgid "Diff with Vim" -msgstr "Vim kullanarak karşılaştır" - -msgid "Edit with &Vim" -msgstr "&Vim ile düzenle" - -msgid "Edit with existing Vim" -msgstr "Mevcut Vim ile düzenle" - -msgid "Edit with existing Vim - " -msgstr "Mevcut Vim ile düzenle - " - -msgid "Edits the selected file(s) with Vim" -msgstr "Seçili dosyaları Vim ile düzenler" - -msgid "Error creating process: Check if gvim is in your path!" -msgstr "İşlem oluşturulurken hata: gvim'in yol üzerinde olduğundan emin olun!" - -msgid "gvimext.dll error" -msgstr "gvimext.dll hatası" - -msgid "Path length too long!" -msgstr "Yol çok uzun!" - -msgid "E10: \\ should be followed by /, ? or &" -msgstr "E10: \\ sonrasında /, ? veya & gelmeli" - -msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" -msgstr "E11: Komut satırı penceresinde geçersiz; <CR> çalıştırır, CTRL-C çıkar" - -msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" -msgstr "" -"E12: Geçerli dizin veya etiket aramasında exrc veya vimrc'den komutlara izin " -"verilmiyor" - -msgid "E13: File exists (add ! to override)" -msgstr "E13: Dosya mevcut (geçersiz kılmak için ! ekleyin)" - -#, c-format -msgid "E15: Invalid expression: \"%s\"" -msgstr "E15: Geçersiz ifade: \"%s\"" - -msgid "E16: Invalid range" -msgstr "E16: Geçersiz erim" - -#, c-format -msgid "E17: \"%s\" is a directory" -msgstr "E17: \"%s\" bir dizin" - -msgid "E18: Unexpected characters in :let" -msgstr "E18: :let içinde beklenmeyen karakter" - -msgid "E18: Unexpected characters in assignment" -msgstr "E18: Atama içerisinde beklenmedik karakterler" - -msgid "E19: Mark has invalid line number" -msgstr "E19: İm satır numarası geçersiz" - -msgid "E20: Mark not set" -msgstr "E20: İm ayarlanmamış" - -msgid "E21: Cannot make changes, 'modifiable' is off" -msgstr "E21: Değişiklik yapılamıyor, 'modifiable' kapalı" - -msgid "E22: Scripts nested too deep" -msgstr "E22: Betikler çok iç içe geçmiş" - -msgid "E23: No alternate file" -msgstr "E23: Başka bir dosya yok" - -msgid "E24: No such abbreviation" -msgstr "E24: Böyle bir kısaltma yok" - -#, c-format -msgid "E121: Undefined variable: %s" -msgstr "E121: Tanımlanmamış değişken: %s" - -#, c-format -msgid "E121: Undefined variable: %c:%s" -msgstr "E121: Tanımlanmamış değişken: %c:%s" - -msgid "E464: Ambiguous use of user-defined command" -msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı" - -msgid "E476: Invalid command" -msgstr "E476: Geçersiz komut" - -#, c-format -msgid "E476: Invalid command: %s" -msgstr "E476: Geçersiz komut: %s" - -msgid "E710: List value has more items than targets" -msgstr "E710: Liste değeri hedeften daha fazla ögeye sahip" - -msgid "E711: List value does not have enough items" -msgstr "E711: Liste değeri yeterli ögeye sahip değil" - -msgid "E719: Cannot slice a Dictionary" -msgstr "E719: Bir Sözlük dilimlenemiyor" - -msgid "" -"E856: \"assert_fails()\" second argument must be a string or a list with one " -"or two strings" -msgstr "" -"E856: \"assert_fails()\" ikinci argüman bir dizi veya bir veya iki dizili " -"bir liste olmalıdır" - -#, c-format -msgid "E908: using an invalid value as a String: %s" -msgstr "E908: Geçersiz bir değer bir Dizi yerine kullanılıyor: %s" - -msgid "E909: Cannot index a special variable" -msgstr "E909: Özel bir değişken dizinlenemiyor" - -#, c-format -msgid "E1100: Command not supported in Vim9 script (missing :var?): %s" -msgstr "E1100: Komut Vim9 betiğinde desteklenmiyor (:var? eksik): %s" - -#, c-format -msgid "E1001: Variable not found: %s" -msgstr "E1001: Değişken bulunamadı: %s" - -#, c-format -msgid "E1002: Syntax error at %s" -msgstr "E1002: %s konumunda sözdizim hatası" - -msgid "E1003: Missing return value" -msgstr "E1003: Dönüş değeri eksik" - -#, c-format -msgid "E1004: White space required before and after '%s' at \"%s\"" -msgstr "E1004: Şu konumda '%s' öncesinde ve sonrasında boşluk gerekiyor: \"%s\"" - -msgid "E1005: Too many argument types" -msgstr "E1005: Çok fazla argüman türü" - -#, c-format -msgid "E1006: %s is used as an argument" -msgstr "E1006: %s bir argüman olarak kullanılıyor" - -msgid "E1007: Mandatory argument after optional argument" -msgstr "E1007: İsteğe bağlı argüman sonrasında zorunlu argüman" - -msgid "E1008: Missing <type>" -msgstr "E1008: <tür> eksik" - -msgid "E1009: Missing > after type" -msgstr "E1009: Tür sonrasında > eksik" - -#, c-format -msgid "E1010: Type not recognized: %s" -msgstr "E1010: Tür tanımlanamadı: %s" - -#, c-format -msgid "E1011: Name too long: %s" -msgstr "E1011: Ad çok uzun: %s" - -#, c-format -msgid "E1012: Type mismatch; expected %s but got %s" -msgstr "E1012: Tür uyumsuzluğu, %s bekleniyordu, ancak %s alındı" - -#, c-format -msgid "E1013: Argument %d: type mismatch, expected %s but got %s" -msgstr "E1013: %d argümanı: Tür uyumsuzluğu, %s bekleniyordu, ancak %s alındı" - -#, c-format -msgid "E1014: Invalid key: %s" -msgstr "E1014: Geçersiz anahtar: %s" - -#, c-format -msgid "E1015: Name expected: %s" -msgstr "E1015: Ad bekleniyordu: %s" - -#, c-format -msgid "E1016: Cannot declare a %s variable: %s" -msgstr "E1016: Bir %s değişkeni tanımlanamıyor: %s" - -#, c-format -msgid "E1016: Cannot declare an environment variable: %s" -msgstr "E1016: Bir ortam değişkeni tanımlanamıyor: %s" - -#, c-format -msgid "E1017: Variable already declared: %s" -msgstr "E1017: Değişken halihazırda tanımlanmış: %s" - -#, c-format -msgid "E1018: Cannot assign to a constant: %s" -msgstr "E1018: Bir sabite atanamıyor: %s" - -msgid "E1019: Can only concatenate to string" -msgstr "E1019: Yalnızca bir diziye birleştirilebilir" +msgstr "bilgi için Yardım -> Sponsorluk/Kayıt" #, c-format -msgid "E1020: Cannot use an operator on a new variable: %s" -msgstr "E1020: Yeni bir değişken üzerinde bir işleç kullanılamaz: %s" - -msgid "E1021: Const requires a value" -msgstr "E1021: Sabit, bir değer gerektirir" - -msgid "E1022: Type or initialization required" -msgstr "E1022: Tür veya ilklendirme gerekiyor" +msgid "E15: Invalid control character present in input: %.*s" +msgstr "E15: Girdide geçersiz denetim karakteri var: %.*s" #, c-format -msgid "E1023: Using a Number as a Bool: %lld" -msgstr "E1023: Bir Sayı, bir Boole yerine kullanılıyor: %lld" - -msgid "E1024: Using a Number as a String" -msgstr "E1024: Bir Sayı, bir Dizi yerine kullanılıyor" - -msgid "E1025: Using } outside of a block scope" -msgstr "E1025: } bir blok kapsamı dışında kullanılıyor" - -msgid "E1026: Missing }" -msgstr "E1026: } eksik" - -msgid "E1027: Missing return statement" -msgstr "E1027: Dönüş ifadesi eksik" - -msgid "E1028: Compiling :def function failed" -msgstr "E1028: :def işlevi derleme başarısız" +msgid "E112: Option name missing: %.*s" +msgstr "E112: Seçenek adı eksik: %.*s" #, c-format -msgid "E1029: Expected %s but got %s" -msgstr "E1029: %s bekleniyordu ancak %s alındı" +msgid "E15: Unexpected EOC character: %.*s" +msgstr "E15: Beklenmedik EOC karakteri: %.*s" #, c-format -msgid "E1030: Using a String as a Number: \"%s\"" -msgstr "E1030: Bir Dizi, bir Sayı yerine kullanılıyor: \"%s\"" - -msgid "E1031: Cannot use void value" -msgstr "E1031: Boş değer kullanılamaz" - -msgid "E1032: Missing :catch or :finally" -msgstr "E1032: :catch veya :finally eksik" - -msgid "E1033: Catch unreachable after catch-all" -msgstr "E1033: catch-all sonrası catch ulaşılamıyor" +msgid "E15: Unidentified character: %.*s" +msgstr "E15: Tanımlanmamış karakter: %.*s" #, c-format -msgid "E1034: Cannot use reserved name %s" -msgstr "E1034: Ayrılmış ad %s kullanılamaz" - -msgid "E1035: % requires number arguments" -msgstr "E1035: %, sayı argümanları gerektirir" +msgid "E15: Operator is not associative: %.*s" +msgstr "E15: İşleç, çağrışımsal değil: %.*s" #, c-format -msgid "E1036: %c requires number or float arguments" -msgstr "E1036: %c, sayı veya kayan noktalı değer argümanları gerektirir" +msgid "E15: Missing operator: %.*s" +msgstr "E15: İşleç eksik: %.*s" #, c-format -msgid "E1037: Cannot use \"%s\" with %s" -msgstr "E1037: \"%s\", %s ile birlikte kullanılamaz" - -msgid "E1038: \"vim9script\" can only be used in a script" -msgstr "E1038: \"vim9script\" yalnızca bir betikte kullanılabilir" - -msgid "E1039: \"vim9script\" must be the first command in a script" -msgstr "E1039: \"vim9script\" bir betikteki ilk komut olmalıdır" - -msgid "E1040: Cannot use :scriptversion after :vim9script" -msgstr "E1040: :vim9script sonrası :scriptversion kullanılamaz" +msgid "E15: Expected lambda arguments list or arrow: %.*s" +msgstr "E15: Lambda argümanlar listesi veya ok bekleniyordu: %.*s" #, c-format -msgid "E1041: Redefining script item %s" -msgstr "E1041: Betik ögesi %s yeniden tanımlanıyor" - -msgid "E1042: Export can only be used in vim9script" -msgstr "E1042: Dışa aktarım yalnızca vim9script içinde kullanılabilir" - -msgid "E1043: Invalid command after :export" -msgstr "E1043: :export sonrası geçersiz komut" - -msgid "E1044: Export with invalid argument" -msgstr "E1044: Geçersiz argümanla dışa aktarım" - -msgid "E1045: Missing \"as\" after *" -msgstr "E1045: * sonrası \"as\" eksik" - -msgid "E1046: Missing comma in import" -msgstr "E1046: İçe aktarımda virgül eksik" - -msgid "E1047: Syntax error in import" -msgstr "E1047: İçe aktarımda sözdizim hatası" - -#, c-format -msgid "E1048: Item not found in script: %s" -msgstr "E1048: Betikte öge bulunamadı: %s" - -#, c-format -msgid "E1049: Item not exported in script: %s" -msgstr "E1049: Betikte öge dışa aktarılmadı: %s" - -#, c-format -msgid "E1050: Colon required before a range: %s" -msgstr "E1050: Bir erim öncesi iki nokta gerekiyor: %s" - -msgid "E1051: Wrong argument type for +" -msgstr "E1051: + için hatalı argüman türü" - -#, c-format -msgid "E1052: Cannot declare an option: %s" -msgstr "E1052: Bir seçenek tanımlanamıyor: %s" - -#, c-format -msgid "E1053: Could not import \"%s\"" -msgstr "E1053: \"%s\" içe aktarılamadı" - -#, c-format -msgid "E1054: Variable already declared in the script: %s" -msgstr "E1054: Betikte değişken halihazırda tanımlanmış: %s" - -msgid "E1055: Missing name after ..." -msgstr "E1055: ... sonraki ad eksik" - -#, c-format -msgid "E1056: Expected a type: %s" -msgstr "E1056: Bir tür bekleniyordu: %s" - -msgid "E1057: Missing :enddef" -msgstr "E1057: :enddef eksik" - -msgid "E1058: Function nesting too deep" -msgstr "E1058: İşlev çok iç içe geçmiş" +msgid "E15: Expected value part of assignment lvalue: %.*s" +msgstr "E15: lvalue ataması değer kısmı bekleniyordu: %.*s" #, c-format -msgid "E1059: No white space allowed before colon: %s" -msgstr "E1059: İki nokta öncesinde boşluğa izin verilmiyor: %s" +msgid "E15: Expected assignment operator or subscript: %.*s" +msgstr "E15: Atama işleci veya alt simgesi bekleniyordu: %.*s" -#, c-format -msgid "E1060: Expected dot after name: %s" -msgstr "E1060: Ad sonrası nokta bekleniyordu: %s" +msgid "E15: Unexpected " +msgstr "E15: Beklenmedik " #, c-format -msgid "E1061: Cannot find function %s" -msgstr "E1061: %s işlevi bulunamıyor" - -msgid "E1062: Cannot index a Number" -msgstr "E1062: Bir Sayı dizinlenemiyor" +msgid "E15: Unexpected multiplication-like operator: %.*s" +msgstr "E15: Beklenmedik çarpma benzeri işleç: %.*s" -msgid "E1063: Type mismatch for v: variable" -msgstr "E1063: v: değişkeni için tür uyumsuzluğu" +msgid "E15: Environment variable name missing" +msgstr "E15: Çevre değişkeni adı eksik" #, c-format -msgid "E1066: Cannot declare a register: %s" -msgstr "E1066: Bir yazmaç tanımlanamıyor: %s" +msgid "E15: Expected value, got comparison operator: %.*s" +msgstr "E15: Değer bekleniyordu, karşılaştırma işleci alındı: %.*s" #, c-format -msgid "E1067: Separator mismatch: %s" -msgstr "E1067: Ayırıcı uyumsuzluğu: %s" +msgid "E15: Expected value, got comma: %.*s" +msgstr "E15: Değer bekleniyordu, virgül alındı: %.*s" #, c-format -msgid "E1068: No white space allowed before '%s': %s" -msgstr "E1068: '%s' önce boşluğa izin verilmiyor: %s" +msgid "E15: Comma outside of call, lambda or literal: %.*s" +msgstr "E15: Çağrı, lambda veya düz veri dışı virgül: %.*s" #, c-format -msgid "E1069: White space required after '%s': %s" -msgstr "E1069: '%s' sonrası boşluk gerekiyor: %s" - -msgid "E1070: Missing \"from\"" -msgstr "E1070: \"from\" eksik" - -msgid "E1071: Invalid string after \"from\"" -msgstr "E1071: \"from\" sonrası geçersiz dizi" +msgid "E15: Colon outside of dictionary or ternary operator: %.*s" +msgstr "E15: Sözlük veya üç terimli işlec dışı iki nokta: %.*s" #, c-format -msgid "E1072: Cannot compare %s with %s" -msgstr "E1072: %s, %s ile karşılaştırılamıyor" +msgid "E15: Expected value, got closing bracket: %.*s" +msgstr "E15: Değer bekleniyordu, kapatma ayracı alındı: %.*s" #, c-format -msgid "E1073: Name already defined: %s" -msgstr "E1073: Ad halihazırda tanımlanmış: %s" - -msgid "E1074: No white space allowed after dot" -msgstr "E1074: Nokta sonrası boşluğa izin verilmiyor" +msgid "E475: Unable to assign to empty list: %.*s" +msgstr "E475: Boş listeye atanamadı: %.*s" #, c-format -msgid "E1075: Namespace not supported: %s" -msgstr "E1075: Ad alanı desteklenmiyor: %s" - -msgid "E1076: This Vim is not compiled with float support" -msgstr "E1076: Bu Vim kayan noktalı değer desteği ile derlenmemiş" +msgid "E15: Unexpected closing figure brace: %.*s" +msgstr "E15: Beklenmedik kapatma kıvrımlı ayracı: %.*s" #, c-format -msgid "E1077: Missing argument type for %s" -msgstr "E1077: %s için argüman türü eksik" +msgid "E475: Nested lists not allowed when assigning: %.*s" +msgstr "E475: Atama sırasında iç içe geçmiş listelere izin verilmez: %.*s" #, c-format -msgid "E1081: Cannot unlet %s" -msgstr "E1081: %s sabitten değişkene çevrilemiyor" +msgid "E15: Expected value, got closing figure brace: %.*s" +msgstr "E15: Değer bekleniyordu, kapatma kıvrımlı ayracı alındı: %.*s" #, c-format -msgid "E1082: Cannot use a namespaced variable: %s" -msgstr "E1082: Ad alanına alınmış bir değişken kullanılamaz: %s" - -msgid "E1083: Missing backtick" -msgstr "E1083: Ters eğik kesme imi eksik" +msgid "E15: Don't know what figure brace means: %.*s" +msgstr "E15: Kıvrımlı ayracın ne anlama geldiği bilinmiyor: %.*s" #, c-format -msgid "E1084: Cannot delete Vim9 script function %s" -msgstr "E1084: Vim9 betik işlevi %s silinemiyor" +msgid "E15: Unexpected arrow: %.*s" +msgstr "E15: Beklenmedik ok: %.*s" #, c-format -msgid "E1085: Not a callable type: %s" -msgstr "E1085: Çağrılabilir bir tür değil: %s" - -msgid "E1086: Cannot use :function inside :def" -msgstr "E1086: :def içinde :function kullanılamaz" - -msgid "E1087: Cannot use an index when declaring a variable" -msgstr "E1087: Bir değişken tanımlarken dizinleme kullanılamaz" +msgid "E15: Arrow outside of lambda: %.*s" +msgstr "E15: Ok, lambda dışında: %.*s" #, c-format -msgid "E1089: Unknown variable: %s" -msgstr "E1089: Bilinmeyen değişken: %s" +msgid "E15: Unexpected dot: %.*s" +msgstr "E15: Beklenmedik nokta: %.*s" #, c-format -msgid "E1090: Cannot assign to argument %s" -msgstr "E1090: %s argümanına atanamıyor" +msgid "E15: Cannot concatenate in assignments: %.*s" +msgstr "E15: Atamalarda uç uca eklenemiyor: %.*s" #, c-format -msgid "E1091: Function is not compiled: %s" -msgstr "E1091: İşlev derlenmemiş: %s" - -msgid "E1092: Cannot use a list for a declaration" -msgstr "E1092: Tanımlama için bir liste kullanılamaz" +msgid "E15: Expected value, got parenthesis: %.*s" +msgstr "E15: Değer bekleniyordu, ayraç alındı: %.*s" #, c-format -msgid "E1093: Expected %d items but got %d" -msgstr "E1093: %d öge bekleniyordu, ancak %d alındı" - -msgid "E1094: Import can only be used in a script" -msgstr "E1094: İçe aktarım yalnızca bir betikte kullanılabilir" - -msgid "E1095: Unreachable code after :return" -msgstr "E1095: :return sonrası ulaşılamayan kod" - -msgid "E1096: Returning a value in a function without a return type" -msgstr "E1096: Dönüş türü olmayan bir işlevde bir değer döndürülüyor" - -msgid "E1097: Line incomplete" -msgstr "E1097: Satır tamamlanmamış" +msgid "E15: Unexpected closing parenthesis: %.*s" +msgstr "E15: Beklenmedik kapatma ayracı: %.*s" #, c-format -msgid "E1099: Unknown error while executing %s" -msgstr "E1099: %s çalıştırılırken bilinmeyen hata" +msgid "E15: Expected value, got question mark: %.*s" +msgstr "E15: Değer bekleniyordu, soru imi alındı: %.*s" #, c-format -msgid "E1101: Cannot declare a script variable in a function: %s" -msgstr "E1101: Bir işlevde bir betik değişkeni tanımlanamıyor: %s" +msgid "E114: Missing double quote: %.*s" +msgstr "E114: Çift tırnak eksik: %.*s" #, c-format -msgid "E1102: Lambda function not found: %s" -msgstr "E1102: Lambda işlevi bulunamadı: %s" - -msgid "E1103: Dictionary not set" -msgstr "E1103: Sözlük ayarlanmamış" - -msgid "E1104: Missing >" -msgstr "E1104: > eksik" +msgid "E115: Missing single quote: %.*s" +msgstr "E115: Tek tırnak eksik: %.*s" #, c-format -msgid "E1105: Cannot convert %s to string" -msgstr "E1105: %s bir diziye dönüştürülemiyor" - -msgid "E1106: One argument too many" -msgstr "E1106: Bir argüman fazladan" +msgid "E475: Expected closing bracket to end list assignment lvalue: %.*s" +msgstr "E475: lvalue liste sonu atamasına kapatma ayracı bekleniyordu: %.*s" #, c-format -msgid "E1106: %d arguments too many" -msgstr "E1106: %d argüman fazladan" - -msgid "E1107: String, List, Dict or Blob required" -msgstr "E1107: Dizi, Liste, Sözlük veya İkili Nesne gerekiyor" +msgid "E15: Misplaced assignment: %.*s" +msgstr "E15: Yanlış konumlandırılmış atama: %.*s" #, c-format -msgid "E1108: Item not found: %s" -msgstr "E1108: Öge bulunamadı: %s" +msgid "E15: Unexpected assignment: %.*s" +msgstr "E15: Beklenmedik atama: %.*s" #, c-format -msgid "E1109: List item %d is not a List" -msgstr "E1109: Liste ögesi %d bir Liste değil" +msgid "E15: Expected value, got EOC: %.*s" +msgstr "E15: Değer bekleniyordu, EOC alındı: %.*s" #, c-format -msgid "E1110: List item %d does not contain 3 numbers" -msgstr "E1110: Liste ögesi %d 3 sayı içermiyor" +msgid "E116: Missing closing parenthesis for function call: %.*s" +msgstr "E116: İşlev çağrısı için geçersiz kapatma ayracı: %.*s" #, c-format -msgid "E1111: List item %d range invalid" -msgstr "E1111: Liste ögesi %d erimi geçersiz" +msgid "E110: Missing closing parenthesis for nested expression: %.*s" +msgstr "E110: İç içe geçmiş ifade için kapatma ayracı eksik: %.*s" #, c-format -msgid "E1112: List item %d cell width invalid" -msgstr "E1112: Liste ögesi %d hücre genişliği geçersiz" +msgid "E697: Missing end of List ']': %.*s" +msgstr "E697: Liste sonunda ']' eksik: %.*s" #, c-format -msgid "E1113: Overlapping ranges for 0x%lx" -msgstr "E1113: 0x%lx için üst üste binen erimler" - -msgid "E1114: Only values of 0x100 and higher supported" -msgstr "E1114: Yalnızca 0x100 ve daha yüksek değerler destekleniyor" - -msgid "E1115: \"assert_fails()\" fourth argument must be a number" -msgstr "E1115: \"assert_fails()\" dördüncü argüman bir sayı olmalıdır" - -msgid "E1116: \"assert_fails()\" fifth argument must be a string" -msgstr "E1116: \"assert_fails()\" beşinci argüman bir dizi olmalıdır" - -msgid "E1117: Cannot use ! with nested :def" -msgstr "E1117: !, iç içe geçmiş :def ile kullanılamaz" - -msgid "E1118: Cannot change list" -msgstr "E1118: Liste değiştirilemez" - -msgid "E1119: Cannot change list item" -msgstr "E1119: Liste ögesi değiştirilemez" - -msgid "E1120: Cannot change dict" -msgstr "E1120: Sözlük değiştirilemez" - -msgid "E1121: Cannot change dict item" -msgstr "E1121: Sözlük ögesi değiştirilemez" +msgid "E723: Missing end of Dictionary '}': %.*s" +msgstr "E723: Sözlük sonu '}' eksik: %.*s" #, c-format -msgid "E1122: Variable is locked: %s" -msgstr "E1122: Değişken kilitli: %s" +msgid "E15: Missing closing figure brace: %.*s" +msgstr "E15: Kapatma kıvrımlı ayracı eksik: %.*s" #, c-format -msgid "E1123: Missing comma before argument: %s" -msgstr "E1123: Değişken öncesi virgül eksik: %s" +msgid "E15: Missing closing figure brace for lambda: %.*s" +msgstr "E15: Lambda için kapatma kıvrımlı ayracı eksik: %.*s" #, c-format -msgid "E1124: \"%s\" cannot be used in legacy Vim script" -msgstr "E1124: \"%s\" yalnızca eski Vim betiklerinde kullanılabilir" - -msgid "E1125: Final requires a value" -msgstr "E1125: Final, bir değer gerektirir" - -msgid "E1126: Cannot use :let in Vim9 script" -msgstr "E1126: :let, Vim9 betiğinde kullanılamaz" - -msgid "E1127: Missing name after dot" -msgstr "E1127: Nokta sonrası ad eksik" - -msgid "E1128: } without {" -msgstr "E1128: { olmadan }" - -msgid "E1129: Throw with empty string" -msgstr "E1129: Boş dizi ile \"Throw\"" - -msgid "E1130: Cannot add to null list" -msgstr "E1130: Null listesine bir öge eklenemez" - -msgid "E1131: Cannot add to null blob" -msgstr "E1131: Null ikili geniş nesnesine ekleme yapılamaz" - -msgid "E1132: Missing function argument" -msgstr "E1132: İşlev argümanı eksik" - -msgid "E1133: Cannot extend a null dict" -msgstr "E1133: Bir null sözlük genişletilemez" - -msgid "E1134: Cannot extend a null list" -msgstr "E1134: Bir null listesi genişletilemez" +msgid "E109: Missing ':' after '?': %.*s" +msgstr "E109: '?' sonrası ':' eksik: %.*s" -#, c-format -msgid "E1135: Using a String as a Bool: \"%s\"" -msgstr "E1135: Bir Dizi, bir Boole yerine kullanılıyor: \"%s\"" - -msgid "E1135: <Cmd> mapping must end with <CR>" -msgstr "E1135: <Cmd> eşlemlemesi <CR> ile bitmelidir" - -msgid "E1136: <Cmd> mapping must end with <CR> before second <Cmd>" -msgstr "E1136: <Cmd> eşlemlemesi ikinci <Cmd>'den önce <CR> ile bitmelidir" - -#, c-format -msgid "E1137: <Cmd> mapping must not include %s key" -msgstr "E1137: <Cmd> eşlemlemesi %s anahtarını içermemelidir" - -msgid "E1138: Using a Bool as a Number" -msgstr "E1138: Bir Boole, Sayı yerine kullanılıyor" - -msgid "E1139: Missing matching bracket after dict key" -msgstr "E1139: Sözlük anahtarı sonrası eşleşen ayraç eksik" - -msgid "E1140: :for argument must be a sequence of lists" -msgstr "E1140: :for argümanı listelerin bir sıralaması olmalıdır" - -msgid "E1141: Indexable type required" -msgstr "E1141: İndekslenebilir tür gerekiyor" - -msgid "E1142: Non-empty string required" -msgstr "E1142: Boş olmayan dizi gerekiyor" - -#, c-format -msgid "E1143: Empty expression: \"%s\"" -msgstr "E1143: Boş ifade: \"%s\"" - -#, c-format -msgid "E1144: Command \"%s\" is not followed by white space: %s" -msgstr "E1144: \"%s\" komutu sonrasında boşluk gelmiyor: %s" - -#, c-format -msgid "E1145: Missing heredoc end marker: %s" -msgstr "E1145: Son imleyicisi eksik: %s" - -#, c-format -msgid "E1146: Command not recognized: %s" -msgstr "E1146: Komut tanınamadı: %s" - -msgid "E1147: List not set" -msgstr "E1147: Liste ayarlanmamış" - -#, c-format -msgid "E1148: Cannot index a %s" -msgstr "E1148: Bir %s dizinlenemiyor" - -#, c-format -msgid "E1149: Script variable is invalid after reload in function %s" -msgstr "E1149: %s işlevindeki yeniden yüklemeden sonra betik değişkeni geçersiz" - -msgid "E1150: Script variable type changed" -msgstr "E1150: Betik değişkeni türü değiştirildi" - -msgid "E1151: Mismatched endfunction" -msgstr "E1151: Eşleşmeyen endfunction" - -msgid "E1152: Mismatched enddef" -msgstr "E1152: Eşleşmeyen enddef" - -msgid "E1153: Invalid operation for bool" -msgstr "E1153: Boole için geçersiz işlem" - -msgid "E1154: Divide by zero" -msgstr "E1154: Sıfır ile bölüm" - -msgid "E1155: Cannot define autocommands for ALL events" -msgstr "E1155: Otokomutlar TÜM olaylar için tanımlanamıyor" - -msgid "E1156: Cannot change the argument list recursively" -msgstr "E1156: Değişken listesi özyineli olarak değiştirilemiyor" - -msgid "E1157: Missing return type" -msgstr "E1157: Dönüş türü eksik" - -msgid "E1158: Cannot use flatten() in Vim9 script" -msgstr "E1158: flatten(), Vim9 betiğinde kullanılamaz" - -msgid "E1159: Cannot split a window when closing the buffer" -msgstr "E1159: Arabellek kapatılırken bir pencere bölünemez" - -msgid "E1160: Cannot use a default for variable arguments" -msgstr "E1160: Değişken argümanları için bir öntanımlı kullanılamaz" - -#, c-format -msgid "E1161: Cannot json encode a %s" -msgstr "E1161: Bir %s JSON olarak kodlanamıyor" - -#, c-format -msgid "E1162: Register name must be one character: %s" -msgstr "E1162: Yazmaç adı tek bir karakter olmalıdır: %s" - -#, c-format -msgid "E1163: Variable %d: type mismatch, expected %s but got %s" -msgstr "E1163: %d değişkeni: Tür uyumsuzluğu, %s bekleniyordu, ancak %s alındı" - -msgid "E1164: vim9cmd must be followed by a command" -msgstr "E1164: vim9cmd sonrasında bir komut gelmelidir" - -#, c-format -msgid "E1165: Cannot use a range with an assignment: %s" -msgstr "E1165: Bir atama ile bir erim kullanılamıyor: %s" - -msgid "E1166: Cannot use a range with a dictionary" -msgstr "E1166: Bir sözlük ile bir erim kullanılamıyor" - -#, c-format -msgid "E1167: Argument name shadows existing variable: %s" -msgstr "E1167: Argüman adı var olan değişkeni gölgeliyor: %s" - -#, c-format -msgid "E1168: Argument already declared in the script: %s" -msgstr "E1168: Betikte argüman halihazırda tanımlanmış: %s" - -msgid "E1169: 'import * as {name}' not supported here" -msgstr "E1169: 'import * as {name} burada desteklenmiyor" - -msgid "E1170: Cannot use #{ to start a comment" -msgstr "E1170: Bir yorum başlatmak için #{ kullanılamaz" - -msgid "E1171: Missing } after inline function" -msgstr "E1171: Satıriçi işlevden sonra } eksik" - -msgid "E1172: Cannot use default values in a lambda" -msgstr "E1172: Bir lambda içerisinde öntanımlı değerler kullanılamıyor" - -#, c-format -msgid "E1173: Text found after enddef: %s" -msgstr "E1173: :enddef sonrası metin bulundu: %s" - -#, c-format -msgid "E1174: String required for argument %d" -msgstr "E1174: %d argümanı için dizi gerekiyor" - -#, c-format -msgid "E1175: Non-empty string required for argument %d" -msgstr "E1175: %d argümanı için boş olmayan dizi gerekiyor" - -msgid "E1176: Misplaced command modifier" -msgstr "E1176: Yanlış yere konulmuş komut değiştiricisi" - -#, c-format -msgid "E1177: For loop on %s not supported" -msgstr "E1177: %s üzerinde for döngüsü desteklenmiyor" - -msgid "E1178: Cannot lock or unlock a local variable" -msgstr "E1178: Bir yerel değişken kilitlenemiyor/kilidi açılamıyor" - -#, c-format -msgid "" -"E1179: Failed to extract PWD from %s, check your shell's config related to " -"OSC 7" -msgstr "" -"E1179: %s içinden PWD çıkarılamadı, kabuğunuzun OSC 7 ile ilgili " -"yapılandırmasını denetleyin" - -#, c-format -msgid "E1180: Variable arguments type must be a list: %s" -msgstr "E1180: Değişken argümanları türü bir liste olmalıdır: %s" - -msgid "E1181: Cannot use an underscore here" -msgstr "E1181: Alt çizgi burada kullanılamaz" - -msgid "E1182: Blob required" -msgstr "E1182: İkili geniş nesne gerekiyor" - -#, c-format -msgid "E1183: Cannot use a range with an assignment operator: %s" -msgstr "E1183: Bir atama işleci ile bir erim kullanılamıyor: %s" - -msgid "E1184: Blob not set" -msgstr "E1184: İkili geniş nesne ayarlanmamış" - -msgid "E1185: Cannot nest :redir" -msgstr "E1185: :redir içe geçirilemiyor" - -msgid "E1185: Missing :redir END" -msgstr "E1185: :redir END eksik" - -#, c-format -msgid "E1186: Expression does not result in a value: %s" -msgstr "E1186: İfade bir değer sonucu vermiyor: %s" - -msgid "E1187: Failed to source defaults.vim" -msgstr "E1187: defaults.vim kaynaklanamadı" - -msgid "E1188: Cannot open a terminal from the command line window" -msgstr "E1188: Komut satırı penceresinden bir uçbirim açılamıyor" - -#, c-format -msgid "E1189: Cannot use :legacy with this command: %s" -msgstr "E1189: :legacy, bu komut ile kullanılamıyor: %s" - -msgid "E1190: One argument too few" -msgstr "E1190: Bir argüman daha gerekiyor" - -#, c-format -msgid "E1190: %d arguments too few" -msgstr "E1190: %d argüman daha gerekiyor" - -#, c-format -msgid "E1191: Call to function that failed to compile: %s" -msgstr "E1191: Derlenemeyen işlev çağrısı: %s" - -msgid "E1192: Empty function name" -msgstr "E1192: Boş işlev adı" - -msgid "E1193: cryptmethod xchacha20 not built into this Vim" -msgstr "E1193: cryptmethod xchacha20 bu Vim ile kullanılamıyor" - -msgid "E1194: Cannot encrypt header, not enough space" -msgstr "E1194: Üstbilgi şifrelenemiyor, yetersiz alan" - -msgid "E1195: Cannot encrypt buffer, not enough space" -msgstr "E1195: Arabellek şifrelenemiyor, yetersiz alan" - -msgid "E1196: Cannot decrypt header, not enough space" -msgstr "E1196: Üstbilgi şifresi çözülemiyor, yetersiz alan" - -msgid "E1197: Cannot allocate_buffer for encryption" -msgstr "E1197: Şifreleme için allocate_buffer yapılamıyor" - -msgid "E1198: Decryption failed: Header incomplete!" -msgstr "E1198: Şifre çözümü başarısız: Üstbilgi tam değil!" - -msgid "E1199: Cannot decrypt buffer, not enough space" -msgstr "E1199: Arabellek şifresi çözülemiyor, yetersiz alan" - -msgid "E1200: Decryption failed!" -msgstr "E1200: Şifre çözümü başarısız!" - -msgid "E1201: Decryption failed: pre-mature end of file!" -msgstr "E1201: Şifre çözümü başarısız: Beklenmedik dosya sonu!" - -#, c-format -msgid "E1202: No white space allowed after '%s': %s" -msgstr "E1202: '%s' sonrası boşluğa izin verilmiyor: %s" - -#, c-format -msgid "E1203: Dot can only be used on a dictionary: %s" -msgstr "E1203: Nokta yalnızca bir sözlükte kullanılabilir: %s" - -#, c-format -msgid "E1204: No Number allowed after .: '\\%%%c'" -msgstr "E1204: . sonrası Sayıya izin verilmiyor: '\\%%%c'" - -msgid "E1205: No white space allowed between option and" -msgstr "E1205: and seçeneği arasında boşluğa izin verilmiyor" - -#, c-format -msgid "E1206: Dictionary required for argument %d" -msgstr "E1206: %d argümanı için sözlük gerekiyor" - -#, c-format -msgid "E1207: Expression without an effect: %s" -msgstr "E1207: Bir efekt olmadan ifade: %s" - -msgid "E1208: -complete used without -nargs" -msgstr "E1208: -complete, -nargs olmadan kullanıldı" - -#, c-format -msgid "E1209: Invalid value for a line number: \"%s\"" -msgstr "E1209: Satır numarası için geçersiz değer: \"%s\"" - -#, c-format -msgid "E1210: Number required for argument %d" -msgstr "E1210: %d argümanı için sayı gerekiyor" - -msgid "--No lines in buffer--" -msgstr "--Arabellek içinde satır yok--" - -msgid "E470: Command aborted" -msgstr "E470: Komut durduruldu" - -msgid "E471: Argument required" -msgstr "E471: Değişken gerekiyor" - -msgid "E171: Missing :endif" -msgstr "E171: :endif eksik" - -msgid "E603: :catch without :try" -msgstr "E603: :try olmadan :catch" - -msgid "E606: :finally without :try" -msgstr "E606: :try olmadan :finally" - -msgid "E607: multiple :finally" -msgstr "E607: Birden fazla :finally" - -msgid "E600: Missing :endtry" -msgstr "E600: :endtry eksik" - -msgid "E602: :endtry without :try" -msgstr "E602: :try olmadan :endtry" - -msgid "E170: Missing :endwhile" -msgstr "E170: :endwhile eksik" - -msgid "E170: Missing :endfor" -msgstr "E170: :endfor eksik" - -msgid "E588: :endwhile without :while" -msgstr "E588: :while olmadan :endwhile" - -msgid "E588: :endfor without :for" -msgstr "E588: :for olmadan :endfor" - -msgid "E472: Command failed" -msgstr "E472: Komut başarısız oldu" - -#, c-format -msgid "E234: Unknown fontset: %s" -msgstr "E234: Bilinmeyen yazıtipi seti: %s" - -#, c-format -msgid "E235: Unknown font: %s" -msgstr "E235: Bilinmeyen yazıtipi: %s" - -#, c-format -msgid "E236: Font \"%s\" is not fixed-width" -msgstr "E236: \"%s\" yazıtipi sabit genişlikli değil" - -msgid "E473: Internal error" -msgstr "E473: İç hata" - -#, c-format -msgid "E685: Internal error: %s" -msgstr "E685: İç hata: %s" - -msgid "Interrupted" -msgstr "Yarıda kesildi" - -msgid "E474: Invalid argument" -msgstr "E474: Geçersiz argüman" - -#, c-format -msgid "E475: Invalid argument: %s" -msgstr "E475: Geçersiz argüman: %s" - -#, c-format -msgid "E983: Duplicate argument: %s" -msgstr "E983: Yinelenen argüman: %s" - -#, c-format -msgid "E475: Invalid value for argument %s" -msgstr "E475: %s argümanı için geçersiz değer" - -#, c-format -msgid "E475: Invalid value for argument %s: %s" -msgstr "E475: %s argümanı için geçersiz değer: %s" - -msgid "E756: Spell checking is not possible" -msgstr "E756: Yazım denetimi olanaklı değil" - -#, c-format -msgid "E364: Library call failed for \"%s()\"" -msgstr "E364: \"%s()\" için kitaplık çağrısı başarısız oldu" - -msgid "E667: Fsync failed" -msgstr "E667: Fsync başarısız oldu" - -#, c-format -msgid "E448: Could not load library function %s" -msgstr "E448: %s kitaplık işlevi yüklenemedi" - -msgid "E477: No ! allowed" -msgstr "E477: ! imine izin verilmiyor" - -msgid "E25: GUI cannot be used: Not enabled at compile time" -msgstr "E25: Grafik arabirim kullanılamaz: Derlenirken etkinleştirilmemiş" - -msgid "E26: Hebrew cannot be used: Not enabled at compile time\n" -msgstr "E26: İbranca kullanılamaz: Derlenirken etkinleştirilmemiş\n" - -msgid "E27: Farsi support has been removed\n" -msgstr "E27: Farsça desteği kaldırıldı\n" - -msgid "E800: Arabic cannot be used: Not enabled at compile time\n" -msgstr "E800: Arapça kullanılamaz: Derlenirken etkinleştirilmemiş\n" - -#, c-format -msgid "E28: No such highlight group name: %s" -msgstr "E28: Böyle bir vurgulama grup adı yok: %s" - -msgid "E29: No inserted text yet" -msgstr "E29: Henüz bir metin eklenmedi" - -msgid "E30: No previous command line" -msgstr "E30: Öncesinde komut satırı yok" - -msgid "E31: No such mapping" -msgstr "E31: Böyle bir eşlem yok" - -msgid "E479: No match" -msgstr "E479: Eşleşme yok" - -#, c-format -msgid "E480: No match: %s" -msgstr "E480: Eşleşme yok: %s" - -msgid "E32: No file name" -msgstr "E32: Dosya adı yok" - -msgid "E33: No previous substitute regular expression" -msgstr "E33: Öncesinde yerine geçen bir düzenli ifade yok" - -msgid "E34: No previous command" -msgstr "E34: Öncesinde komut yok" - -msgid "E35: No previous regular expression" -msgstr "E35: Öncesinde düzenli ifade yok" - -msgid "E481: No range allowed" -msgstr "E481: Erime izin verilmiyor" - -msgid "E36: Not enough room" -msgstr "E36: Yeterli alan yok" - -#, c-format -msgid "E247: no registered server named \"%s\"" -msgstr "E247: \"%s\" adlı kayıtlı bir sunucu yok" - -#, c-format -msgid "E482: Can't create file %s" -msgstr "E482: %s dosyası oluşturulamıyor" - -msgid "E483: Can't get temp file name" -msgstr "E483: Geçici dosya adı alınamıyor" - -#, c-format -msgid "E484: Can't open file %s" -msgstr "E484: %s dosyası açılamıyor" - -#, c-format -msgid "E485: Can't read file %s" -msgstr "E485: %s dosyası okunamıyor" - -msgid "E38: Null argument" -msgstr "E38: Anlamsız argüman" - -msgid "E39: Number expected" -msgstr "E39: Sayı bekleniyordu" - -#, c-format -msgid "E40: Can't open errorfile %s" -msgstr "E40: Hata dosyası %s açılamıyor" - -msgid "E233: cannot open display" -msgstr "E233: Görüntü açılamıyor" - -msgid "E41: Out of memory!" -msgstr "E41: Bellek yetersiz!" - -msgid "Pattern not found" -msgstr "Dizgi bulunamadı" - -#, c-format -msgid "E486: Pattern not found: %s" -msgstr "E486: Dizgi bulunamadı: %s" - -msgid "E487: Argument must be positive" -msgstr "E487: Değişken pozitif olmalı" - -msgid "E459: Cannot go back to previous directory" -msgstr "E459: Bir önceki dizine gidilemiyor" - -msgid "E42: No Errors" -msgstr "E42: Hata yok" - -msgid "E776: No location list" -msgstr "E776: Konum listesi yok" - -msgid "E43: Damaged match string" -msgstr "E43: Hasarlı eşleşme dizisi" - -msgid "E44: Corrupted regexp program" -msgstr "E44: Bozulmuş regexp programı" - -msgid "E45: 'readonly' option is set (add ! to override)" -msgstr "E45: 'readonly' seçeneği ayarlanmış (geçersiz kılmak için ! ekleyin)" - -#, c-format -msgid "E734: Wrong variable type for %s=" -msgstr "E734: %s= için yanlış değişken türü" - -#, c-format -msgid "E461: Illegal variable name: %s" -msgstr "E461: İzin verilmeyen değişken adı: %s" - -msgid "E995: Cannot modify existing variable" -msgstr "E995: Mevcut değişken değiştirilemiyor" - -#, c-format -msgid "E46: Cannot change read-only variable \"%s\"" -msgstr "E46: Salt okunur değişken \"%s\" değiştirilemiyor" - -#, c-format -msgid "E794: Cannot set variable in the sandbox: \"%s\"" -msgstr "E794: Değişken kum havuzunda ayarlanamıyor: \"%s\"" - -msgid "E928: String required" -msgstr "E928: Dizi gerekiyor" - -msgid "E889: Number required" -msgstr "E889: Sayı gerekiyor" - -msgid "E713: Cannot use empty key for Dictionary" -msgstr "E713: Sözlük için boş anahtar kullanılamaz" - -msgid "E715: Dictionary required" -msgstr "E715: Sözlük gerekiyor" - -#, c-format -msgid "E684: list index out of range: %ld" -msgstr "E684: Liste sırası erimin dışında: %ld" - -#, c-format -msgid "E979: Blob index out of range: %ld" -msgstr "E979: İkili geniş nesne sırası erimin dışında: %ld" - -msgid "E978: Invalid operation for Blob" -msgstr "E978: İkili geniş nesne için geçersiz işlem" - -#, c-format -msgid "E118: Too many arguments for function: %s" -msgstr "E118: İşlev için çok fazla argüman: %s" - -#, c-format -msgid "E119: Not enough arguments for function: %s" -msgstr "E119: Şu işlev için yetersiz sayıda argüman: %s" - -#, c-format -msgid "E933: Function was deleted: %s" -msgstr "E933: İşlev silinmiş: %s" - -#, c-format -msgid "E716: Key not present in Dictionary: \"%s\"" -msgstr "E716: Anahtar sözlükte mevcut değil: \"%s\"" - -msgid "E714: List required" -msgstr "E714: Liste gerekiyor" - -msgid "E897: List or Blob required" -msgstr "E897: Liste veya ikili geniş nesne gerekiyor" - -#, c-format -msgid "E697: Missing end of List ']': %s" -msgstr "E697: Liste sonunda ']' eksik: %s" - -#, c-format -msgid "E712: Argument of %s must be a List or Dictionary" -msgstr "E712: %s ögesinin argümanı bir liste veya sözlük olmalıdır" - -#, c-format -msgid "E896: Argument of %s must be a List, Dictionary or Blob" -msgstr "E896: %s argümanı bir liste, sözlük veya ikili geniş nesne olmalıdır" - -msgid "E804: Cannot use '%' with Float" -msgstr "E804: Bir kayan noktalı değer ile '%' kullanılamaz" - -msgid "E996: Cannot lock an option" -msgstr "E996: Seçenek kilitlenemiyor" - -#, c-format -msgid "E113: Unknown option: %s" -msgstr "E113: Bilinmeyen seçenek: %s" - -#, c-format -msgid "E998: Reduce of an empty %s with no initial value" -msgstr "E998: Başlangıç değeri olmayan boş bir %s için reduce() yapılamıyor" - -#, c-format -msgid "E857: Dictionary key \"%s\" required" -msgstr "E857: Sözlük anahtarı \"%s\" gerekiyor" - -msgid "E47: Error while reading errorfile" -msgstr "E47: Hata dosyası okunurken hata" - -msgid "E48: Not allowed in sandbox" -msgstr "E48: Kum havuzunda izin verilmiyor" - -msgid "E523: Not allowed here" -msgstr "E523: Burada izin verilmiyor" - -msgid "E578: Not allowed to change text here" -msgstr "E578: Burada metin değişikliğine izin verilmiyor" - -msgid "E565: Not allowed to change text or change window" -msgstr "E565: Pencere veya metin değişikliğine izin verilmiyor" - -msgid "E359: Screen mode setting not supported" -msgstr "E359: Ekran kipi ayarı desteklenmiyor" - -msgid "E49: Invalid scroll size" -msgstr "E49: Geçersiz kaydırma boyutu" - -msgid "E91: 'shell' option is empty" -msgstr "E91: 'shell' seçeneği boş" - -msgid "E255: Couldn't read in sign data!" -msgstr "E255: İşaret verisi okunamadı!" - -msgid "E72: Close error on swap file" -msgstr "E72: Takas dosyasında kapama hatası" - -msgid "E73: tag stack empty" -msgstr "E73: Etiket yığını boş" - -msgid "E74: Command too complex" -msgstr "E74: Komut çok karmaşık" - -msgid "E75: Name too long" -msgstr "E75: Ad çok uzun" - -msgid "E76: Too many [" -msgstr "E76: Çok fazla [" - -msgid "E77: Too many file names" -msgstr "E77: Çok fazla dosya adı" - -msgid "E488: Trailing characters" -msgstr "E488: Sonda fazladan karakterler" - -#, c-format -msgid "E488: Trailing characters: %s" -msgstr "E488: Sonda fazladan karakterler: %s" - -msgid "E78: Unknown mark" -msgstr "E78: Bilinmeyen im" - -msgid "E79: Cannot expand wildcards" -msgstr "E79: Joker karakterleri genişletilemiyor" - -msgid "E591: 'winheight' cannot be smaller than 'winminheight'" -msgstr "E591: 'winheight' değeri 'winminheight' değerinden küçük olamaz" - -msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" -msgstr "E592: 'winwidth' değeri 'winminwidth' değerinden küçük olamaz" - -msgid "E80: Error while writing" -msgstr "E80: Yazma sırasında hata" - -msgid "E939: Positive count required" -msgstr "E939: Pozitif sayım gerekiyor" - -msgid "E81: Using <SID> not in a script context" -msgstr "E81: <SID> bir betik bağlamında kullanılmıyor" - -#, c-format -msgid "E107: Missing parentheses: %s" -msgstr "E107: Ayraç eksik: %s" - -msgid "E110: Missing ')'" -msgstr "E110: ')' eksik" - -#, c-format -msgid "E720: Missing colon in Dictionary: %s" -msgstr "E720: Sözlükte iki nokta eksik: %s" - -#, c-format -msgid "E721: Duplicate key in Dictionary: \"%s\"" -msgstr "E721: Sözlükte yinelenmiş anahtar: \"%s\"" - -#, c-format -msgid "E722: Missing comma in Dictionary: %s" -msgstr "E722: Sözlükte virgül eksik: %s" - -#, c-format -msgid "E723: Missing end of Dictionary '}': %s" -msgstr "E723: Sözlük sonu '}' eksik: %s" - -msgid "E449: Invalid expression received" -msgstr "E449: Geçersiz ifade alındı" - -msgid "E463: Region is guarded, cannot modify" -msgstr "E463: Bölge korunuyor, değiştirilemez" - -msgid "E744: NetBeans does not allow changes in read-only files" -msgstr "E744: NetBeans salt okunur dosyalarda değişikliklere izin vermiyor" - -msgid "E363: pattern uses more memory than 'maxmempattern'" -msgstr "E363: Dizgi 'maxmempattern' ögesinden daha fazla bellek kullanıyor" - -msgid "E749: empty buffer" -msgstr "E749: Boş arabellek" - -#, c-format -msgid "E86: Buffer %ld does not exist" -msgstr "E86: Arabellek %ld mevcut değil" - -msgid "E682: Invalid search pattern or delimiter" -msgstr "E682: Geçersiz arama dizgisi veya sınırlandırıcısı" - -msgid "E139: File is loaded in another buffer" -msgstr "E139: Dosya başka bir arabellekte yüklü" - -#, c-format -msgid "E764: Option '%s' is not set" -msgstr "E764: '%s' seçeneği ayarlanmamış" - -msgid "E850: Invalid register name" -msgstr "E850: Geçersiz yazmaç adı" - -msgid "E806: using Float as a String" -msgstr "E806: Kayan Noktalı Değer, bir Dizi yerine kullanılıyor" - -#, c-format -msgid "E919: Directory not found in '%s': \"%s\"" -msgstr "E919: '%s' içinde dizin bulunamadı: \"%s\"" - -msgid "E952: Autocommand caused recursive behavior" -msgstr "E952: Otokomut özyineli davranışa neden oldu" - -msgid "E813: Cannot close autocmd or popup window" -msgstr "E813: Otokomut veya açılır pencere kapatılamıyor" - -msgid "E328: Menu only exists in another mode" -msgstr "E328: Menü yalnızca başka bir kipte mevcut" - -msgid "E957: Invalid window number" -msgstr "E957: Geçersiz pencere numarası" - -#, c-format -msgid "E686: Argument of %s must be a List" -msgstr "E686: %s argümanı bir liste olmalı" - -msgid "E109: Missing ':' after '?'" -msgstr "E109: '?' sonrası ':' eksik" - -msgid "E690: Missing \"in\" after :for" -msgstr "E690: :for sonrası \"in\" eksik" - -#, c-format -msgid "E117: Unknown function: %s" -msgstr "E117: Bilinmeyen işlev: %s" - -msgid "E111: Missing ']'" -msgstr "E111: ']' eksik" - -msgid "E581: :else without :if" -msgstr "E581: :if olmadan :else" - -msgid "E582: :elseif without :if" -msgstr "E582: :if olmadan :elseif" - -msgid "E580: :endif without :if" -msgstr "E580: :if olmadan :endif" - -msgid "E586: :continue without :while or :for" -msgstr "E586: :while veya :for olmadan :continue" - -msgid "E587: :break without :while or :for" -msgstr "E587: :while veya :for olmadan :break" - -msgid "E274: No white space allowed before parenthesis" -msgstr "E274: Ayraçtan önce boşluğa izin verilmiyor" - -#, c-format -msgid "E940: Cannot lock or unlock variable %s" -msgstr "E940: Değişken %s kilitlenemiyor veya açılamıyor" - -#, c-format -msgid "E254: Cannot allocate color %s" -msgstr "E254: %s rengi ayrılamıyor" - -msgid "search hit TOP, continuing at BOTTOM" -msgstr "Arama dosyanın BAŞINI geçti, dosyanın SONUNDAN sürüyor" - -msgid "search hit BOTTOM, continuing at TOP" -msgstr "Arama dosyanın SONUNU geçti, dosyanın BAŞINDAN sürüyor" - -msgid " line " -msgstr " satır " - -#, c-format -msgid "Need encryption key for \"%s\"" -msgstr "\"%s\" için şifreleme anahtarı gerekli" - -msgid "empty keys are not allowed" -msgstr "boş anahtarlara izin verilmiyor" - -msgid "dictionary is locked" -msgstr "sözlük kilitli" - -msgid "list is locked" -msgstr "liste kilitli" - -#, c-format -msgid "failed to add key '%s' to dictionary" -msgstr "'%s' anahtarı sözlüğe eklenemedi" - -#, c-format -msgid "index must be int or slice, not %s" -msgstr "sıra bir tamsayı veya dilim olmalıdır, %s olamaz" - -#, c-format -msgid "expected str() or unicode() instance, but got %s" -msgstr "str() veya unicode() örneği bekleniyordu, %s geldi" - -#, c-format -msgid "expected bytes() or str() instance, but got %s" -msgstr "bytes() veya str() örneği bekleniyordu, %s geldi" - -#, c-format -msgid "" -"expected int(), long() or something supporting coercing to long(), but got %s" -msgstr "" -"int(), long() veya long()'a baskıyı destekleyen bir şey bekleniyordu, %s " -"geldi" - -#, c-format -msgid "expected int() or something supporting coercing to int(), but got %s" -msgstr "int() veya int()'e baskıyı destekleyen bir şey bekleniyordu, %s geldi" - -msgid "value is too large to fit into C int type" -msgstr "değer C tamsayı türüne sığmak için çok büyük" - -msgid "value is too small to fit into C int type" -msgstr "değer C tamsayı türüne sığmak için çok küçük" - -msgid "number must be greater than zero" -msgstr "sayı sıfırdan büyük olmalı" - -msgid "number must be greater or equal to zero" -msgstr "sayı sıfıra eşit veya sıfırdan büyük olmalı" - -msgid "can't delete OutputObject attributes" -msgstr "OutputObject öznitelikleri silinemiyor" - -#, c-format -msgid "invalid attribute: %s" -msgstr "geçersiz öznitelik: %s" - -msgid "E264: Python: Error initialising I/O objects" -msgstr "E264: Python: Girdi/Çıktı nesneleri başlatılırken hata" - -msgid "failed to change directory" -msgstr "dizin değiştirilemedi" - -#, c-format -msgid "expected 3-tuple as imp.find_module() result, but got %s" -msgstr "imp.find_module() sonucu olarak 3 çoklu öge bekleniyordu, %s geldi" - -#, c-format -msgid "expected 3-tuple as imp.find_module() result, but got tuple of size %d" -msgstr "" -"imp.find_module() sonucu olarak 3 tuple bekleniyordu, %d boyutlu çok öge " -"geldi" - -msgid "internal error: imp.find_module returned tuple with NULL" -msgstr "iç hata: imp.find_module BOŞ bir çoklu öge döndürdü" - -msgid "cannot delete vim.Dictionary attributes" -msgstr "vim.Dictionary öznitelikleri silinemiyor" - -msgid "cannot modify fixed dictionary" -msgstr "sabit sözlük değiştirilemiyor" - -#, c-format -msgid "cannot set attribute %s" -msgstr "%s özniteliği ayarlanamıyor" - -msgid "hashtab changed during iteration" -msgstr "Sağlama tablosu dürüm sırasında değişti" - -#, c-format -msgid "expected sequence element of size 2, but got sequence of size %d" -msgstr "2 boyut bir sıralama bekleniyordu, ancak %d boyut bir sıralama geldi" - -msgid "list constructor does not accept keyword arguments" -msgstr "liste yapıcısı anahtar sözcük argümanları kabul etmez" - -msgid "list index out of range" -msgstr "liste sırası erimin dışında" - -#, c-format -msgid "internal error: failed to get Vim list item %d" -msgstr "iç hata: %d vim liste ögesi alınamadı" - -msgid "slice step cannot be zero" -msgstr "dilim adımı sıfır olamaz" - -#, c-format -msgid "attempt to assign sequence of size greater than %d to extended slice" -msgstr "genişletilmiş dilime %d boyuttan büyük bir sıralamayı atama denemesi" - -#, c-format -msgid "internal error: no Vim list item %d" -msgstr "iç hata: %d vim liste ögesi yok" - -msgid "internal error: not enough list items" -msgstr "iç hata: yeterli liste ögesi yok" - -msgid "internal error: failed to add item to list" -msgstr "iç hata: öge listeye eklenemedi" - -#, c-format -msgid "attempt to assign sequence of size %d to extended slice of size %d" -msgstr "%d boyut sıralamayı %d boyut genişletilmiş dizine atama denemesi" - -msgid "failed to add item to list" -msgstr "öge listeye eklenemedi" - -msgid "cannot delete vim.List attributes" -msgstr "vim.List öznitelikleri silinemiyor" - -msgid "cannot modify fixed list" -msgstr "sabit liste değiştirilemiyor" - -#, c-format -msgid "unnamed function %s does not exist" -msgstr "adsız %s işlevi mevcut değil" - -#, c-format -msgid "function %s does not exist" -msgstr "%s işlevi mevcut değil" - -#, c-format -msgid "failed to run function %s" -msgstr "%s işlevi çalıştırılamadı" - -msgid "unable to get option value" -msgstr "seçenek değeri alınamadı" - -msgid "internal error: unknown option type" -msgstr "iç hata: bilinmeyen seçenek türü" - -msgid "problem while switching windows" -msgstr "pencereler arasında gezinirken hata" - -#, c-format -msgid "unable to unset global option %s" -msgstr "%s global seçenek ayarı kapatılamıyor" - -#, c-format -msgid "unable to unset option %s which does not have global value" -msgstr "global değeri olmayan %s seçenek ayarı kapatılamıyor" - -msgid "attempt to refer to deleted tab page" -msgstr "silinmiş sekme sayfasına başvurma denemesi" - -msgid "no such tab page" -msgstr "böyle bir sekme sayfası yok" - -msgid "attempt to refer to deleted window" -msgstr "silinmiş pencereye başvurma denemesi" - -msgid "readonly attribute: buffer" -msgstr "saltokunur öznitelik: arabellek" - -msgid "cursor position outside buffer" -msgstr "imleç konumu arabelleğin dışında" - -msgid "no such window" -msgstr "böyle bir pencere yok" - -msgid "attempt to refer to deleted buffer" -msgstr "silinmiş arabelleğe başvurma denemesi" - -msgid "failed to rename buffer" -msgstr "arabellek adı değiştirilemedi" - -msgid "mark name must be a single character" -msgstr "im adı tek bir karakterden olmalıdır" - -#, c-format -msgid "expected vim.Buffer object, but got %s" -msgstr "vim.Buffer nesnesi bekleniyordu, %s geldi" - -#, c-format -msgid "failed to switch to buffer %d" -msgstr "%d arabelleğine geçilemedi" - -#, c-format -msgid "expected vim.Window object, but got %s" -msgstr "vim.Window nesnesi bekleniyordu, %s geldi" - -msgid "failed to find window in the current tab page" -msgstr "mevcut sekme sayfasında pencere bulunamadı" - -msgid "did not switch to the specified window" -msgstr "belirtilen pencereye geçilemedi" - -#, c-format -msgid "expected vim.TabPage object, but got %s" -msgstr "vim.TabPage nesnesi bekleniyordu, %s geldi" - -msgid "did not switch to the specified tab page" -msgstr "belirtilen sekme sayfasına geçilemedi" - -msgid "failed to run the code" -msgstr "kod çalıştırılamadı" - -msgid "E858: Eval did not return a valid python object" -msgstr "E858: Eval geçerli bir python nesnesi döndürmedi" - -msgid "E859: Failed to convert returned python object to a Vim value" -msgstr "E859: Döndürülen python nesnesi vim değerine dönüştürülemedi" - -#, c-format -msgid "unable to convert %s to a Vim dictionary" -msgstr "%s vim sözlüğüne dönüştürülemedi" - -#, c-format -msgid "unable to convert %s to a Vim list" -msgstr "%s vim listesine dönüştürülemedi" - -#, c-format -msgid "unable to convert %s to a Vim structure" -msgstr "%s vim yapısına dönüştürülemedi" - -msgid "internal error: NULL reference passed" -msgstr "iç hata: BOŞ başvuru geçirildi" - -msgid "internal error: invalid value type" -msgstr "iç hata: geçersiz değer türü" - -msgid "" -"Failed to set path hook: sys.path_hooks is not a list\n" -"You should now do the following:\n" -"- append vim.path_hook to sys.path_hooks\n" -"- append vim.VIM_SPECIAL_PATH to sys.path\n" -msgstr "" -"Yol kancası ayarlanamadı: sys.path_hooks bir liste değil\n" -"Şimdi şunları yapmanız gerekiyor:\n" -"- vim.path_hook'u sys.path_hooks'a iliştirmek\n" -"- vim.VIM_SPECIAL_PATH'i sys.path'e iliştirmek\n" - -msgid "" -"Failed to set path: sys.path is not a list\n" -"You should now append vim.VIM_SPECIAL_PATH to sys.path" -msgstr "" -"Yol ayarlanamadı: sys.path bir liste değil\n" -"Şimdi vim.VIM_SPECIAL_PATH'i sys.path'e iliştirmelisiniz" - -msgid "" -"Vim macro files (*.vim)\t*.vim\n" -"All Files (*.*)\t*.*\n" -msgstr "" -"Vim makro dosyaları (*.vim)\t*.vim\n" -"Tüm Dosyalar (*.*)\t*.*\n" - -msgid "All Files (*.*)\t*.*\n" -msgstr "Tüm Dosyalar (*.*)\t*.*\n" - -msgid "" -"All Files (*.*)\t*.*\n" -"C source (*.c, *.h)\t*.c;*.h\n" -"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"VB code (*.bas, *.frm)\t*.bas;*.frm\n" -"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" -msgstr "" -"Tüm Dosyalar (*.*)\t*.*\n" -"C kaynak dosyaları (*.c, *.h)\t*.c;*.h\n" -"C++ kaynak dosyaları (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"VB kodu (*.bas, *.frm)\t*.bas;*.frm\n" -"Vim dosyaları (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" - -msgid "" -"Vim macro files (*.vim)\t*.vim\n" -"All Files (*)\t*\n" -msgstr "" -"Vim makro dosyaları (*.vim)\t*.vim\n" -"Tüm Dosyalar (*)\t*\n" - -msgid "All Files (*)\t*\n" -msgstr "Tüm Dosyalar (*)\t*\n" - -msgid "" -"All Files (*)\t*\n" -"C source (*.c, *.h)\t*.c;*.h\n" -"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" -msgstr "" -"Tüm Dosyalar (*)\t*\n" -"C kaynak dosyaları (*.c, *.h)\t*.c;*.h\n" -"C++ kaynak dosyaları (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"Vim dosyaları (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" - -msgid "GVim" -msgstr "GVim" - -msgid "Text Editor" -msgstr "Metin Düzenleyici" - -msgid "Edit text files" -msgstr "Metin dosyaları düzenleyin" - -msgid "Text;editor;" -msgstr "Metin;düzenleyici;" - -msgid "Vim" -msgstr "Vim" - -msgid "(local to window)" -msgstr "(pencereye yerel)" - -msgid "(local to buffer)" -msgstr "(arabelleğe yerel)" - -msgid "(global or local to buffer)" -msgstr "(arabelleğe global veya yerel)" - -msgid "" -"\" Each \"set\" line shows the current value of an option (on the left)." -msgstr "\" Her \"set\" satırı bir seçeneğin geçerli değerini gösterir (solda)." - -msgid "\" Hit <Enter> on a \"set\" line to execute it." -msgstr "\" Değiştirmek için bir \"set\" satırında <Enter>'a basın." - -msgid "\" A boolean option will be toggled." -msgstr "\" Bir Boole değeri işletilecektir." - -msgid "" -"\" For other options you can edit the value before hitting " -"<Enter>." -msgstr "" -"\" Diğer seçenekler için <Enter>'a basmadan önce değeri " -"düzenleyebilirsiniz." - -msgid "\" Hit <Enter> on a help line to open a help window on this option." -msgstr "\" Yardım penceresini açmak için seçenek adı üzerinde <Enter>'a basın." - -msgid "\" Hit <Enter> on an index line to jump there." -msgstr "" -"\" Bir seçeneğe atlamak için indeks satırının üzerinde <Enter>'a basın." - -msgid "\" Hit <Space> on a \"set\" line to refresh it." -msgstr "" -"\" Bir seçeneği yenilemek için bir \"set\" satırının üzerinde <Boşluk>'a " -"basın." - -msgid "important" -msgstr "önemli" - -msgid "behave very Vi compatible (not advisable)" -msgstr "olabildiğince Vi uyumlu biçimde davran (önerilmez)" - -msgid "list of flags to specify Vi compatibility" -msgstr "Vi uyumluluğu bayrakları listesi" - -msgid "use Insert mode as the default mode" -msgstr "Ekleme kipini öntanımlı kip olarak kullan" - -msgid "paste mode, insert typed text literally" -msgstr "yapıştır kipi, girilen metni doğrudan ekle" - -msgid "key sequence to toggle paste mode" -msgstr "yapıştır kipini açıp/kapatmak için düğme sıralaması" - -msgid "list of directories used for runtime files and plugins" -msgstr "çalışma zamanı dosyaları ve eklentileri için kullanılan dizinler" - -msgid "list of directories used for plugin packages" -msgstr "eklenti paketleri için kullanılan dizinlerin listesi" - -msgid "name of the main help file" -msgstr "ana yardım dosyasının adı" - -msgid "moving around, searching and patterns" -msgstr "dolaşma, arama ve dizgeler" - -msgid "list of flags specifying which commands wrap to another line" -msgstr "" -"hangi komutların diğer satıra kaydırıldığını belirleyen bayraklar\n" -"listesi" - -msgid "" -"many jump commands move the cursor to the first non-blank\n" -"character of a line" -msgstr "" -"çoğu atlama komutu, imleci satırın boş olmayan ilk\n" -"karakterine taşır" - -msgid "nroff macro names that separate paragraphs" -msgstr "paragrafları ayıran nroff makro adları" - -msgid "nroff macro names that separate sections" -msgstr "bölümleri ayıran nroff makro adları" - -msgid "list of directory names used for file searching" -msgstr "dosya arama için kullanılan dizin adları listesi" - -msgid "list of directory names used for :cd" -msgstr ":cd için kullanılan dizin adları listesi" - -msgid "change to directory of file in buffer" -msgstr "arabellekteki dosyanın olduğu dizine değiştir" - -msgid "change to pwd of shell in terminal buffer" -msgstr "uçbirim arabelleğindeki kabuğun pwd'sine geç" - -msgid "search commands wrap around the end of the buffer" -msgstr "arama komutları, arabelleğin sonunda kaydırılır" - -msgid "show match for partly typed search command" -msgstr "bir kısmı yazılmış arama komutu ile eşleşeni göster" - -msgid "change the way backslashes are used in search patterns" -msgstr "arama dizgilerinde ters eğik çizginin kullanımını değiştir" - -msgid "select the default regexp engine used" -msgstr "öntanımlı kullanılan düzenli ifade motorunu seç" - -msgid "ignore case when using a search pattern" -msgstr "bir arama dizgisinde BÜYÜK/küçük harf ayrımını yok say" - -msgid "override 'ignorecase' when pattern has upper case characters" -msgstr "dizgide BÜYÜK harf varsa 'ignorecase'i geçersiz kıl" - -msgid "what method to use for changing case of letters" -msgstr "BÜYÜK/küçük harf değiştirirken hangi yöntemin kullanılacağı" - -msgid "maximum amount of memory in Kbyte used for pattern matching" -msgstr "dizgi eşleşme için kullanılabilecek en çok bellek miktarı (KiB)" - -msgid "pattern for a macro definition line" -msgstr "bir makro tanım satırı için dizgi" - -msgid "pattern for an include-file line" -msgstr "bir 'include-file' satırı için dizgi" - -msgid "expression used to transform an include line to a file name" -msgstr "bir 'include' satırını dosya adına dönüştürmede kullanılan ifade" - -msgid "tags" -msgstr "etiketler" - -msgid "use binary searching in tags files" -msgstr "etiketler dosyasında ikili arama kullan" - -msgid "number of significant characters in a tag name or zero" -msgstr "bir etiket adındaki belirgin karakterlerin sayısı veya sıfır" - -msgid "list of file names to search for tags" -msgstr "etiketlerin aranacağı dosyaların listesi" - -msgid "" -"how to handle case when searching in tags files:\n" -"\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"" -msgstr "" -"etiket dosyalarında arama yaparken BÜYÜK/küçük harf kullanımı:\n" -"'ignorecase', \"ignore\" veya \"match\"den sonra \"followic\" gelir" - -msgid "file names in a tags file are relative to the tags file" -msgstr "bir etiket dosyasındaki dosya adları etiket dosyasına görelidir" - -msgid "a :tag command will use the tagstack" -msgstr "bir :tag komutu etiket yığınını kullanır" - -msgid "when completing tags in Insert mode show more info" -msgstr "Ekleme kipinde etiketleri tamamlarken daha çok bilgi göster" - -msgid "a function to be used to perform tag searches" -msgstr "etiket aramaları gerçekleştirmek için bir işlev kullanılır" - -msgid "command for executing cscope" -msgstr "cscope çalıştırmak için kullanılacak komut" - -msgid "use cscope for tag commands" -msgstr "etiket komutları için cscope kullan" - -msgid "0 or 1; the order in which \":cstag\" performs a search" -msgstr "0 veya 1; \":cstag\"in arama yaparken kullanacağı sıra" - -msgid "give messages when adding a cscope database" -msgstr "bir cscope veritabanı eklerken iletiler göster" - -msgid "how many components of the path to show" -msgstr "yolun kaç tane bileşeninin gösterileceği" - -msgid "when to open a quickfix window for cscope" -msgstr "cscope için ne zaman bir hızlı düzelt penceresinin açılacağı" - -msgid "file names in a cscope file are relative to that file" -msgstr "bir cscope dosyasındaki o dosyaya göreli olan dosya adları" - -msgid "displaying text" -msgstr "metin görüntüleme" - -msgid "number of lines to scroll for CTRL-U and CTRL-D" -msgstr "CTRL-U ve CTRL-D için kaydırılacak satır sayısı" - -msgid "number of screen lines to show around the cursor" -msgstr "imleç etrafında gösterilecek ekran satırları sayısı" - -msgid "long lines wrap" -msgstr "uzun satırları kaydır" - -msgid "wrap long lines at a character in 'breakat'" -msgstr "'breakat' içindeki bir karakterde uzun satırları kaydır" - -msgid "preserve indentation in wrapped text" -msgstr "kaydırılmış metindeki girintilemeyi koru" - -msgid "adjust breakindent behaviour" -msgstr "'breakindent' davranışını ayarla" - -msgid "which characters might cause a line break" -msgstr "hangi karakterler bir satır sonuna neden olabilir" - -msgid "string to put before wrapped screen lines" -msgstr "kaydırılmış ekran satırlarından önce konumlanacak dizi" - -msgid "minimal number of columns to scroll horizontally" -msgstr "en az yatay kaydırma sütunu sayısı" - -msgid "minimal number of columns to keep left and right of the cursor" -msgstr "imlecin sağında ve solunda bırakılacak en az sütun sayısı" - -msgid "" -"include \"lastline\" to show the last line even if it doesn't fit\n" -"include \"uhex\" to show unprintable characters as a hex number" -msgstr "" -"eğer sığmasa bile son satırı göstermek için \"lastline\"ı içer\n" -"yazdırılamayan karakterleri onaltılık olarak göstermek için\n" -"\"uhex\" içer" - -msgid "characters to use for the status line, folds and filler lines" -msgstr "durum satırı, kıvırma ve doldurucular için kullanılan karakterler" - -msgid "number of lines used for the command-line" -msgstr "komut satırı için kullanılan satırların sayısı" - -msgid "width of the display" -msgstr "ekranın genişliği" - -msgid "number of lines in the display" -msgstr "ekrandaki satırların sayısı" - -msgid "number of lines to scroll for CTRL-F and CTRL-B" -msgstr "CTRL-F ve CTRL-B için kaydırılacak satır sayısı" - -msgid "don't redraw while executing macros" -msgstr "makroları çalıştırırken yenileme yapma" - -msgid "timeout for 'hlsearch' and :match highlighting in msec" -msgstr "'hlsearch' ve : match vurgulaması için zaman aşımı (milisaniye)" - -msgid "" -"delay in msec for each char written to the display\n" -"(for debugging)" -msgstr "" -"ekrana yazılan her karakter için gecikme süresi (milisaniye)\n" -"(hata ayıklama için)" - -msgid "show <Tab> as ^I and end-of-line as $" -msgstr "<Tab>'ı ^I ve satır sonunu $ olarak göster" - -msgid "list of strings used for list mode" -msgstr "liste kipi için kullanılan diziler listesi" - -msgid "show the line number for each line" -msgstr "her satır için satır numarasını göster" - -msgid "show the relative line number for each line" -msgstr "her satır için göreli satır numarasını göster" - -msgid "number of columns to use for the line number" -msgstr "satır numarası için kullanılacak sütün sayısı" - -msgid "controls whether concealable text is hidden" -msgstr "gizlenebilir metnin saklı olup olmadığını denetler" - -msgid "modes in which text in the cursor line can be concealed" -msgstr "imleç satırındaki metnin gizlenebileceği kipler" - -msgid "syntax, highlighting and spelling" -msgstr "sözdizim, vurgulama ve yazım denetimi" - -msgid "\"dark\" or \"light\"; the background color brightness" -msgstr "\"dark\" veya \"light\"; arka plan renk parlaklığı" - -msgid "type of file; triggers the FileType event when set" -msgstr "dosya türü; ayarlandığında FileType olayını tetikler" - -msgid "name of syntax highlighting used" -msgstr "kullanılan sözdizim vurgulamanın adı" - -msgid "maximum column to look for syntax items" -msgstr "sözdizim ögeleri için bakılacak en çok sütun sayısı" - -msgid "which highlighting to use for various occasions" -msgstr "çeşitli durumlarda hangi vurgulamanın kullanılacağı" - -msgid "highlight all matches for the last used search pattern" -msgstr "son kullanılan arama dizgisi için tüm eşleşmeleri vurgula" - -msgid "highlight group to use for the window" -msgstr "pencere için kullanılacak vurgulama grubu" - -msgid "use GUI colors for the terminal" -msgstr "uçbirim için grafik arabirim renklerini kullan" - -msgid "highlight the screen column of the cursor" -msgstr "imlecin ekrandaki sütununu vurgula" - -msgid "highlight the screen line of the cursor" -msgstr "imlecin ekrandaki satırını vurgula" - -msgid "specifies which area 'cursorline' highlights" -msgstr "'cursorline'ın hangi alanı vurgulayacağı" - -msgid "columns to highlight" -msgstr "vurgulanacak sütunlar" - -msgid "highlight spelling mistakes" -msgstr "yazım yanlışlarını vurgula" - -msgid "list of accepted languages" -msgstr "kabul edilen dillerin listesi" - -msgid "file that \"zg\" adds good words to" -msgstr "\"zg\" komutunun düzgün sözcükleri ekleyeceği dosya" - -msgid "pattern to locate the end of a sentence" -msgstr "bir tümcenin sonunu bulmak için kullanılan dizgi" - -msgid "flags to change how spell checking works" -msgstr "yazım denetiminin nice çalıştığını değiştirmek için bayraklar" - -msgid "methods used to suggest corrections" -msgstr "düzeltmeleri önermek için yöntemler" - -msgid "amount of memory used by :mkspell before compressing" -msgstr "sıkıştırma öncesi :mkspell tarafından kullanılan bellek" - -msgid "multiple windows" -msgstr "çoklu pencereler" - -msgid "0, 1 or 2; when to use a status line for the last window" -msgstr "" -"0, 1 veya 2; son pencere için ne zaman bir durum satırı\n" -"kullanılacağı" - -msgid "alternate format to be used for a status line" -msgstr "durum satırı için kullanılabilecek alternatif biçim" - -msgid "make all windows the same size when adding/removing windows" -msgstr "pencere eklerken/kaldırırken tüm pencereleri aynı boyuta getir" - -msgid "in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"" -msgstr "'equalalways'in hangi yönde çalıştığı: \"ver\", \"hor\" veya \"both\"" - -msgid "minimal number of lines used for the current window" -msgstr "geçerli pencere için kullanılan en az satır sayısı" - -msgid "minimal number of lines used for any window" -msgstr "herhangi bir pencere için kullanılan en az satır sayısı" - -msgid "keep the height of the window" -msgstr "pencerenin yüksekliğini tut" - -msgid "keep the width of the window" -msgstr "pencerenin genişliğini tut" - -msgid "minimal number of columns used for the current window" -msgstr "geçerli pencere için kullanılan en az sütun sayısı" - -msgid "minimal number of columns used for any window" -msgstr "herhangi bir pencere için kullanılan en az sütun sayısı" - -msgid "initial height of the help window" -msgstr "yardım penceresinin başlangıç yüksekliği" - -msgid "use a popup window for preview" -msgstr "önizleme için bir açılır pencere kullan" - -msgid "default height for the preview window" -msgstr "önizleme penceresi için öntanımlı yükseklik" - -msgid "identifies the preview window" -msgstr "önizleme penceresini tanımlar" - -msgid "don't unload a buffer when no longer shown in a window" -msgstr "arabellek artık pencerede görüntülenmiyorsa bellekten kaldırma" - -msgid "" -"\"useopen\" and/or \"split\"; which window to use when jumping\n" -"to a buffer" -msgstr "" -"\"useopen\" ve/veya \"split\"; bir belleğe atlarken hangi\n" -"pencerenin kullanılacağı" - -msgid "a new window is put below the current one" -msgstr "geçerli pencerenin altına yeni bir pencere koyulur" - -msgid "a new window is put right of the current one" -msgstr "yeni bir pencere geçerli pencerenin sağına koyulur" - -msgid "this window scrolls together with other bound windows" -msgstr "bu pencere, bağlı diğer pencerelerle birlikte kayar" - -msgid "\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'" -msgstr "" -"\"ver\", \"hor\" ve/veya \"jump\"; 'scrollbind' için seçenekler listesi" - -msgid "this window's cursor moves together with other bound windows" -msgstr "bu pencerenin imleci bağlı diğer pencerelerle birlikte kayar" - -msgid "size of a terminal window" -msgstr "bir uçbirim penceresinin boyutu" - -msgid "key that precedes Vim commands in a terminal window" -msgstr "bir uçbirim penceresinde Vim komutlarından önce gelen düğme" - -msgid "max number of lines to keep for scrollback in a terminal window" -msgstr "" -"bir uçbirim penceresinde geri kaydırma için kullanılacak\n" -"en çok satır sayısı" - -msgid "type of pty to use for a terminal window" -msgstr "bir uçbirim penceresi için kullanılacak pty türü" - -msgid "name of the winpty dynamic library" -msgstr "winpty devingen kitaplığının adı" - -msgid "multiple tab pages" -msgstr "çoklu sekme sayfaları" - -msgid "0, 1 or 2; when to use a tab pages line" -msgstr "0, 1 veya 2; ne zaman bir sekme sayfası satırının kullanılacağı" - -msgid "maximum number of tab pages to open for -p and \"tab all\"" -msgstr "-p ve \"tab all\"un açacağı en çok sekme sayfası sayısı" - -msgid "custom tab pages line" -msgstr "özelleştirilmiş sekme sayfası satırı" - -msgid "custom tab page label for the GUI" -msgstr "grafik arabirim için özelleştirilmiş sekme sayfası etiketi" - -msgid "custom tab page tooltip for the GUI" -msgstr "grafik arabirim için özelleştirilmiş sekme sayfası bilgi kutusu" - -msgid "terminal" -msgstr "uçbirim" - -msgid "name of the used terminal" -msgstr "kullanılan uçbirimin adı" - -msgid "alias for 'term'" -msgstr "'term' için arma" - -msgid "check built-in termcaps first" -msgstr "önce iç termcaps'i denetle" - -msgid "terminal connection is fast" -msgstr "uçbirim bağlantısı hızlı" - -msgid "terminal that requires extra redrawing" -msgstr "ek yenileme gerektiren uçbirim" - -msgid "recognize keys that start with <Esc> in Insert mode" -msgstr "Ekleme kipinde <Esc> ile başlayan düğmeleri tanı" - -msgid "minimal number of lines to scroll at a time" -msgstr "herhangi bir zamanda kaydırılacak en az satır sayısı" - -msgid "maximum number of lines to use scrolling instead of redrawing" -msgstr "yenileme yerine kaydırma kullanacak en çok satır sayısı" - -msgid "specifies what the cursor looks like in different modes" -msgstr "imlecin farklı kiplerde nice göründüğünü belirler" - -msgid "show info in the window title" -msgstr "pencere başlığında bilgi görüntüle" - -msgid "percentage of 'columns' used for the window title" -msgstr "pencere başlığı için kullanılacak 'columns' yüzdesi" - -msgid "when not empty, string to be used for the window title" -msgstr "boş değilken, pencere başlığı yerine kullanılacak dizi" - -msgid "string to restore the title to when exiting Vim" -msgstr "Vim'den çıkarken başlığın döndürüleceği dizi" - -msgid "set the text of the icon for this window" -msgstr "bu pencere için simgenin metnini ayarla" - -msgid "when not empty, text for the icon of this window" -msgstr "boş değilken, bu pencerenin simgesi için metin" - -msgid "restore the screen contents when exiting Vim" -msgstr "Vim'den çıkarken ekran içeriğini eski haline getir" - -msgid "using the mouse" -msgstr "fare kullanımı" - -msgid "list of flags for using the mouse" -msgstr "fare kullanımı için bayraklar listesi" - -msgid "the window with the mouse pointer becomes the current one" -msgstr "fare imlecinin olduğu pencere geçerli pencere olur" - -msgid "the window with the mouse pointer scrolls with the mouse wheel" -msgstr "fare imlecinin olduğu pencere fare tekerleği ile kaydırılabilir" - -msgid "hide the mouse pointer while typing" -msgstr "yazı yazarken fare imlecini gizle" - -msgid "" -"\"extend\", \"popup\" or \"popup_setpos\"; what the right\n" -"mouse button is used for" -msgstr "" -"\"extend\", \"popup\" veya \"popup_setpos\"; sağ fare düğmesinin\"\n" -"ne için kullanıldığı" - -msgid "maximum time in msec to recognize a double-click" -msgstr "bir çif tıklamayı tanımak için en çok süre (milisaniye)" - -msgid "\"xterm\", \"xterm2\", \"sgr\", etc.; type of mouse" -msgstr "\"xterm\", \"xterm2\", \"sgr\" vb.; fare türü" - -msgid "what the mouse pointer looks like in different modes" -msgstr "farklı kiplerde fare imlecinin nice göründüğü" - -msgid "GUI" -msgstr "grafik arabirim" - -msgid "list of font names to be used in the GUI" -msgstr "grafik arabirimde kullanılacak yazıtiplerinin listesi" - -msgid "pair of fonts to be used, for multibyte editing" -msgstr "çoklu bayt düzenlemede kullanılacak yazıtipi eşleşmeleri" - -msgid "list of font names to be used for double-wide characters" -msgstr "çift genişlikli karakterler için kullanılacak yazıtiplerinin listesi" - -msgid "use smooth, antialiased fonts" -msgstr "düzletilmiş yazıtipleri kullan" - -msgid "list of flags that specify how the GUI works" -msgstr "grafik arabirimin nice çalıştığını belirleyen bayraklar listesi" - -msgid "\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar" -msgstr "\"icons\", \"text\" ve/veya \"tooltips\"; araç çubuğu kipleri " - -msgid "size of toolbar icons" -msgstr "araç çubuğu simgelerinin boyutu" - -msgid "room (in pixels) left above/below the window" -msgstr "pencerenin altında/üstünde bırakılan alan (piksel)" - -msgid "options for text rendering" -msgstr "metin dokuması için seçenekler" - -msgid "use a pseudo-tty for I/O to external commands" -msgstr "dış komutlara, girdi çıktı için yalancı-tty kullan" - -msgid "" -"\"last\", \"buffer\" or \"current\": which directory used for the file " -"browser" -msgstr "" -"\"last\", \"buffer\" veya \"current\"; dosya tarayıcısı için hangi dizinin " -"kullanıldığı" - -msgid "language to be used for the menus" -msgstr "menüler için kullanılan dil" - -msgid "maximum number of items in one menu" -msgstr "bir menüdeki en çok öge sayısı" - -msgid "\"no\", \"yes\" or \"menu\"; how to use the ALT key" -msgstr "\"no\", \"yes\" veya \"menu\"; ALT düğmesinin nice kullanılacağı" - -msgid "number of pixel lines to use between characters" -msgstr "karakterler arasında kullanılacak piksel satırları sayısı" - -msgid "delay in milliseconds before a balloon may pop up" -msgstr "bir balonun patlamadan önceki gecikme (milisaniye)" - -msgid "use balloon evaluation in the GUI" -msgstr "grafik arabirimde balon değerlendirme kullan" - -msgid "use balloon evaluation in the terminal" -msgstr "uçbirimde balon değerlendirme kullan" - -msgid "expression to show in balloon eval" -msgstr "balon değerlendirmesinde gösterilecek ifade" - -msgid "printing" -msgstr "yazdırma" - -msgid "list of items that control the format of :hardcopy output" -msgstr ":hardcopy çıktısının biçimini denetleyen ögelerin listesi" - -msgid "name of the printer to be used for :hardcopy" -msgstr ":hardcopy için kullanılan yazıcının adı" - -msgid "expression used to print the PostScript file for :hardcopy" -msgstr ":hardcopy için PostScript dosyasını yazdırmada kullanılan ifade" - -msgid "name of the font to be used for :hardcopy" -msgstr ":hardcopy için kullanılan yazıtipinin adı" - -msgid "format of the header used for :hardcopy" -msgstr ":hardcopy için kullanılan üstbilginin biçimi" - -msgid "encoding used to print the PostScript file for :hardcopy" -msgstr ":hardcopy için Postscript dosyasını yazdırmada kullanılan kodlama" - -msgid "the CJK character set to be used for CJK output from :hardcopy" -msgstr ":hardcopy'deki ÇJK çıktısında kullanılan ÇJK karakter seti" - -msgid "list of font names to be used for CJK output from :hardcopy" -msgstr ":hardcopy'deki ÇJK çıktısında kullanılan ÇJK yazıtipi" - -msgid "messages and info" -msgstr "iletiler ve bilgi" - -msgid "add 's' flag in 'shortmess' (don't show search message)" -msgstr "'shortness'daki 's' bayrağını ekle (arama iletisini gösterme)" - -msgid "list of flags to make messages shorter" -msgstr "iletileri kısalaştırmak için kullanılan bayraklar listesi" - -msgid "show (partial) command keys in the status line" -msgstr "durum satırında (kısmi) komut düğmelerini göster" - -msgid "display the current mode in the status line" -msgstr "durum satırında geçerli kipi görüntüle" - -msgid "show cursor position below each window" -msgstr "her pencerenin altında imleç konumunu göster" - -msgid "alternate format to be used for the ruler" -msgstr "cetvel için kullanılan alternatif biçim" - -msgid "threshold for reporting number of changed lines" -msgstr "değiştirilmiş satırların sayısını raporlama eşiği" - -msgid "the higher the more messages are given" -msgstr "ne kadar yüksek olursa o kadar çok ileti olur" - -msgid "file to write messages in" -msgstr "iletilerin içine yazılacağı dosya" - -msgid "pause listings when the screen is full" -msgstr "ekran doluyken listelemeleri duraklat" - -msgid "start a dialog when a command fails" -msgstr "bir komut başarısız olursa iletişim kutusu göster" - -msgid "ring the bell for error messages" -msgstr "hata iletilerinde zili çal" - -msgid "use a visual bell instead of beeping" -msgstr "bipleme yerine görsel zil kullan" - -msgid "do not ring the bell for these reasons" -msgstr "bu nedenlerle zili çalma" - -msgid "list of preferred languages for finding help" -msgstr "yardım için yeğlenen diller listesi" - -msgid "selecting text" -msgstr "metin seçme" - -msgid "\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves" -msgstr "\"old\", \"inclusive\" veya \"exclusive\"; metin seçim davranışı" - -msgid "" -"\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode\n" -"instead of Visual mode" -msgstr "" -"\"mouse\", \"key\", ve/veya \"cmd\"; Görsel kip yerine Seçim\n" -"kipinin başlatılacağı zaman" - -msgid "" -"\"unnamed\" to use the * register like unnamed register\n" -"\"autoselect\" to always put selected text on the clipboard" -msgstr "" -"\"unnamed\": * yazmacını adsız yazmaç gibi kullan\n" -"\"autoselect\": seçili metni her zaman panoya koy" - -msgid "\"startsel\" and/or \"stopsel\"; what special keys can do" -msgstr "\"startsel\" ve/veya \"stopsel\"; özel düğmelerin işlevleri" - -msgid "editing text" -msgstr "metin düzenleme" - -msgid "maximum number of changes that can be undone" -msgstr "geri alınabilecek en çok değişiklik sayısı" - -msgid "automatically save and restore undo history" -msgstr "geri al geçmişini kendiliğinden kaydet ve eski haline getir" - -msgid "list of directories for undo files" -msgstr "geri al dosyaları için dizinler listesi" - -msgid "maximum number lines to save for undo on a buffer reload" -msgstr "" -"arabellek yeniden yüklemesinde geri al için kaydedilecek\n" -"en çok satır sayısı" - -msgid "changes have been made and not written to a file" -msgstr "yapılan; ancak bir dosyaya yazılmayan değişiklikler" - -msgid "buffer is not to be written" -msgstr "arabellek, yazım için değil" - -msgid "changes to the text are possible" -msgstr "metne değişiklik yapımı olanaklı" - -msgid "line length above which to break a line" -msgstr "sonrasında yeni satır yapılacak satır uzunluğu" - -msgid "margin from the right in which to break a line" -msgstr "sonrasında yeni satır yapılacak sağ kenar boşluğu" - -msgid "specifies what <BS>, CTRL-W, etc. can do in Insert mode" -msgstr "<BS>, CTRL-W, vb. Ekleme kipinde ne yapabileceğini belirtir" - -msgid "definition of what comment lines look like" -msgstr "yorum satırlarının nice görüneceğinin tanımı" - -msgid "list of flags that tell how automatic formatting works" -msgstr "" -"kendiliğinden biçimlendirmenin nice çalıştığını anlatan\n" -"bayraklar listesi" - -msgid "pattern to recognize a numbered list" -msgstr "numaralandırılmış bir listeyi tanımak için dizgi" - -msgid "expression used for \"gq\" to format lines" -msgstr "satırları biçimlendirmek için \"gq\" için kullanılan ifade" - -msgid "specifies how Insert mode completion works for CTRL-N and CTRL-P" -msgstr "" -"Ekleme kipi tamamlamasının CTRL-N ve CTRL-P için nice çalıştığını\n" -"belirler" - -msgid "whether to use a popup menu for Insert mode completion" -msgstr "Ekleme kipi tamamlaması için açılır menü kullanımı" - -msgid "options for the Insert mode completion info popup" -msgstr "Ekleme kipi tamamlama açılır penceresi için seçenekler" - -msgid "maximum height of the popup menu" -msgstr "açılır menünün en çok yüksekliği" - -msgid "minimum width of the popup menu" -msgstr "açılır menünün en çok genişliği" - -msgid "user defined function for Insert mode completion" -msgstr "Ekleme kipi tamamlaması için kullanıcı tanımlı işlev" - -msgid "function for filetype-specific Insert mode completion" -msgstr "dosya türüne özel Ekleme kipi tamamlaması için işlev" - -msgid "list of dictionary files for keyword completion" -msgstr "anahtar sözcük tamamlaması için sözlük dosyaları listesi" - -msgid "list of thesaurus files for keyword completion" -msgstr "" -"anahtar sözcük tamamlaması için eşanlamlılar sözlüğü dosyaları\n" -"listesi" - -msgid "adjust case of a keyword completion match" -msgstr "anahtar sözcük tamamlama eşleşmesinin BÜYÜK/küçük harfini ayarla" - -msgid "enable entering digraphs with c1 <BS> c2" -msgstr "c1 <BS> c2 ile ikili harflerin girilmesini etkinleştir" - -msgid "the \"~\" command behaves like an operator" -msgstr "\"~\" komutu bir işleç gibi davranır" - -msgid "function called for the \"g@\" operator" -msgstr "\"g@\" işleci için çağrılan işlev" - -msgid "when inserting a bracket, briefly jump to its match" -msgstr "bir ayraç eklendiğinde hemen eşine atla" - -msgid "tenth of a second to show a match for 'showmatch'" -msgstr "bir 'showmatch' eşleşmesini göstermek için saniyenin onda biri" - -msgid "list of pairs that match for the \"%\" command" -msgstr "\"%\" komutu için eşleşen eşleşmelerin listesi" - -msgid "use two spaces after '.' when joining a line" -msgstr "bir satırı birleştirirken '.' sonrası iki boşluk kullan" - -msgid "" -"\"alpha\", \"octal\", \"hex\", \"bin\" and/or \"unsigned\"; number formats\n" -"recognized for CTRL-A and CTRL-X commands" -msgstr "" -"\"alpha\", \"octal\", \"hex\", \"bin\" ve/veya \"unsigned\"; CTRL-A ve\n" -"CTRL-X komutları için tanınan sayı biçimleri" - -msgid "tabs and indenting" -msgstr "sekmeler ve girintileme" - -msgid "number of spaces a <Tab> in the text stands for" -msgstr "metinde bir <Tab>'ın denk olduğu boşluk sayısı" - -msgid "number of spaces used for each step of (auto)indent" -msgstr "her bir kendiliğinden girintileme için kullanılan boşluk sayısı" - -msgid "list of number of spaces a tab counts for" -msgstr "bir sekmenin denk olduğu boşlukların sayısının listesi" - -msgid "list of number of spaces a soft tabsstop counts for" -msgstr "bir yumuşak sekmedurağının denk olduğu boşlukların sayısı listesi" - -msgid "a <Tab> in an indent inserts 'shiftwidth' spaces" -msgstr "bir girintideki <Tab>, 'shiftwidth' kadar boşluk ekler" - -msgid "if non-zero, number of spaces to insert for a <Tab>" -msgstr "eğer sıfırdan farklıysa, bir <Tab> için eklenecek boşluk sayısı" - -msgid "round to 'shiftwidth' for \"<<\" and \">>\"" -msgstr "\"<<\" ve \">>\" için 'shiftwidth'e yuvarla" - -msgid "expand <Tab> to spaces in Insert mode" -msgstr "Ekleme kipinde <Tab>'ı boşluklara genişlet" - -msgid "automatically set the indent of a new line" -msgstr "yeni bir satırın girintisini kendiliğinden ayarla" - -msgid "do clever autoindenting" -msgstr "akıllı kendiliğinden girintileme yap" - -msgid "enable specific indenting for C code" -msgstr "C kodu için özel girintilemeyi etkinleştir" - -msgid "options for C-indenting" -msgstr "C girintilemesi için seçenekler" - -msgid "keys that trigger C-indenting in Insert mode" -msgstr "Ekleme kipinde C girintilemesini tetikleyen düğmeler" - -msgid "list of words that cause more C-indent" -msgstr "daha çok C girintilemesine neden olan sözcüklerin listesi" - -msgid "expression used to obtain the indent of a line" -msgstr "bir satırın girintisini elde etmek için kullanılan ifade" - -msgid "keys that trigger indenting with 'indentexpr' in Insert mode" -msgstr "Ekleme kipinde 'indentexpr' ile girintilemeyi tetikleyen düğmeler" - -msgid "copy whitespace for indenting from previous line" -msgstr "bir önceki satırdan girintileme için boşlukları kopyala" - -msgid "preserve kind of whitespace when changing indent" -msgstr "girintilemeyi değiştirirken boşluk türünü koru" - -msgid "enable lisp mode" -msgstr "lisp kipini etkinleştir" - -msgid "words that change how lisp indenting works" -msgstr "lisp girintilemesinin nice çalıştığını değiştiren sözcükler" - -msgid "folding" -msgstr "kıvırma" - -msgid "unset to display all folds open" -msgstr "tüm kıvırmaları açık görüntülemek için ayarı kaldır" - -msgid "folds with a level higher than this number will be closed" -msgstr "bu sayıdan daha yüksek düzeyli kıvırmalar kapatılacak" - -msgid "value for 'foldlevel' when starting to edit a file" -msgstr "bir dosyayı düzenlemeye başlarkenki 'foldlevel' değeri" - -msgid "width of the column used to indicate folds" -msgstr "kıvırmaları belirtmek için kullanılan sütunun genişliği" - -msgid "expression used to display the text of a closed fold" -msgstr "kapalı bir kıvırmanın metnini görüntülemek için kullanılan ifade" - -msgid "set to \"all\" to close a fold when the cursor leaves it" -msgstr "imleç ayrıldığında kıvırmayı kapatmak için \"all\" olarak ayarlayın" - -msgid "specifies for which commands a fold will be opened" -msgstr "hangi komutlarda bir kıvırmanın açılacağını belirler" - -msgid "minimum number of screen lines for a fold to be closed" -msgstr "bir kıvırmanın kapatılması için en az ekran satırı sayısı" - -msgid "template for comments; used to put the marker in" -msgstr "yorumlar için şablon; imleyiciyi içine koymak için kullanılır" - -msgid "" -"folding type: \"manual\", \"indent\", \"expr\", \"marker\",\n" -"\"syntax\" or \"diff\"" -msgstr "" -"kıvırma türü: \"manual\", \"indent\", \"expr\", \"marker\",\n" -"\"syntax\" veya \"diff\"" - -msgid "expression used when 'foldmethod' is \"expr\"" -msgstr "'foldmethod' \"expr\" olduğundan kullanılacak ifade" - -msgid "used to ignore lines when 'foldmethod' is \"indent\"" -msgstr "'foldmethod' \"indent\" olduğunda satırları yok saymada kullanılır" - -msgid "markers used when 'foldmethod' is \"marker\"" -msgstr "'foldmethod' \"marker\" olduğunda kullanılan imleyiciler" - -msgid "maximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"" -msgstr "" -"'foldmethod' \"indent\" veya \"syntax\" olduğunda kullanılan en çok\n" -"kıvırma derinliği" - -msgid "diff mode" -msgstr "diff kipi" - -msgid "use diff mode for the current window" -msgstr "geçerli pencere için diff kipi kullan" - -msgid "options for using diff mode" -msgstr "diff kipi kullanımı için seçenekler" - -msgid "expression used to obtain a diff file" -msgstr "bir diff dosyası elde etmek için kullanılan ifade" - -msgid "expression used to patch a file" -msgstr "bir dosyayı yamalamak için kullanılan ifade" - -msgid "mapping" -msgstr "eşlemleme" - -msgid "maximum depth of mapping" -msgstr "en çok eşlemleme derinliği" - -msgid "recognize mappings in mapped keys" -msgstr "eşlemlenmiş düğmelerdeki eşlemlemeleri tanımla" - -msgid "allow timing out halfway into a mapping" -msgstr "bir eşlemlemenin yarısında zaman aşımına izin ver" - -msgid "allow timing out halfway into a key code" -msgstr "bir düğme kodunun yarısında zaman aşımına izin ver" - -msgid "time in msec for 'timeout'" -msgstr "'timeout' için süre (milisaniye)" - -msgid "time in msec for 'ttimeout'" -msgstr "'ttimeout' için süre (milisaniye)" - -msgid "reading and writing files" -msgstr "dosyaları okuma ve yazma" - -msgid "enable using settings from modelines when reading a file" -msgstr "dosya okurken ayarları kip satırından kullanımı etkinleştir" - -msgid "allow setting expression options from a modeline" -msgstr "ifade seçeneklerini bir kip satırından ayarlamaya izin ver" - -msgid "number of lines to check for modelines" -msgstr "kip satırlarını denetlemede kullanılacak satırların sayısı" - -msgid "binary file editing" -msgstr "ikili dosya düzenleme" - -msgid "last line in the file has an end-of-line" -msgstr "dosyanın son satırında bir satırsonu var" - -msgid "fixes missing end-of-line at end of text file" -msgstr "bir metin dosyasının sonundaki eksik satırsonlarını onarır" - -msgid "prepend a Byte Order Mark to the file" -msgstr "dosyanın önüne bir Bayt Sıralama İmi ekle" - -msgid "end-of-line format: \"dos\", \"unix\" or \"mac\"" -msgstr "satırsonu biçimi: \"dos\", \"unix\" veya \"mac\"" - -msgid "list of file formats to look for when editing a file" -msgstr "bir dosyayı düzenlerken bakılacak dosya biçimler listesi" - -msgid "obsolete, use 'fileformat'" -msgstr "eskimiş, yerine 'fileformat' kullanın" - -msgid "obsolete, use 'fileformats'" -msgstr "eskimiş, yerine 'fileformats' kullanın" - -msgid "writing files is allowed" -msgstr "dosya yazımına izin verilir" - -msgid "write a backup file before overwriting a file" -msgstr "bir dosyanın üzerine yazmadan önce bir yedek dosyası yaz" - -msgid "keep a backup after overwriting a file" -msgstr "bir dosyanın üzerine yazdıktan sonra bir yedek tut" - -msgid "patterns that specify for which files a backup is not made" -msgstr "bir yedeği yapılmayan dosyaları belirleyen dizgi" - -msgid "whether to make the backup as a copy or rename the existing file" -msgstr "yedeğin kopya olarak mı yoksa ad değişikliği ile mi yapılacağı" - -msgid "list of directories to put backup files in" -msgstr "yedek dosyalarının koyulacağı dizinlerin listesi" - -msgid "file name extension for the backup file" -msgstr "yedek dosyası için dosya adı uzantısı" - -msgid "automatically write a file when leaving a modified buffer" -msgstr "değiştirilmiş arabellekten çıkarken dosyayı kendiliğinden yaz" - -msgid "as 'autowrite', but works with more commands" -msgstr "'autowrite' gibi, ancak daha çok komutla çalışır" - -msgid "always write without asking for confirmation" -msgstr "onay beklemeden her zaman yaz" - -msgid "automatically read a file when it was modified outside of Vim" -msgstr "Vim dışında değiştirildiğinde dosyayı kendiliğinden oku" - -msgid "keep oldest version of a file; specifies file name extension" -msgstr "bir dosyanın en eski sürümünü tut; dosya adı uzantısı belirler" - -msgid "forcibly sync the file to disk after writing it" -msgstr "yazımdan sonra dosyayı zorla diske eşitle" - -msgid "use 8.3 file names" -msgstr "8.3 dosya adlarını kullan" - -msgid "encryption method for file writing: zip, blowfish or blowfish2" -msgstr "dosya yazımı için şifreleme yöntemi: zip, blowfish veya blowfish2" - -msgid "the swap file" -msgstr "takas dosyası" - -msgid "list of directories for the swap file" -msgstr "takas dosyası için dizinler listesi" - -msgid "use a swap file for this buffer" -msgstr "bu arabellek için bir takas dosyası kullan" - -msgid "\"sync\", \"fsync\" or empty; how to flush a swap file to disk" -msgstr "" -"\"sync\", \"fsync\", veya boş; bir takas dosyasının diske\n" -"nice floşlanacağı" - -msgid "number of characters typed to cause a swap file update" -msgstr "takas dosyası güncellemesi için yazılması gereken karakter sayısı" - -msgid "time in msec after which the swap file will be updated" -msgstr "takas dosyasının güncelleneceği süre dilimi (milisaniye)" - -msgid "maximum amount of memory in Kbyte used for one buffer" -msgstr "bir arabellek için kullanılacak en çok bellek miktarı (KiB)" - -msgid "maximum amount of memory in Kbyte used for all buffers" -msgstr "tüm arabellekler için kullanılacak en çok bellek miktarı (KiB)" - -msgid "command line editing" -msgstr "komut satırı düzenleme" - -msgid "how many command lines are remembered" -msgstr "kaç tane komut satırının hatırlandığı" - -msgid "key that triggers command-line expansion" -msgstr "komut satırı ifadesi tetikleyen düğme" - -msgid "like 'wildchar' but can also be used in a mapping" -msgstr "'wildchar' gibi; ancak bir eşlemleme içinde kullanılabilir" - -msgid "specifies how command line completion works" -msgstr "komut satırı tamamlamasının nasıl çalıştığını belirtir" - -msgid "empty or \"tagfile\" to list file name of matching tags" -msgstr "eşleşen etiketlerin dosya adını listelemek için boş veya \"tagfile\"" - -msgid "list of file name extensions that have a lower priority" -msgstr "düşük öncelikli dosya adı uzantılarının listesi" - -msgid "list of file name extensions added when searching for a file" -msgstr "bir dosya ararken eklenen dosya adı uzantılarının listesi" - -msgid "list of patterns to ignore files for file name completion" -msgstr "dosya adı tamamlaması için yok sayılacak dizgelerin listesi" - -msgid "ignore case when using file names" -msgstr "dosya adları kullanırken BÜYÜK/küçük harf yok say" - -msgid "ignore case when completing file names" -msgstr "dosya adları tamamlarken BÜYÜK/küçük harf yok say" - -msgid "command-line completion shows a list of matches" -msgstr "komut satırı tamamlaması, eşleşmelerin bir listesini gösterir" - -msgid "key used to open the command-line window" -msgstr "komut satırı penceresini açmak için kullanılan düğme" - -msgid "height of the command-line window" -msgstr "komut satırı penceresinin yüksekliği" - -msgid "executing external commands" -msgstr "dış komutları çalıştırma" - -msgid "name of the shell program used for external commands" -msgstr "dış komutlar için kullanılan kabuk programının adı" - -msgid "when to use the shell or directly execute a command" -msgstr "ne zaman kabuğu kullanmalı veya doğrudan bir komut çalıştırmalı" - -msgid "character(s) to enclose a shell command in" -msgstr "bir kabuk komutunu çevreleyen karakter(ler)" - -msgid "like 'shellquote' but include the redirection" -msgstr "'shellquote' gibi; ancak yeniden yönlendirmeyi içer" - -msgid "characters to escape when 'shellxquote' is (" -msgstr "'shellxquote' ( iken kaçırılacak karakterler" - -msgid "argument for 'shell' to execute a command" -msgstr "bir komut çalıştırmak için 'shell' için argüman" - -msgid "used to redirect command output to a file" -msgstr "komut çıktısını bir dosyaya yeniden yönlendirmek için kullanılır" - -msgid "use a temp file for shell commands instead of using a pipe" -msgstr "" -"bir veri yolu kullanımı yerine kabuk komutları için geçici\n" -"bir dosya kullan" - -msgid "program used for \"=\" command" -msgstr "\"=\" komutu için kullanılan program" - -msgid "program used to format lines with \"gq\" command" -msgstr "\"gq\" komutu ile satır biçimlemek için kullanılan program" - -msgid "program used for the \"K\" command" -msgstr "\"K\" komutu için kullanılan program" - -msgid "warn when using a shell command and a buffer has changes" -msgstr "" -"bir kabuk komutu kullanılıyorsa ve arabellekte değişiklikler\n" -"varsa uyar" - -msgid "running make and jumping to errors (quickfix)" -msgstr "make çalıştırma ve hatalara atlama (hızlı düzelt)" - -msgid "name of the file that contains error messages" -msgstr "hata iletileri içeren dosyanın adı" - -msgid "list of formats for error messages" -msgstr "hata iletileri için biçim listesi" - -msgid "program used for the \":make\" command" -msgstr "\":make\" komutu için kullanılan program" - -msgid "string used to put the output of \":make\" in the error file" -msgstr "" -"\":make\" komutunun çıktısını hata dosyasına koymak için\n" -"kullanılan dizi" - -msgid "name of the errorfile for the 'makeprg' command" -msgstr "'makeprg' komutu için hata dosyası adı" - -msgid "program used for the \":grep\" command" -msgstr "\":grep\" komutu için kullanılan program" - -msgid "list of formats for output of 'grepprg'" -msgstr "'grepprg' çıktısı için kullanılan biçimlerin listesi" - -msgid "encoding of the \":make\" and \":grep\" output" -msgstr "\":make\" ve \":grep\" çıktılarının kodlaması" - -msgid "function to display text in the quickfix window" -msgstr "hızlı düzelt içinde metin düzenlemek için işlev" - -msgid "system specific" -msgstr "sisteme özel" - -msgid "use forward slashes in file names; for Unix-like shells" -msgstr "dosya adlarında eğik çizgi kullan; Unix tarzı kabuklar için" - -msgid "specifies slash/backslash used for completion" -msgstr "tamamlama için kullanılan eğik/ters eğik çizgiyi belirler" - -msgid "language specific" -msgstr "dile özel ayarlar" - -msgid "specifies the characters in a file name" -msgstr "bir dosya adındaki karakterleri belirtir" - -msgid "specifies the characters in an identifier" -msgstr "bir tanımlayıcıdaki karakterleri belirler" - -msgid "specifies the characters in a keyword" -msgstr "bir anahtar sözcükteki karakterleri belirler" - -msgid "specifies printable characters" -msgstr "yazdırılabilir karakterleri belirler" - -msgid "specifies escape characters in a string" -msgstr "bir dizideki kaçış karakterlerini belirler" - -msgid "display the buffer right-to-left" -msgstr "arabelleği sağdan sola görüntüle" - -msgid "when to edit the command-line right-to-left" -msgstr "komut satırının ne zaman sağdan sola düzenleneceği" - -msgid "insert characters backwards" -msgstr "karakterleri geriye doğru ekle" - -msgid "allow CTRL-_ in Insert and Command-line mode to toggle 'revins'" -msgstr "" -"'revins' açıp kapatmak için Ekleme ve Komut Satırı kipinde\n" -"CTRL-_ izin ver" - -msgid "the ASCII code for the first letter of the Hebrew alphabet" -msgstr "İbran abecesinin ilk harfinin ASCII kodu" - -msgid "use Hebrew keyboard mapping" -msgstr "İbranca klavye eşlemlemesini kullan" - -msgid "use phonetic Hebrew keyboard mapping" -msgstr "fonetik İbranca klavye eşlemlemesini kullan" - -msgid "prepare for editing Arabic text" -msgstr "Arapça metni düzenleme için hazırlan" - -msgid "perform shaping of Arabic characters" -msgstr "Arapça karakterlerin şekillendirmesini gerçekleştir" - -msgid "terminal will perform bidi handling" -msgstr "sağdan sola yazımı uçbirim gerçekleştirecek" - -msgid "name of a keyboard mapping" -msgstr "bir klavye eşlemlemesinin adı" - -msgid "list of characters that are translated in Normal mode" -msgstr "Normal kipte çevrilen karakterlerin listesi" - -msgid "apply 'langmap' to mapped characters" -msgstr "eşlemlenen karakterlere 'langmap' uygula" - -msgid "when set never use IM; overrules following IM options" -msgstr "" -"ayarlandığında hiçbir zaman IM kullanma; aşağıdaki IM seçeneklerini geçersiz " -"kılar" - -msgid "in Insert mode: 1: use :lmap; 2: use IM; 0: neither" -msgstr "Ekleme kipinde: 1: :lmap kullan; 2; IM kullan; 0: hiçbiri" - -msgid "input method style, 0: on-the-spot, 1: over-the-spot" -msgstr "girdi yöntemi stili, 0: on-the-spot, 1: over-the-spot" - -msgid "entering a search pattern: 1: use :lmap; 2: use IM; 0: neither" -msgstr "bir arama dizgisi gir: 1: :lmap kullan; 2: IM kullan; 0: hiçbiri" - -msgid "when set always use IM when starting to edit a command line" -msgstr "" -"ayarlandığında, bir komut satırı düzenlemeye başlarken her zaman IM kullan" - -msgid "function to obtain IME status" -msgstr "IME durumunu elde etmek için işlev" - -msgid "function to enable/disable IME" -msgstr "IME'yi etkinleştirmek/devre dışı bırakmak için işlev" - -msgid "multi-byte characters" -msgstr "çoklu bayt karakterler" - -msgid "" -"character encoding used in Vim: \"latin1\", \"utf-8\",\n" -"\"euc-jp\", \"big5\", etc." -msgstr "" -"Vim'de kullanılan karakter kodlamaları: \"latin1\", \"utf-8\",\n" -"\"euc-jp\", \"big5\" gibi" - -msgid "character encoding for the current file" -msgstr "geçerli dosya için karakter kodlaması" - -msgid "automatically detected character encodings" -msgstr "karakter kodlamasını kendiliğinden algıla" - -msgid "character encoding used by the terminal" -msgstr "uçbirim tarafından kullanılan karakter kodlaması" - -msgid "expression used for character encoding conversion" -msgstr "karakter kodlaması dönüşümü için kullanılan ifade" - -msgid "delete combining (composing) characters on their own" -msgstr "birleştiren (oluşturucu) karakterleri kendi başına kullan" - -msgid "maximum number of combining (composing) characters displayed" -msgstr "en çok görüntülenen birleştiren (oluşturucu) karakterlerin sayısı" - -msgid "key that activates the X input method" -msgstr "X girdi yöntemini etkinleştiren düğme" - -msgid "width of ambiguous width characters" -msgstr "belirsiz genişlikli karakterlerin genişliği" - -msgid "emoji characters are full width" -msgstr "emoji karakterleri tam genişliklidir" - -msgid "various" -msgstr "çeşitli" - -msgid "" -"when to use virtual editing: \"block\", \"insert\", \"all\"\n" -"and/or \"onemore\"" -msgstr "" -"ne zaman sanal düzenleme kullanmalı: \"block\", \"insert\",\n" -"\"all\" ve/veya \"onemore\"" - -msgid "list of autocommand events which are to be ignored" -msgstr "yok sayılacak otokomut olayları" - -msgid "load plugin scripts when starting up" -msgstr "başlarken eklenti betiklerini yükle" - -msgid "enable reading .vimrc/.exrc/.gvimrc in the current directory" -msgstr "geçerli dizinde .vimrc/.exrc/.gvimrc okumayı etkinleştir" - -msgid "safer working with script files in the current directory" -msgstr "geçerli dizinde betik dosyalarıyla daha güvenli çalışma" - -msgid "use the 'g' flag for \":substitute\"" -msgstr "\":substitute\" için 'g' bayrağını kullan" - -msgid "'g' and 'c' flags of \":substitute\" toggle" -msgstr "\":substitute\" açma/kapama düğmesinin 'g' ve 'c' bayrakları" - -msgid "allow reading/writing devices" -msgstr "aygıtları okumaya/yazmaya izin ver" - -msgid "maximum depth of function calls" -msgstr "işlev çağrılarının en çok derinliği" - -msgid "list of words that specifies what to put in a session file" -msgstr "bir oturum dosyasına ne koyulacağını belirleyen sözcükler listesi" - -msgid "list of words that specifies what to save for :mkview" -msgstr ":mkview için neyin kaydedileceğini belirleyen sözcükler listesi" - -msgid "directory where to store files with :mkview" -msgstr ":mkview ile dosyaların depolanacağı dizin" - -msgid "list that specifies what to write in the viminfo file" -msgstr "viminfo dosyasına nelerin yazılacağını belirleyen liste" - -msgid "file name used for the viminfo file" -msgstr "viminfo dosyası için kullanılan dosya adı" - -msgid "what happens with a buffer when it's no longer in a window" -msgstr "bir arabellek artık bir pencerede değilken ne olacağı" - -msgid "empty, \"nofile\", \"nowrite\", \"quickfix\", etc.: type of buffer" -msgstr "boş, \"nofile\", \"nowrite\", \"quickfix\" vb.: arabellek türü" - -msgid "whether the buffer shows up in the buffer list" -msgstr "arabelleğin, arabellek listesinde görünüp görünmeyeceği" - -msgid "set to \"msg\" to see all error messages" -msgstr "tüm hata iletilerini görmek için \"msg\" olarak ayarlayın" - -msgid "whether to show the signcolumn" -msgstr "işaret sütununun görünüp görünmeyeceği" - -msgid "interval in milliseconds between polls for MzScheme threads" -msgstr "MzScheme iş parçacıkları için anketler arasındaki süre (milisaniye)" - -msgid "name of the Lua dynamic library" -msgstr "Lua devingen kitaplığının adı" - -msgid "name of the Perl dynamic library" -msgstr "Perl devingen kitaplığının adı" - -msgid "whether to use Python 2 or 3" -msgstr "Python 2 veya 3 mü kullanılacağı" - -msgid "name of the Python 2 dynamic library" -msgstr "Python 2 devingen kitaplığının adı" +msgid "Already only one window" +msgstr "Zaten tek pencere" -msgid "name of the Python 2 home directory" -msgstr "Python 2 ev dizininin adı" +msgid "E441: There is no preview window" +msgstr "E441: Önizleme penceresi yok" -msgid "name of the Python 3 dynamic library" -msgstr "Python 3 devingen kitaplığının adı" +msgid "E442: Can't split topleft and botright at the same time" +msgstr "E442: Üst sol ve alt sağ pencereler aynı anda bölünemez" -msgid "name of the Python 3 home directory" -msgstr "Python 3 ev dizininin adı" +msgid "E443: Cannot rotate when another window is split" +msgstr "E443: Başka bir pencere bölünmüşken döndürme yapılamaz" -msgid "name of the Ruby dynamic library" -msgstr "Ruby devingen kitaplığının adı" +msgid "E444: Cannot close last window" +msgstr "E444: Son pencere kapatılamıyor" -msgid "name of the Tcl dynamic library" -msgstr "Tcl devingen kitaplığının adı" +msgid "E814: Cannot close window, only autocmd window would remain" +msgstr "E814: Pencere kapatılamıyor, yalnızca otokomut penceresi açık kalır" -msgid "name of the MzScheme dynamic library" -msgstr "MzScheme devingen kitaplığının adı" +msgid "E445: Other window contains changes" +msgstr "E445: Diğer pencerede değişiklikler var" -msgid "name of the MzScheme GC dynamic library" -msgstr "MzScheme GC devingen kitaplığının adı" +msgid "E446: No file name under cursor" +msgstr "E446: İmleç altında bir dosya adı yok" diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po index 9a8cd38f5e..70c1389d7f 100644 --- a/src/nvim/po/zh_CN.UTF-8.po +++ b/src/nvim/po/zh_CN.UTF-8.po @@ -32,7 +32,7 @@ msgstr "选项参数后的内容无效" #: ../api/private/helpers.c:204 msgid "internal error: unknown option type" -msgstr "" +msgstr "内部错误:未知的选项类型" #: ../buffer.c:92 msgid "[Location List]" @@ -44,7 +44,7 @@ msgstr "[Quickfix 列表]" #: ../buffer.c:94 msgid "E855: Autocommands caused command to abort" -msgstr "" +msgstr "E855: 自动命令导致命令被停止" #: ../buffer.c:135 msgid "E82: Cannot allocate any buffer, exiting..." @@ -336,7 +336,7 @@ msgstr "E105: 不是在脚本文件中使用 :loadkeymap " #: ../digraph.c:1821 msgid "E791: Empty keymap entry" -msgstr "" +msgstr "E791: 空的键位映射项" #: ../edit.c:82 msgid " Keyword completion (^N^P)" @@ -401,11 +401,11 @@ msgstr "已到段落结尾" #: ../edit.c:101 msgid "E839: Completion function changed window" -msgstr "" +msgstr "E839: 补全函数更改了窗口" #: ../edit.c:102 msgid "E840: Completion function deleted text" -msgstr "" +msgstr "E840: 补全函数删除了文本" #: ../edit.c:1847 msgid "'dictionary' option is empty" @@ -1209,6 +1209,9 @@ msgid "" "It may still be possible to write it.\n" "Do you wish to try?" msgstr "" +"此文件权限 \"%s\" 是只读的。\n" +"它仍然有可能被写入。\n" +"你想继续尝试吗?" #: ../ex_cmds.c:2451 #, fuzzy, c-format @@ -1519,7 +1522,7 @@ msgstr "环境变量" #: ../ex_cmds2.c:2773 msgid "error handler" -msgstr "" +msgstr "错误的处理程序" #: ../ex_cmds2.c:3020 msgid "W15: Warning: Wrong line separator, ^M may be missing" @@ -1836,7 +1839,7 @@ msgstr "捕获异常: %s" #: ../ex_eval.c:676 #, c-format msgid "%s made pending" -msgstr "" +msgstr "%s 待定" #: ../ex_eval.c:679 #, fuzzy, c-format @@ -1846,7 +1849,7 @@ msgstr " 已返回\n" #: ../ex_eval.c:683 #, c-format msgid "%s discarded" -msgstr "" +msgstr "%s 舍弃" #: ../ex_eval.c:708 msgid "Exception" @@ -2005,7 +2008,7 @@ msgstr "E199: 活动窗口或缓冲区已被删除" #: ../file_search.c:203 msgid "E854: path too long for completion" -msgstr "" +msgstr "E854: 补全用的路径太长" #: ../file_search.c:446 #, c-format @@ -2432,7 +2435,7 @@ msgstr "--已删除--" #: ../fileio.c:5732 #, c-format msgid "auto-removing autocommand: %s <buffer=%d>" -msgstr "" +msgstr "自动删除自动命令: %s <buffer=%d>" #. the group doesn't exist #: ../fileio.c:5772 @@ -2671,7 +2674,7 @@ msgstr "E49: 无效的滚动大小" #: ../globals.h:1021 msgid "E901: Job table is full" -msgstr "" +msgstr "E901: 任务表已经满" #: ../globals.h:1024 #, c-format @@ -3215,6 +3218,7 @@ msgstr "%-5s: %-30s (用法: %s)" #: ../if_cscope.c:1155 msgid "" "\n" +" a: Find assignments to this symbol\n" " c: Find functions calling this function\n" " d: Find functions called by this function\n" " e: Find this egrep pattern\n" @@ -3224,6 +3228,16 @@ msgid "" " s: Find this C symbol\n" " t: Find this text string\n" msgstr "" +"\n" +" a: 搜索对此符号的赋值\n" +" c: 搜索调用此函数的函数\n" +" d: 搜索此函数调用的函数\n" +" e: 搜索此 egrep 模式\n" +" f: 搜索此文件\n" +" g: 搜索此定义\n" +" i: 搜索包含此文件的文件\n" +" s: 搜索此 C 符号\n" +" t: 搜索此文本字符串\n" #: ../if_cscope.c:1226 msgid "E568: duplicate cscope database not added" @@ -3455,7 +3469,7 @@ msgstr "-N\t\t\t不完全兼容传统的 Vi: 'nocompatible'" #: ../main.c:2215 msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" -msgstr "" +msgstr "-V[N][fname]\t\t详细 [level N] [log messages to fname]" #: ../main.c:2216 msgid "-D\t\t\tDebugging mode" @@ -3547,7 +3561,7 @@ msgstr "-W <scriptout>\t将所有输入的命令写入到文件 <scriptout>" #: ../main.c:2240 msgid "--startuptime <file>\tWrite startup timing messages to <file>" -msgstr "" +msgstr "--startuptime <file>\t将启动时间信息写入到文件 <file>" #: ../main.c:2242 msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" @@ -3738,7 +3752,7 @@ msgstr "" #: ../memline.c:945 msgid " has been damaged (page size is smaller than minimum value).\n" -msgstr "" +msgstr "已损坏(页面大小小于最小值)。\n" #: ../memline.c:974 #, c-format @@ -3844,7 +3858,7 @@ msgstr "再运行 diff 与原文件比较以检查是否有改变)\n" #: ../memline.c:1254 msgid "Recovery completed. Buffer contents equals file contents." -msgstr "" +msgstr "恢复完成。缓冲区内容与文件内容相同。" #: ../memline.c:1255 #, fuzzy @@ -4553,7 +4567,7 @@ msgstr "" #: ../option.c:1238 msgid "%<%f%h%m%=Page %N" -msgstr "" +msgstr "%<%f%h%m%=页 %N" #: ../option.c:1574 msgid "Thanks for flying Vim" @@ -4574,7 +4588,7 @@ msgstr "E520: 不允许在 modeline 中使用" #: ../option.c:2815 msgid "E846: Key code not set" -msgstr "" +msgstr "E846: 未设置键位代码" #: ../option.c:2924 msgid "E521: Number required after =" @@ -4599,11 +4613,11 @@ msgstr "E589: 'backupext' 和 'patchmode' 相等" #: ../option.c:3964 msgid "E834: Conflicts with value of 'listchars'" -msgstr "" +msgstr "E834: 与'listchars'中的值发生冲突" #: ../option.c:3966 msgid "E835: Conflicts with value of 'fillchars'" -msgstr "" +msgstr "E835: 与'fillchars'中的值冲突" #: ../option.c:4163 msgid "E524: Missing colon" @@ -4978,49 +4992,50 @@ msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " "used " msgstr "" +"E864: \\%#= 后面只能是0,1,或者2。自动引擎将会被使用" #: ../regexp_nfa.c:239 msgid "E865: (NFA) Regexp end encountered prematurely" -msgstr "" +msgstr "E865: (NFA) 过早地遇到了正则表达式的结尾" #: ../regexp_nfa.c:240 #, c-format msgid "E866: (NFA regexp) Misplaced %c" -msgstr "" +msgstr "E866: (NFA regexp) %c 放错了位置" #: ../regexp_nfa.c:242 #, c-format msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" -msgstr "" +msgstr "E877: (NFA regexp) 不可用的字符类: %<PRId64>" #: ../regexp_nfa.c:1261 #, c-format msgid "E867: (NFA) Unknown operator '\\z%c'" -msgstr "" +msgstr "E867: (NFA) 未知的操作符 '\\z%c'" #: ../regexp_nfa.c:1387 #, c-format msgid "E867: (NFA) Unknown operator '\\%%%c'" -msgstr "" +msgstr "E867: (NFA) 未知的操作符 '\\%%%c'" #: ../regexp_nfa.c:1802 #, c-format msgid "E869: (NFA) Unknown operator '\\@%c'" -msgstr "" +msgstr "E869: (NFA) 未知的操作符 '\\@%c'" #: ../regexp_nfa.c:1831 msgid "E870: (NFA regexp) Error reading repetition limits" -msgstr "" +msgstr "E870: (NFA regexp) 读取重复限制时出错" #. Can't have a multi follow a multi. #: ../regexp_nfa.c:1895 msgid "E871: (NFA regexp) Can't have a multi follow a multi !" -msgstr "" +msgstr "E871: (NFA regexp) 不能多个跟多个!" #. Too many `(' #: ../regexp_nfa.c:2037 msgid "E872: (NFA regexp) Too many '('" -msgstr "" +msgstr "E872: (NFA regexp) 太多 '('" #: ../regexp_nfa.c:2042 #, fuzzy @@ -5029,31 +5044,32 @@ msgstr "E50: 太多 \\z(" #: ../regexp_nfa.c:2066 msgid "E873: (NFA regexp) proper termination error" -msgstr "" +msgstr "E873: (NFA regexp) 未适当终止" #: ../regexp_nfa.c:2599 msgid "E874: (NFA) Could not pop the stack !" -msgstr "" +msgstr "E874: (NFA) 无法出栈!" #: ../regexp_nfa.c:3298 msgid "" "E875: (NFA regexp) (While converting from postfix to NFA), too many states " "left on stack" -msgstr "" +msgstr "E875: (NFA regexp) (从后缀转换到 NFA 时),栈上遗留了太多状态" #: ../regexp_nfa.c:3302 msgid "E876: (NFA regexp) Not enough space to store the whole NFA " -msgstr "" +msgstr "E876: (NFA regexp) 没有足够的空间存储整个NFA " #: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869 msgid "" "Could not open temporary log file for writing, displaying on stderr ... " msgstr "" +"无法打开临时日志文件进行写入,显示在 stderr 中..." #: ../regexp_nfa.c:4840 #, c-format msgid "(NFA) COULD NOT OPEN %s !" -msgstr "" +msgstr "(NFA) 不能打开 %s !" #: ../regexp_nfa.c:6049 #, fuzzy @@ -5218,6 +5234,9 @@ msgid "" "# Last %sSearch Pattern:\n" "~" msgstr "" +"\n" +"# 最后 %s搜索模式:\n" +"~" #: ../spell.c:951 msgid "E759: Format error in spell file" @@ -5314,14 +5333,16 @@ msgstr "%s 第 %d 行,在使用标志后出现 FLAG: %s" msgid "" "Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line " "%d" -msgstr "" +msgstr "在 PFX 项之后定义 COMPOUNDFORBIDFLAG (%s 第%d行)可能会给出错误的结果" +"%d" #: ../spell.c:4731 #, c-format msgid "" "Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line " "%d" -msgstr "" +msgstr "在 PFX 项之后定义 COMPOUNDPERMITFLAG (%s 第%d行)可能会给出错误的结果" +"%d" #: ../spell.c:4747 #, fuzzy, c-format @@ -5486,7 +5507,7 @@ msgstr "读取单词文件 %s ……" #: ../spell.c:6155 #, c-format msgid "Duplicate /encoding= line ignored in %s line %d: %s" -msgstr "" +msgstr "%s 第 %ld 行,重复的 /encoding= 行已被忽略: %s" #: ../spell.c:6159 #, c-format @@ -5641,17 +5662,17 @@ msgstr "E778: 看起来不像是 .sug 文件: %s" #: ../spell.c:9282 #, c-format msgid "E779: Old .sug file, needs to be updated: %s" -msgstr "" +msgstr "E779: 旧的.sug 文件,需要更新: %s" #: ../spell.c:9286 #, c-format msgid "E780: .sug file is for newer version of Vim: %s" -msgstr "" +msgstr "E780: .sug 文件适用于较新的vim 版本: %s" #: ../spell.c:9295 #, c-format msgid "E781: .sug file doesn't match .spl file: %s" -msgstr "" +msgstr "E781: .sug 文件不能匹配 .spl 文件: %s" #: ../spell.c:9305 #, fuzzy, c-format @@ -5662,7 +5683,7 @@ msgstr "E47: 读取错误文件失败" #. file. #: ../spell.c:11575 msgid "E783: duplicate char in MAP entry" -msgstr "" +msgstr "E783: MAP 条目中有重复的字符" #: ../syntax.c:266 msgid "No Syntax items defined for this buffer" @@ -5927,7 +5948,7 @@ msgstr "W18: 组名中含有无效字符" #: ../syntax.c:7448 msgid "E849: Too many highlight and syntax groups" -msgstr "" +msgstr "E849: 高亮和语法组过多" #: ../tag.c:104 msgid "E555: at bottom of tag stack" @@ -6002,7 +6023,7 @@ msgstr "查找 tag 文件 %s" #: ../tag.c:1545 msgid "Ignoring long line in tags file" -msgstr "" +msgstr "忽略较长的行在 tags 文件中" #: ../tag.c:1915 #, c-format @@ -6094,25 +6115,25 @@ msgstr "E212: 无法打开并写入文件" #: ../undo.c:717 #, c-format msgid "E825: Corrupted undo file (%s): %s" -msgstr "" +msgstr "E825: 已损坏的撤销文件 (%s): %s" #: ../undo.c:1039 msgid "Cannot write undo file in any directory in 'undodir'" -msgstr "" +msgstr "不能写入撤销文件到 'undodir' 中的任何文件夹" #: ../undo.c:1074 #, c-format msgid "Will not overwrite with undo file, cannot read: %s" -msgstr "" +msgstr "不能重写撤销文件, 不可读取: %s" #: ../undo.c:1092 #, c-format msgid "Will not overwrite, this is not an undo file: %s" -msgstr "" +msgstr "这个文件: %s 不是撤销文件,不可以重写" #: ../undo.c:1108 msgid "Skipping undo file write, nothing to undo" -msgstr "" +msgstr "跳过写入撤销文件,没有任何撤销" #: ../undo.c:1121 #, fuzzy, c-format @@ -6127,7 +6148,7 @@ msgstr "E297: 交换文件写入错误" #: ../undo.c:1280 #, c-format msgid "Not reading undo file, owner differs: %s" -msgstr "" +msgstr "不能读取撤销文件, 所有者不同: %s" #: ../undo.c:1292 #, fuzzy, c-format @@ -6151,7 +6172,7 @@ msgstr "E484: 无法打开文件 %s" #: ../undo.c:1328 msgid "File contents changed, cannot use undo info" -msgstr "" +msgstr "文件内容已改变,不能使用撤销信息" #: ../undo.c:1497 #, fuzzy, c-format @@ -6218,7 +6239,7 @@ msgstr "无可撤销" #: ../undo.c:2330 msgid "number changes when saved" -msgstr "" +msgstr " 编号 变更 时间 保存" #: ../undo.c:2360 #, fuzzy, c-format diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 625fd15886..4accddfce0 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -19,6 +19,7 @@ #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/highlight.h" +#include "nvim/insexpand.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/menu.h" @@ -1105,7 +1106,10 @@ void pum_show_popupmenu(vimmenu_T *menu) ui_flush(); int c = vgetc(); - if (c == ESC || c == Ctrl_C) { + + // Bail out when typing Esc, CTRL-C or some callback or <expr> mapping + // closed the popup menu. + if (c == ESC || c == Ctrl_C || pum_array == NULL) { break; } else if (c == CAR || c == NL) { // enter: select current item, if any, and close diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 2138437b29..9b46fad67a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -270,10 +270,8 @@ static qf_delq_T *qf_delq_head = NULL; static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T *state, qffields_T *fields) { - int status; - // Get the next line from a file/buffer/list/string - status = qf_get_nextline(state); + int status = qf_get_nextline(state); if (status != QF_OK) { return status; } @@ -547,9 +545,7 @@ static void free_efm_list(efm_T **efm_first) /// a regular expression pattern. static size_t efm_regpat_bufsz(char *efm) { - size_t sz; - - sz = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); + size_t sz = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); for (int i = FMT_PATTERNS - 1; i >= 0;) { sz += STRLEN(fmt_pat[i--].pattern); } @@ -581,10 +577,8 @@ static int efm_option_part_len(char *efm) /// the parsed 'errorformat' option. static efm_T *parse_efm_option(char *efm) { - efm_T *fmt_ptr = NULL; efm_T *fmt_first = NULL; efm_T *fmt_last = NULL; - int len; // Get some space to modify the format string into. size_t sz = efm_regpat_bufsz(efm); @@ -592,7 +586,7 @@ static efm_T *parse_efm_option(char *efm) while (efm[0] != NUL) { // Allocate a new eformat structure and put it at the end of the list - fmt_ptr = (efm_T *)xcalloc(1, sizeof(efm_T)); + efm_T *fmt_ptr = (efm_T *)xcalloc(1, sizeof(efm_T)); if (fmt_first == NULL) { // first one fmt_first = fmt_ptr; } else { @@ -601,7 +595,7 @@ static efm_T *parse_efm_option(char *efm) fmt_last = fmt_ptr; // Isolate one part in the 'errorformat' option - len = efm_option_part_len(efm); + int len = efm_option_part_len(efm); if (efm_to_regpat(efm, len, fmt_ptr, fmtstr) == FAIL) { goto parse_efm_error; @@ -649,19 +643,13 @@ static int qf_get_next_str_line(qfstate_T *state) { // Get the next line from the supplied string char *p_str = state->p_str; - char *p; - size_t len; if (*p_str == NUL) { // Reached the end of the string return QF_END_OF_INPUT; } - p = vim_strchr(p_str, '\n'); - if (p != NULL) { - len = (size_t)(p - p_str) + 1; - } else { - len = STRLEN(p_str); - } + char *p = vim_strchr(p_str, '\n'); + size_t len = (p != NULL) ? (size_t)(p - p_str) + 1 : STRLEN(p_str); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); @@ -684,7 +672,6 @@ static int qf_get_next_str_line(qfstate_T *state) static int qf_get_next_list_line(qfstate_T *state) { listitem_T *p_li = state->p_li; - size_t len; // Get the next line from the supplied list while (p_li != NULL @@ -698,7 +685,7 @@ static int qf_get_next_list_line(qfstate_T *state) return QF_END_OF_INPUT; } - len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string); + size_t len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -716,17 +703,14 @@ static int qf_get_next_list_line(qfstate_T *state) /// Get the next string from state->buf. static int qf_get_next_buf_line(qfstate_T *state) { - char *p_buf = NULL; - size_t len; - // Get the next line from the supplied buffer if (state->buflnum > state->lnumlast) { return QF_END_OF_INPUT; } - p_buf = (char *)ml_get_buf(state->buf, state->buflnum, false); + char *p_buf = (char *)ml_get_buf(state->buf, state->buflnum, false); state->buflnum += 1; - len = STRLEN(p_buf); + size_t len = STRLEN(p_buf); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -741,8 +725,6 @@ static int qf_get_next_buf_line(qfstate_T *state) /// Get the next string from file state->fd. static int qf_get_next_file_line(qfstate_T *state) { - size_t growbuflen; - retry: errno = 0; if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) { @@ -765,7 +747,7 @@ retry: // Copy the read part of the line, excluding null-terminator memcpy(state->growbuf, IObuff, IOSIZE - 1); - growbuflen = state->linelen; + size_t growbuflen = state->linelen; for (;;) { errno = 0; @@ -1071,16 +1053,11 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu linenr_T lnumlast, const char *restrict qf_title, char *restrict enc) FUNC_ATTR_NONNULL_ARG(1) { - qf_list_T *qfl; qfstate_T state = { 0 }; qffields_T fields = { 0 }; - qfline_T *old_last = NULL; - bool adding = false; static efm_T *fmt_first = NULL; - char *efm; static char *last_efm = NULL; int retval = -1; // default: return error flag - int status; // Do not used the cached buffer, it may have been wiped out. XFREE_CLEAR(qf_last_bufname); @@ -1090,6 +1067,9 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu goto qf_init_end; } + qf_list_T *qfl; + qfline_T *old_last = NULL; + bool adding = false; if (newlist || qf_idx == qi->qf_listcount) { // make place for a new list qf_new_list(qi, qf_title); @@ -1104,6 +1084,8 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu } } + char *efm; + // Use the local value of 'errorformat' if it's set. if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) { efm = (char *)buf->b_p_efm; @@ -1136,7 +1118,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu // Read the lines in the error file one by one. // Try to recognize one of the error formats in each line. while (!got_int) { - status = qf_init_process_nextline(qfl, fmt_first, &state, &fields); + int status = qf_init_process_nextline(qfl, fmt_first, &state, &fields); if (status == QF_END_OF_INPUT) { // end of input break; } @@ -1223,9 +1205,6 @@ static qf_list_T *qf_get_curlist(qf_info_T *qi) /// the new list is added. static void qf_new_list(qf_info_T *qi, const char *qf_title) { - int i; - qf_list_T *qfl; - // If the current entry is not the last entry, delete entries beyond // the current entry. This makes it possible to browse in a tree-like // way with ":grep". @@ -1237,14 +1216,14 @@ static void qf_new_list(qf_info_T *qi, const char *qf_title) // Otherwise, add a new entry. if (qi->qf_listcount == LISTCOUNT) { qf_free(&qi->qf_lists[0]); - for (i = 1; i < LISTCOUNT; i++) { + for (int i = 1; i < LISTCOUNT; i++) { qi->qf_lists[i - 1] = qi->qf_lists[i]; } qi->qf_curlist = LISTCOUNT - 1; } else { qi->qf_curlist = qi->qf_listcount++; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); memset(qfl, 0, sizeof(qf_list_T)); qf_store_title(qfl, qf_title); qfl->qfl_type = qi->qfl_type; @@ -1255,14 +1234,12 @@ static void qf_new_list(qf_info_T *qi, const char *qf_title) /// Return the matched value in "fields->namebuf". static int qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int prefix) { - char c; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } // Expand ~/file and $HOME/file to full path. - c = (char)(*rmp->endp[midx]); + char c = (char)(*rmp->endp[midx]); *rmp->endp[midx] = NUL; expand_env(rmp->startp[midx], (char_u *)fields->namebuf, CMDBUFFSIZE); *rmp->endp[midx] = (char_u)c; @@ -1360,12 +1337,10 @@ static void qf_parse_fmt_plus(const char *linebuf, size_t linelen, qffields_T *f /// Return the matched value in "fields->errmsg". static int qf_parse_fmt_m(regmatch_T *rmp, int midx, qffields_T *fields) { - size_t len; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } - len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); + size_t len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); if (len >= fields->errmsglen) { // len + null terminator fields->errmsg = xrealloc(fields->errmsg, len + 1); @@ -1390,13 +1365,11 @@ static int qf_parse_fmt_r(regmatch_T *rmp, int midx, char **tail) /// Return the matched value in "fields->col". static int qf_parse_fmt_p(regmatch_T *rmp, int midx, qffields_T *fields) { - char *match_ptr; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } fields->col = 0; - for (match_ptr = (char *)rmp->startp[midx]; (char_u *)match_ptr != rmp->endp[midx]; + for (char *match_ptr = (char *)rmp->startp[midx]; (char_u *)match_ptr != rmp->endp[midx]; match_ptr++) { fields->col++; if (*match_ptr == TAB) { @@ -1425,12 +1398,10 @@ static int qf_parse_fmt_v(regmatch_T *rmp, int midx, qffields_T *fields) /// Return the matched value in "fields->pattern". static int qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields) { - size_t len; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } - len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); + size_t len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); if (len > CMDBUFFSIZE - 5) { len = CMDBUFFSIZE - 5; } @@ -1446,14 +1417,11 @@ static int qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields) /// Return the matched value in "fields->module". static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) { - size_t len; - size_t dsize; - if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) { return QF_FAIL; } - len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); - dsize = STRLEN(fields->module) + len + 1; + size_t len = (size_t)(rmp->endp[midx] - rmp->startp[midx]); + size_t dsize = STRLEN(fields->module) + len + 1; if (dsize > CMDBUFFSIZE) { dsize = CMDBUFFSIZE; } @@ -1489,9 +1457,6 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc qffields_T *fields, int qf_multiline, int qf_multiscan, char **tail) { char idx = fmt_ptr->prefix; - int i; - int midx; - int status; if ((idx == 'C' || idx == 'Z') && !qf_multiline) { return QF_FAIL; @@ -1505,9 +1470,9 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc // 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. - for (i = 0; i < FMT_PATTERNS; i++) { - status = QF_OK; - midx = (int)fmt_ptr->addr[i]; + for (int i = 0; i < FMT_PATTERNS; i++) { + int status = QF_OK; + int midx = (int)fmt_ptr->addr[i]; if (i == 0 && midx > 0) { // %f status = qf_parse_fmt_f(regmatch, midx, fields, idx); } else if (i == FMT_PATTERN_M) { @@ -1537,10 +1502,6 @@ static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatc static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qffields_T *fields, int qf_multiline, int qf_multiscan, char **tail) { - regmatch_T regmatch; - int status = QF_FAIL; - int r; - if (qf_multiscan && vim_strchr("OPQ", fmt_ptr->prefix) == NULL) { return QF_FAIL; } @@ -1560,11 +1521,13 @@ static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qf fields->type = 0; *tail = NULL; + regmatch_T regmatch; // Always ignore case when looking for a matching error. regmatch.rm_ic = true; regmatch.regprog = fmt_ptr->prog; - r = vim_regexec(®match, linebuf, (colnr_T)0); + int r = vim_regexec(®match, linebuf, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; + int status = QF_FAIL; if (r) { status = qf_parse_match(linebuf, linelen, fmt_ptr, ®match, fields, qf_multiline, qf_multiscan, tail); @@ -1689,9 +1652,7 @@ static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields) /// Queue location list stack delete request. static void locstack_queue_delreq(qf_info_T *qi) { - qf_delq_T *q; - - q = xmalloc(sizeof(qf_delq_T)); + qf_delq_T *q = xmalloc(sizeof(qf_delq_T)); q->qi = qi; q->next = qf_delq_head; qf_delq_head = q; @@ -1722,10 +1683,7 @@ static void wipe_qf_buffer(qf_info_T *qi) /// Free a location list stack static void ll_free_all(qf_info_T **pqi) { - int i; - qf_info_T *qi; - - qi = *pqi; + qf_info_T *qi = *pqi; if (qi == NULL) { return; } @@ -1744,7 +1702,7 @@ static void ll_free_all(qf_info_T **pqi) // If the quickfix window buffer is loaded, then wipe it wipe_qf_buffer(qi); - for (i = 0; i < qi->qf_listcount; i++) { + for (int i = 0; i < qi->qf_listcount; i++) { qf_free(qf_get_list(qi, i)); } xfree(qi); @@ -1754,16 +1712,14 @@ static void ll_free_all(qf_info_T **pqi) /// Free all the quickfix/location lists in the stack. void qf_free_all(win_T *wp) { - int i; - qf_info_T *qi = &ql_info; - if (wp != NULL) { // location list ll_free_all(&wp->w_llist); ll_free_all(&wp->w_llist_ref); } else { // quickfix list - for (i = 0; i < qi->qf_listcount; i++) { + qf_info_T *qi = &ql_info; + for (int i = 0; i < qi->qf_listcount; i++) { qf_free(qf_get_list(qi, i)); } } @@ -1837,7 +1793,6 @@ static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, in char vis_col, char *pattern, int nr, char type, char valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); - qfline_T **lastp; // pointer to qf_last or NULL if (bufnum != 0) { buf_T *buf = buflist_findnr(bufnum); @@ -1873,7 +1828,7 @@ static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, in qfp->qf_type = type; qfp->qf_valid = valid; - lastp = &qfl->qf_last; + qfline_T **lastp = &qfl->qf_last; if (qf_list_empty(qfl)) { // first element in the list qfl->qf_start = qfp; @@ -2060,16 +2015,10 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl) void copy_loclist_stack(win_T *from, win_T *to) FUNC_ATTR_NONNULL_ALL { - qf_info_T *qi; - // When copying from a location list window, copy the referenced // location list. For other windows, copy the location list for // that window. - if (IS_LL_WINDOW(from)) { - qi = from->w_llist_ref; - } else { - qi = from->w_llist; - } + qf_info_T *qi = IS_LL_WINDOW(from) ? from->w_llist_ref : from->w_llist; if (qi == NULL) { // no location list to copy return; @@ -2213,14 +2162,12 @@ static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_fi // stack is empty static char *qf_pop_dir(struct dir_stack_T **stackptr) { - struct dir_stack_T *ds_ptr; - // TODO(vim): Should we check if dirbuf is the directory on top of the stack? // What to do if it isn't? // pop top element and free it if (*stackptr != NULL) { - ds_ptr = *stackptr; + struct dir_stack_T *ds_ptr = *stackptr; *stackptr = (*stackptr)->next; xfree(ds_ptr->dirname); xfree(ds_ptr); @@ -2262,17 +2209,13 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr) /// qf_guess_filepath will return NULL. static char *qf_guess_filepath(qf_list_T *qfl, char *filename) { - struct dir_stack_T *ds_ptr; - struct dir_stack_T *ds_tmp; - char *fullname; - // no dirs on the stack - there's nothing we can do if (qfl->qf_dir_stack == NULL) { return NULL; } - ds_ptr = qfl->qf_dir_stack->next; - fullname = NULL; + struct dir_stack_T *ds_ptr = qfl->qf_dir_stack->next; + char *fullname = NULL; while (ds_ptr) { xfree(fullname); fullname = concat_fnames(ds_ptr->dirname, filename, true); @@ -2288,7 +2231,7 @@ static char *qf_guess_filepath(qf_list_T *qfl, char *filename) // clean up all dirs we already left while (qfl->qf_dir_stack->next != ds_ptr) { - ds_tmp = qfl->qf_dir_stack->next; + struct dir_stack_T *ds_tmp = qfl->qf_dir_stack->next; qfl->qf_dir_stack->next = qfl->qf_dir_stack->next->next; xfree(ds_tmp->dirname); xfree(ds_tmp); @@ -2392,13 +2335,11 @@ static qfline_T *get_nth_valid_entry(qf_list_T *qfl, int errornr, int dir, int * { qfline_T *qf_ptr = qfl->qf_ptr; int qf_idx = qfl->qf_index; - qfline_T *prev_qf_ptr; - int prev_index; char *err = e_no_more_items; while (errornr--) { - prev_qf_ptr = qf_ptr; - prev_index = qf_idx; + qfline_T *prev_qf_ptr = qf_ptr; + int prev_index = qf_idx; if (dir == FORWARD || dir == FORWARD_FILE) { qf_ptr = get_next_valid_entry(qfl, qf_ptr, &qf_idx, dir); @@ -2802,11 +2743,9 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int /// a search pattern. static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char *qf_pattern) { - linenr_T i; - if (qf_pattern == NULL) { // Go to line with error, unless qf_lnum is 0. - i = qf_lnum; + linenr_T i = qf_lnum; if (i > 0) { if (i > curbuf->b_ml.ml_line_count) { i = curbuf->b_ml.ml_line_count; @@ -2933,14 +2872,11 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int forceit, int prev_winid, int *opened_window, int openfold, int print_message) { - buf_T *old_curbuf; - linenr_T old_lnum; - int retval = OK; - // If there is a file name, read the wanted file if needed, and check // autowrite etc. - old_curbuf = curbuf; - old_lnum = curwin->w_cursor.lnum; + buf_T *old_curbuf = curbuf; + linenr_T old_lnum = curwin->w_cursor.lnum; + int retval = OK; if (qf_ptr->qf_fnum != 0) { retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, prev_winid, @@ -2984,18 +2920,9 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) // If 'newwin' is true, then open the file in a new window. static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, bool newwin) { - qf_list_T *qfl; - qfline_T *qf_ptr; - qfline_T *old_qf_ptr; - int qf_index; - int old_qf_index; char *old_swb = (char *)p_swb; unsigned old_swb_flags = swb_flags; - int prev_winid; - int opened_window = false; - int print_message = true; const bool old_KeyTyped = KeyTyped; // getting file may reset it - int retval = OK; if (qi == NULL) { qi = &ql_info; @@ -3008,12 +2935,12 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo incr_quickfix_busy(); - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); - qf_ptr = qfl->qf_ptr; - old_qf_ptr = qf_ptr; - qf_index = qfl->qf_index; - old_qf_index = qf_index; + qfline_T *qf_ptr = qfl->qf_ptr; + qfline_T *old_qf_ptr = qf_ptr; + int qf_index = qfl->qf_index; + int old_qf_index = qf_index; qf_ptr = qf_get_entry(qfl, errornr, dir, &qf_index); if (qf_ptr == NULL) { @@ -3024,15 +2951,14 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo qfl->qf_index = qf_index; qfl->qf_ptr = qf_ptr; - if (qf_win_pos_update(qi, old_qf_index)) { - // No need to print the error message if it's visible in the error - // window - print_message = false; - } - prev_winid = curwin->handle; + // No need to print the error message if it's visible in the error window + bool print_message = !qf_win_pos_update(qi, old_qf_index); + + int prev_winid = curwin->handle; - retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window); + int opened_window = false; + int retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window); if (retval == FAIL) { goto failed; } @@ -3085,13 +3011,11 @@ static int qfLineAttr; /// quickfix list. static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) { - char *fname; - buf_T *buf; - - fname = NULL; + char *fname = NULL; if (qfp->qf_module != NULL && *qfp->qf_module != NUL) { vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx, qfp->qf_module); } else { + buf_T *buf; if (qfp->qf_fnum != 0 && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) { fname = buf->b_fname; @@ -3147,13 +3071,27 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) } msg_puts(" "); + char_u *tbuf = IObuff; + size_t tbuflen = IOSIZE; + size_t len = STRLEN(qfp->qf_text) + 3; + + if (len > IOSIZE) { + tbuf = xmalloc(len); + tbuflen = len; + } + // Remove newlines and leading whitespace from the text. For an // unrecognized line keep the indent, the compiler may mark a word - // with ^^^^. */ + // with ^^^^. qf_fmt_text((fname != NULL || qfp->qf_lnum != 0) ? skipwhite(qfp->qf_text) : qfp->qf_text, - (char *)IObuff, IOSIZE); - msg_prt_line(IObuff, false); + (char *)tbuf, (int)tbuflen); + msg_prt_line(tbuf, false); + + if (tbuf != IObuff) { + xfree(tbuf); + } + ui_flush(); // show one line at a time } @@ -3161,17 +3099,11 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) // ":llist": list all locations void qf_list(exarg_T *eap) { - qf_list_T *qfl; - qfline_T *qfp; - int i; - int idx1 = 1; - int idx2 = -1; char *arg = eap->arg; - int all = eap->forceit; // if not :cl!, only show - // recognised errors - qf_info_T *qi; + int all = eap->forceit; // if not :cl!, only show recognised errors + qf_info_T *qi = qf_cmd_get_stack(eap, true); - if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { + if (qi == NULL) { return; } @@ -3185,11 +3117,14 @@ void qf_list(exarg_T *eap) arg++; plus = true; } + int idx1 = 1; + int idx2 = -1; if (!get_list_range((char_u **)&arg, &idx1, &idx2) || *arg != NUL) { emsg(_(e_trailing)); return; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); + int i; if (plus) { i = qfl->qf_index; idx2 = i + idx1; @@ -3225,6 +3160,7 @@ void qf_list(exarg_T *eap) if (qfl->qf_nonevalid) { all = true; } + qfline_T *qfp; FOR_ALL_QFL_ITEMS(qfl, qfp, i) { if ((qfp->qf_valid || all) && idx1 <= i && i <= idx2) { qf_list_entry(qfp, i, i == qfl->qf_index); @@ -3312,17 +3248,12 @@ static void qf_msg(qf_info_T *qi, int which, char *lead) void qf_age(exarg_T *eap) { qf_info_T *qi; - int count; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; } - if (eap->addr_count != 0) { - count = (int)eap->line2; - } else { - count = 1; - } + int count = (eap->addr_count != 0) ? (int)eap->line2 : 1; while (count--) { if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder) { if (qi->qf_curlist == 0) { @@ -3346,7 +3277,6 @@ void qf_age(exarg_T *eap) void qf_history(exarg_T *eap) { qf_info_T *qi = qf_cmd_get_stack(eap, false); - int i; if (eap->addr_count > 0) { if (qi == NULL) { @@ -3369,7 +3299,7 @@ void qf_history(exarg_T *eap) if (qf_stack_empty(qi)) { msg(_("No entries")); } else { - for (i = 0; i < qi->qf_listcount; i++) { + for (int i = 0; i < qi->qf_listcount; i++) { qf_msg(qi, i, i == qi->qf_curlist ? "> " : " "); } } @@ -3379,13 +3309,11 @@ void qf_history(exarg_T *eap) /// associated with the list like context and title are not freed. static void qf_free_items(qf_list_T *qfl) { - qfline_T *qfp; - qfline_T *qfpnext; bool stop = false; while (qfl->qf_count && qfl->qf_start != NULL) { - qfp = qfl->qf_start; - qfpnext = qfp->qf_next; + qfline_T *qfp = qfl->qf_start; + qfline_T *qfpnext = qfp->qf_next; if (!stop) { xfree(qfp->qf_module); xfree(qfp->qf_text); @@ -3438,11 +3366,7 @@ static void qf_free(qf_list_T *qfl) bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after) { - int i; - qfline_T *qfp; - int idx; qf_info_T *qi = &ql_info; - bool found_one = false; int buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; if (!(curbuf->b_has_qf_entry & buf_has_flag)) { @@ -3455,7 +3379,10 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, qi = wp->w_llist; } - for (idx = 0; idx < qi->qf_listcount; idx++) { + int i; + qfline_T *qfp; + bool found_one = false; + for (int idx = 0; idx < qi->qf_listcount; idx++) { qf_list_T *qfl = qf_get_list(qi, idx); if (!qf_list_empty(qfl)) { FOR_ALL_QFL_ITEMS(qfl, qfp, i) { @@ -3495,7 +3422,6 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, // 1 x "" :helpgrep static char *qf_types(int c, int nr) { - static char buf[20]; static char cc[3]; char *p; @@ -3520,6 +3446,7 @@ static char *qf_types(int c, int nr) return p; } + static char buf[20]; snprintf((char *)buf, sizeof(buf), "%s %3d", p, nr); return buf; } @@ -3558,17 +3485,15 @@ void qf_view_result(bool split) void ex_cwindow(exarg_T *eap) { qf_info_T *qi; - qf_list_T *qfl; - win_T *win; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); // Look for an existing quickfix window. - win = qf_find_win(qi); + win_T *win = qf_find_win(qi); // If a quickfix window is open but we have no errors to display, // close the window. If a quickfix window is not open, then open @@ -3588,7 +3513,6 @@ void ex_cwindow(exarg_T *eap) // ":lclose": close the window showing the location list void ex_cclose(exarg_T *eap) { - win_T *win = NULL; qf_info_T *qi; if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { @@ -3596,7 +3520,7 @@ void ex_cclose(exarg_T *eap) } // Find existing quickfix window and close it. - win = qf_find_win(qi); + win_T *win = qf_find_win(qi); if (win != NULL) { win_close(win, false, false); } @@ -3726,10 +3650,6 @@ static void qf_set_title_var(qf_list_T *qfl) void ex_copen(exarg_T *eap) { qf_info_T *qi; - qf_list_T *qfl; - int height; - int status = FAIL; - int lnum; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; @@ -3737,6 +3657,7 @@ void ex_copen(exarg_T *eap) incr_quickfix_busy(); + int height; if (eap->addr_count != 0) { height = (int)eap->line2; } else { @@ -3745,6 +3666,7 @@ void ex_copen(exarg_T *eap) reset_VIsual_and_resel(); // stop Visual mode // Find an existing quickfix window, or open a new one. + int status = FAIL; if (cmdmod.cmod_tab == 0) { status = qf_goto_cwindow(qi, eap->addr_count != 0, height, cmdmod.cmod_split & WSP_VERT); @@ -3756,11 +3678,11 @@ void ex_copen(exarg_T *eap) } } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); qf_set_title_var(qfl); // Save the current index here, as updating the quickfix buffer may free // the quickfix list - lnum = qfl->qf_index; + int lnum = qfl->qf_index; // Fill the buffer with the quickfix list. qf_fill_buffer(qfl, curbuf, NULL, curwin->handle); @@ -3825,14 +3747,13 @@ linenr_T qf_current_entry(win_T *wp) /// Return TRUE if there is a quickfix window. /// /// @param old_qf_index previous qf_index or zero -static int qf_win_pos_update(qf_info_T *qi, int old_qf_index) +static bool qf_win_pos_update(qf_info_T *qi, int old_qf_index) { - win_T *win; int qf_index = qf_get_curlist(qi)->qf_index; // Put the cursor on the current error in the quickfix window, so that // it's viewable. - win = qf_find_win(qi); + win_T *win = qf_find_win(qi); if (win != NULL && qf_index <= win->w_buffer->b_ml.ml_line_count && old_qf_index != qf_index) { @@ -3909,14 +3830,12 @@ static buf_T *qf_find_buf(qf_info_T *qi) // Process the 'quickfixtextfunc' option value. bool qf_process_qftf_option(void) { - typval_T *tv; - Callback cb; - if (p_qftf == NULL || *p_qftf == NUL) { callback_free(&qftf_cb); return true; } + typval_T *tv; if (*p_qftf == '{') { // Lambda expression tv = eval_expr((char *)p_qftf); @@ -3930,6 +3849,7 @@ bool qf_process_qftf_option(void) tv->vval.v_string = (char *)vim_strsave(p_qftf); } + Callback cb; if (!callback_from_typval(&cb, tv)) { tv_free(tv); return false; @@ -3961,16 +3881,13 @@ static void qf_update_win_titlevar(qf_info_T *qi) // Find the quickfix buffer. If it exists, update the contents. static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) { - buf_T *buf; - win_T *win; - aco_save_T aco; - // Check if a buffer for the quickfix list exists. Update it. - buf = qf_find_buf(qi); + buf_T *buf = qf_find_buf(qi); if (buf != NULL) { linenr_T old_line_count = buf->b_ml.ml_line_count; int qf_winid = 0; + win_T *win; if (IS_LL_STACK(qi)) { if (curwin->w_llist == qi) { win = curwin; @@ -3983,6 +3900,8 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) qf_winid = (int)win->handle; } + aco_save_T aco; + if (old_last == NULL) { // set curwin/curbuf to buf and save a few things aucmd_prepbuf(&aco, buf); @@ -4013,14 +3932,13 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli char *dirname, char *qftf_str, bool first_bufline) FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5) { - int len; - buf_T *errbuf; - // If the 'quickfixtextfunc' function returned a non-empty custom string // for this entry, then use it. if (qftf_str != NULL && *qftf_str != NUL) { STRLCPY(IObuff, qftf_str, IOSIZE); } else { + buf_T *errbuf; + int len; if (qfp->qf_module != NULL) { STRLCPY(IObuff, qfp->qf_module, IOSIZE); len = (int)STRLEN(IObuff); @@ -4109,8 +4027,6 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long args[0].v_type = VAR_DICT; args[0].vval.v_dict = dict; - qftf_list = NULL; - if (callback_call(cb, 1, args, &rettv)) { if (rettv.v_type == VAR_LIST) { qftf_list = rettv.vval.v_list; @@ -4132,11 +4048,7 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int qf_winid) FUNC_ATTR_NONNULL_ARG(2) { - linenr_T lnum; - qfline_T *qfp; const bool old_KeyTyped = KeyTyped; - list_T *qftf_list = NULL; - listitem_T *qftf_li = NULL; if (old_last == NULL) { if (buf != curbuf) { @@ -4153,11 +4065,12 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // Check if there is anything to display if (qfl != NULL) { char dirname[MAXPATHL]; - int prev_bufnr = -1; - bool invalid_val = false; *dirname = NUL; + linenr_T lnum; + qfline_T *qfp; + // Add one line for each error if (old_last == NULL) { qfp = qfl->qf_start; @@ -4171,8 +4084,11 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q lnum = buf->b_ml.ml_line_count; } - qftf_list = call_qftf_func(qfl, qf_winid, lnum + 1, (long)qfl->qf_count); - qftf_li = tv_list_first(qftf_list); + list_T *qftf_list = call_qftf_func(qfl, qf_winid, lnum + 1, (long)qfl->qf_count); + listitem_T *qftf_li = tv_list_first(qftf_list); + + int prev_bufnr = -1; + bool invalid_val = false; while (lnum < qfl->qf_count) { char *qftf_str = NULL; @@ -4349,10 +4265,6 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname) // Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd" void ex_make(exarg_T *eap) { - char *fname; - win_T *wp = NULL; - qf_info_T *qi = &ql_info; - int res; char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc; // Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal". @@ -4369,12 +4281,13 @@ void ex_make(exarg_T *eap) } } + win_T *wp = NULL; if (is_loclist_cmd(eap->cmdidx)) { wp = curwin; } autowrite_all(); - fname = get_mef_name(); + char *fname = get_mef_name(); if (fname == NULL) { return; } @@ -4386,10 +4299,12 @@ void ex_make(exarg_T *eap) incr_quickfix_busy(); - res = qf_init(wp, fname, (eap->cmdidx != CMD_make - && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm, - (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd), - qf_cmdtitle(*eap->cmdlinep), enc); + int res = qf_init(wp, fname, (eap->cmdidx != CMD_make + && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm, + (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd), + qf_cmdtitle(*eap->cmdlinep), enc); + + qf_info_T *qi = &ql_info; if (wp != NULL) { qi = GET_LOC_LIST(wp); if (qi == NULL) { @@ -4423,7 +4338,6 @@ cleanup: // Returns NULL for error. static char *get_mef_name(void) { - char *p; char *name; static int start = -1; static int off = 0; @@ -4436,6 +4350,8 @@ static char *get_mef_name(void) return name; } + char *p; + for (p = p_mef; *p; ++p) { if (p[0] == '#' && p[1] == '#') { break; @@ -4484,7 +4400,6 @@ size_t qf_get_size(exarg_T *eap) size_t qf_get_valid_size(exarg_T *eap) { qf_info_T *qi; - qf_list_T *qfl; if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { return 0; @@ -4495,7 +4410,7 @@ size_t qf_get_valid_size(exarg_T *eap) qfline_T *qfp; int i; assert(qf_get_curlist(qi)->qf_count >= 0); - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); FOR_ALL_QFL_ITEMS(qfl, qfp, i) { if (!qfp->qf_valid) { continue; @@ -4894,12 +4809,10 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl, int bnr, const pos_T *pos bool linewise, int *errornr) FUNC_ATTR_NONNULL_ALL { - qfline_T *qfp; - *errornr = 0; // Find the first entry in this file - qfp = qf_find_first_entry_in_buf(qfl, bnr, errornr); + qfline_T *qfp = qf_find_first_entry_in_buf(qfl, bnr, errornr); if (qfp == NULL) { return NULL; // no entry in this file } @@ -4997,48 +4910,39 @@ static int qf_find_nth_adj_entry(qf_list_T *qfl, int bnr, pos_T *pos, linenr_T n /// ":lafter" and ":lbefore" commands void ex_cbelow(exarg_T *eap) { - qf_info_T *qi; - qf_list_T *qfl; - int dir; - int buf_has_flag; - if (eap->addr_count > 0 && eap->line2 <= 0) { emsg(_(e_invrange)); return; } // Check whether the current buffer has any quickfix entries - if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow - || eap->cmdidx == CMD_cbefore || eap->cmdidx == CMD_cafter) { - buf_has_flag = BUF_HAS_QF_ENTRY; - } else { - buf_has_flag = BUF_HAS_LL_ENTRY; - } + int buf_has_flag = (eap->cmdidx == CMD_cabove + || eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_cbefore + || eap->cmdidx == CMD_cafter) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; + if (!(curbuf->b_has_qf_entry & buf_has_flag)) { emsg(_(e_quickfix)); return; } + qf_info_T *qi; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { return; } - qfl = qf_get_curlist(qi); + qf_list_T *qfl = qf_get_curlist(qi); // check if the list has valid errors if (!qf_list_has_valid_entries(qfl)) { emsg(_(e_quickfix)); return; } - if (eap->cmdidx == CMD_cbelow - || eap->cmdidx == CMD_lbelow - || eap->cmdidx == CMD_cafter - || eap->cmdidx == CMD_lafter) { - // Forward motion commands - dir = FORWARD; - } else { - dir = BACKWARD; - } + // Forward motion commands + int dir = (eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_lbelow + || eap->cmdidx == CMD_cafter + || eap->cmdidx == CMD_lafter) ? FORWARD : BACKWARD; pos_T pos = curwin->w_cursor; // A quickfix entry column number is 1 based whereas cursor column @@ -5425,7 +5329,7 @@ static int vgr_process_args(exarg_T *eap, vgr_args_T *args) } // Parse the list of arguments, wildcards have already been expanded. - if (get_arglist_exp((char_u *)p, &args->fcount, (char_u ***)&args->fnames, true) == FAIL) { + if (get_arglist_exp((char_u *)p, &args->fcount, &args->fnames, true) == FAIL) { return FAIL; } if (args->fcount == 0) { @@ -5601,12 +5505,12 @@ void ex_vimgrep(exarg_T *eap) int status = vgr_process_files(wp, qi, &args, &redraw_for_dummy, &first_match_buf, &target_dir); if (status != OK) { - FreeWild(args.fcount, (char_u **)args.fnames); + FreeWild(args.fcount, args.fnames); decr_quickfix_busy(); goto theend; } - FreeWild(args.fcount, (char_u **)args.fnames); + FreeWild(args.fcount, args.fnames); qf_list_T *qfl = qf_get_curlist(qi); qfl->qf_nonevalid = false; @@ -5689,18 +5593,14 @@ static void restore_start_dir(char *dirname_start) /// @return NULL if it fails. static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resulting_dir) { - buf_T *newbuf; - bufref_T newbufref; - bufref_T newbuf_to_wipe; - int failed = true; - aco_save_T aco; - int readfile_result; - // Allocate a buffer without putting it in the buffer list. - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); if (newbuf == NULL) { return NULL; } + + int failed = true; + bufref_T newbufref; set_bufref(&newbufref, newbuf); // Init the options. @@ -5711,6 +5611,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin // Make sure this buffer isn't wiped out by autocommands. newbuf->b_locked++; // set curwin/curbuf to buf and save a few things + aco_save_T aco; aucmd_prepbuf(&aco, newbuf); // Need to set the filename for autocommands. @@ -5723,10 +5624,11 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin // work. curbuf->b_flags &= ~BF_DUMMY; + bufref_T newbuf_to_wipe; newbuf_to_wipe.br_buf = NULL; - readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0, - (linenr_T)MAXLNUM, NULL, - READ_NEW | READ_DUMMY, false); + int readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0, + (linenr_T)MAXLNUM, NULL, + READ_NEW | READ_DUMMY, false); newbuf->b_locked--; if (readfile_result == OK && !got_int @@ -5830,11 +5732,8 @@ static void unload_dummy_buffer(buf_T *buf, char *dirname_start) /// to 'list'. Returns OK on success. static int get_qfline_items(qfline_T *qfp, list_T *list) { - char buf[2]; - int bufnum; - // Handle entries with a non-existing buffer number. - bufnum = qfp->qf_fnum; + int bufnum = qfp->qf_fnum; if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) { bufnum = 0; } @@ -5842,6 +5741,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) dict_T *const dict = tv_dict_alloc(); tv_list_append_dict(list, dict); + char buf[2]; buf[0] = qfp->qf_type; buf[1] = NUL; if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL @@ -5882,9 +5782,6 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list) { qf_info_T *qi = qi_arg; - qf_list_T *qfl; - qfline_T *qfp; - int i; if (qi == NULL) { qi = &ql_info; @@ -5908,11 +5805,13 @@ int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *li return FAIL; } - qfl = qf_get_list(qi, qf_idx); + qf_list_T *qfl = qf_get_list(qi, qf_idx); if (qf_list_empty(qfl)) { return FAIL; } + qfline_T *qfp; + int i; FOR_ALL_QFL_ITEMS(qfl, qfp, i) { if (eidx > 0) { if (eidx == i) { @@ -5949,11 +5848,11 @@ enum { static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict) { int status = FAIL; - char *errorformat = p_efm; - dictitem_T *efm_di; // Only a List value is supported if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { + char *errorformat = p_efm; + dictitem_T *efm_di; // If errorformat is supplied then use it, otherwise use the 'efm' // option setting if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) { @@ -6253,11 +6152,9 @@ static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict) int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) { qf_info_T *qi = &ql_info; - qf_list_T *qfl; dictitem_T *di = NULL; int status = OK; int qf_idx = INVALID_QFIDX; - int eidx = 0; if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) { return qf_get_list_from_lines(what, di, retdict); @@ -6278,7 +6175,8 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) return qf_getprop_defaults(qi, flags, wp != NULL, retdict); } - qfl = qf_get_list(qi, qf_idx); + qf_list_T *qfl = qf_get_list(qi, qf_idx); + int eidx = 0; // If an entry index is specified, use that if ((di = tv_dict_find(what, S_LEN("idx"))) != NULL) { @@ -6665,9 +6563,6 @@ static int qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, const dictitem_T *di static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char *title) FUNC_ATTR_NONNULL_ALL { - qf_list_T *qfl; - dictitem_T *di; - int retval = FAIL; bool newlist = action == ' ' || qf_stack_empty(qi); int qf_idx = qf_setprop_get_qfidx(qi, what, action, &newlist); if (qf_idx == INVALID_QFIDX) { // List not found @@ -6680,7 +6575,9 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char qf_idx = qi->qf_curlist; } - qfl = qf_get_list(qi, qf_idx); + qf_list_T *qfl = qf_get_list(qi, qf_idx); + dictitem_T *di; + int retval = FAIL; if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { retval = qf_setprop_title(qi, qf_idx, what, di); } @@ -6761,7 +6658,6 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi) int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what) { qf_info_T *qi = &ql_info; - int retval = OK; if (wp != NULL) { qi = ll_get_or_alloc_list(wp); @@ -6781,6 +6677,7 @@ int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what incr_quickfix_busy(); + int retval = OK; if (what != NULL) { retval = qf_set_properties(qi, what, action, title); } else { @@ -6911,14 +6808,7 @@ static int cbuffer_process_args(exarg_T *eap, buf_T **bufp, linenr_T *line1, lin // ":[range]lgetbuffer [bufnr]" command. void ex_cbuffer(exarg_T *eap) { - buf_T *buf = NULL; - char *au_name = NULL; - win_T *wp = NULL; - char *qf_title; - linenr_T line1; - linenr_T line2; - - au_name = cbuffer_get_auname(eap->cmdidx); + char *au_name = cbuffer_get_auname(eap->cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, true, curbuf)) { if (aborting()) { @@ -6927,13 +6817,17 @@ void ex_cbuffer(exarg_T *eap) } // Must come after autocommands. + win_T *wp = NULL; qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp); + buf_T *buf = NULL; + linenr_T line1; + linenr_T line2; if (cbuffer_process_args(eap, &buf, &line1, &line2) == FAIL) { return; } - qf_title = qf_cmdtitle(*eap->cmdlinep); + char *qf_title = qf_cmdtitle(*eap->cmdlinep); if (buf->b_sfname) { vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)", qf_title, buf->b_sfname); @@ -7001,10 +6895,7 @@ static char *cexpr_get_auname(cmdidx_T cmdidx) /// ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command. void ex_cexpr(exarg_T *eap) { - char *au_name = NULL; - win_T *wp = NULL; - - au_name = cexpr_get_auname(eap->cmdidx); + char *au_name = cexpr_get_auname(eap->cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, true, curbuf)) { if (aborting()) { @@ -7012,6 +6903,7 @@ void ex_cexpr(exarg_T *eap) } } + win_T *wp = NULL; qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp); // Evaluate the expression. When the result is a string or a list we can @@ -7060,22 +6952,10 @@ cleanup: static qf_info_T *hgr_get_ll(bool *new_ll) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { - win_T *wp; - qf_info_T *qi; - - // If the current window is a help window, then use it - if (bt_help(curwin->w_buffer)) { - wp = curwin; - } else { - // Find an existing help window - wp = qf_find_help_win(); - } + // If the current window is a help window, then use it, else find an existing help window + win_T *wp = bt_help(curwin->w_buffer) ? curwin : qf_find_help_win(); - if (wp == NULL) { // Help window not found - qi = NULL; - } else { - qi = wp->w_llist; - } + qf_info_T *qi = wp == NULL ? NULL : wp->w_llist; if (qi == NULL) { // Allocate a new location list for help text matches qi = qf_alloc_stack(QFLT_LOCATION); @@ -7151,8 +7031,7 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p // Find all "*.txt" and "*.??x" files in the "doc" directory. add_pathsep(dirname); STRCAT(dirname, "doc/*.\\(txt\\|??x\\)"); // NOLINT - if (gen_expand_wildcards(1, (char_u **)&dirname, &fcount, - (char_u ***)&fnames, EW_FILE|EW_SILENT) == OK + if (gen_expand_wildcards(1, &dirname, &fcount, &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { for (int fi = 0; fi < fcount && !got_int; fi++) { // Skip files for a different language. @@ -7166,7 +7045,7 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p hgr_search_file(qfl, fnames[fi], p_regmatch); } - FreeWild(fcount, (char_u **)fnames); + FreeWild(fcount, fnames); } } @@ -7190,7 +7069,6 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char void ex_helpgrep(exarg_T *eap) { qf_info_T *qi = &ql_info; - bool new_qi = false; char *au_name = NULL; switch (eap->cmdidx) { @@ -7212,6 +7090,7 @@ void ex_helpgrep(exarg_T *eap) char *const save_cpo = p_cpo; p_cpo = (char *)empty_option; + bool new_qi = false; if (is_loclist_cmd(eap->cmdidx)) { qi = hgr_get_ll(&new_qi); } diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index 6407ac172e..2f718e9c2e 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -156,7 +156,7 @@ void rbuffer_consumed(RBuffer *buf, size_t count) /// Use instead of rbuffer_consumed to use rbuffer in a linear, non-cyclic fashion. /// -/// This is generally usefull if we can guarantee to parse all input +/// This is generally useful if we can guarantee to parse all input /// except some small incomplete token, like when parsing msgpack. void rbuffer_consumed_compact(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 4c49d30819..fbbf904f8b 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -176,12 +176,10 @@ static int backslash_trans(int c) return c; } -/* - * Check for a character class name "[:name:]". "pp" points to the '['. - * Returns one of the CLASS_ items. CLASS_NONE means that no item was - * recognized. Otherwise "pp" is advanced to after the item. - */ -static int get_char_class(char_u **pp) +/// Check for a character class name "[:name:]". "pp" points to the '['. +/// Returns one of the CLASS_ items. CLASS_NONE means that no item was +/// recognized. Otherwise "pp" is advanced to after the item. +static int get_char_class(char **pp) { static const char *(class_names[]) = { @@ -306,7 +304,7 @@ static void init_class_tab(void) // Global work variables for vim_regcomp(). -static char_u *regparse; ///< Input-scan pointer. +static char *regparse; ///< Input-scan pointer. static int regnpar; ///< () count. static bool wants_nfa; ///< regex should use NFA engine static int regnzpar; ///< \z() count. @@ -395,11 +393,11 @@ int re_multiline(const regprog_T *prog) * Returns a character representing the class. Zero means that no item was * recognized. Otherwise "pp" is advanced to after the item. */ -static int get_equi_class(char_u **pp) +static int get_equi_class(char **pp) { int c; int l = 1; - char_u *p = *pp; + char_u *p = (char_u *)(*pp); if (p[1] == '=' && p[2] != NUL) { l = utfc_ptr2len((char *)p + 2); @@ -418,11 +416,11 @@ static int get_equi_class(char_u **pp) * "pp" is advanced to after the item. * Currently only single characters are recognized! */ -static int get_coll_element(char_u **pp) +static int get_coll_element(char **pp) { int c; int l = 1; - char_u *p = *pp; + char_u *p = (char_u *)(*pp); if (p[0] != NUL && p[1] == '.' && p[2] != NUL) { l = utfc_ptr2len((char *)p + 2); @@ -442,12 +440,10 @@ static void get_cpo_flags(void) reg_cpo_lit = vim_strchr(p_cpo, CPO_LITERAL) != NULL; } -/* - * Skip over a "[]" range. - * "p" must point to the character after the '['. - * The returned pointer is on the matching ']', or the terminating NUL. - */ -static char_u *skip_anyof(char_u *p) +/// Skip over a "[]" range. +/// "p" must point to the character after the '['. +/// The returned pointer is on the matching ']', or the terminating NUL. +static char_u *skip_anyof(char *p) { int l; @@ -458,7 +454,7 @@ static char_u *skip_anyof(char_u *p) p++; } while (*p != NUL && *p != ']') { - if ((l = utfc_ptr2len((char *)p)) > 1) { + if ((l = utfc_ptr2len(p)) > 1) { p += l; } else if (*p == '-') { p++; @@ -482,7 +478,7 @@ static char_u *skip_anyof(char_u *p) } } - return p; + return (char_u *)p; } /* @@ -512,7 +508,7 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp) } if ((p[0] == '[' && mymagic >= MAGIC_ON) || (p[0] == '\\' && p[1] == '[' && mymagic <= MAGIC_OFF)) { - p = skip_anyof(p + 1); + p = skip_anyof((char *)p + 1); if (p[0] == NUL) { break; } @@ -547,7 +543,7 @@ static int prev_at_start; // True when on the second character */ static void initchr(char_u *str) { - regparse = str; + regparse = (char *)str; prevchr_len = 0; curchr = prevprevchr = prevchr = nextchr = -1; at_start = true; @@ -560,7 +556,7 @@ static void initchr(char_u *str) */ static void save_parse_state(parse_state_T *ps) { - ps->regparse = regparse; + ps->regparse = (char_u *)regparse; ps->prevchr_len = prevchr_len; ps->curchr = curchr; ps->prevchr = prevchr; @@ -576,7 +572,7 @@ static void save_parse_state(parse_state_T *ps) */ static void restore_parse_state(parse_state_T *ps) { - regparse = ps->regparse; + regparse = (char *)ps->regparse; prevchr_len = ps->prevchr_len; curchr = ps->curchr; prevchr = ps->prevchr; @@ -598,7 +594,7 @@ static int peekchr(void) return curchr; } - switch (curchr = regparse[0]) { + switch (curchr = (uint8_t)regparse[0]) { case '.': case '[': case '~': @@ -669,7 +665,7 @@ static int peekchr(void) // '$' is only magic as the very last char and if it's in front of // either "\|", "\)", "\&", or "\n" if (reg_magic >= MAGIC_OFF) { - char_u *p = regparse + 1; + char_u *p = (char_u *)regparse + 1; bool is_magic_all = (reg_magic == MAGIC_ALL); // ignore \c \C \m \M \v \V and \Z after '$' @@ -696,7 +692,7 @@ static int peekchr(void) } break; case '\\': { - int c = regparse[1]; + int c = (uint8_t)regparse[1]; if (c == NUL) { curchr = '\\'; // trailing '\' @@ -725,13 +721,13 @@ static int peekchr(void) } else { // Next character can never be (made) magic? // Then backslashing it won't do anything. - curchr = utf_ptr2char((char *)regparse + 1); + curchr = utf_ptr2char(regparse + 1); } break; } default: - curchr = utf_ptr2char((char *)regparse); + curchr = utf_ptr2char(regparse); } return curchr; @@ -750,7 +746,7 @@ static void skipchr(void) } if (regparse[prevchr_len] != NUL) { // Exclude composing chars that utfc_ptr2len does include. - prevchr_len += utf_ptr2len((char *)regparse + prevchr_len); + prevchr_len += utf_ptr2len(regparse + prevchr_len); } regparse += prevchr_len; prev_at_start = at_start; @@ -820,8 +816,8 @@ static int64_t gethexchrs(int maxinputlen) int c; int i; - for (i = 0; i < maxinputlen; ++i) { - c = regparse[0]; + for (i = 0; i < maxinputlen; i++) { + c = (uint8_t)regparse[0]; if (!ascii_isxdigit(c)) { break; } @@ -846,8 +842,8 @@ static int64_t getdecchrs(void) int c; int i; - for (i = 0;; ++i) { - c = regparse[0]; + for (i = 0;; i++) { + c = (uint8_t)regparse[0]; if (c < '0' || c > '9') { break; } @@ -878,7 +874,7 @@ static int64_t getoctchrs(void) int i; for (i = 0; i < 3 && nr < 040; i++) { // -V536 - c = regparse[0]; + c = (uint8_t)regparse[0]; if (c < '0' || c > '7') { break; } @@ -910,7 +906,7 @@ static int read_limits(long *minval, long *maxval) regparse++; reverse = true; } - first_char = regparse; + first_char = (char_u *)regparse; *minval = getdigits_long(®parse, false, 0); if (*regparse == ',') { // There is a comma. if (ascii_isdigit(*++regparse)) { @@ -1270,8 +1266,8 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e if (reg_tofree == NULL || len >= (int)reg_tofreelen) { len += 50; // get some extra xfree(reg_tofree); - reg_tofree = xmalloc(len); - reg_tofreelen = len; + reg_tofree = xmalloc((size_t)len); + reg_tofreelen = (unsigned)len; } STRCPY(reg_tofree, rex.line); rex.input = reg_tofree + (rex.input - rex.line); @@ -1554,7 +1550,7 @@ char_u *regtilde(char_u *source, int magic, bool preview) if (reg_prev_sub != NULL) { // length = len(newsub) - 1 + len(prev_sub) + 1 prevlen = (int)STRLEN(reg_prev_sub); - tmpsub = xmalloc(STRLEN(newsub) + prevlen); + tmpsub = xmalloc(STRLEN(newsub) + (size_t)prevlen); // copy prefix len = (int)(p - newsub); // not including ~ memmove(tmpsub, newsub, (size_t)len); @@ -1587,12 +1583,10 @@ char_u *regtilde(char_u *source, int magic, bool preview) // Only change reg_prev_sub when not previewing. if (!preview) { + // Store a copy of newsub in reg_prev_sub. It is always allocated, + // because recursive calls may make the returned string invalid. xfree(reg_prev_sub); - if (newsub != source) { // newsub was allocated, just keep it - reg_prev_sub = newsub; - } else { // no ~ found, need to save newsub - reg_prev_sub = vim_strsave(newsub); - } + reg_prev_sub = vim_strsave(newsub); } return newsub; @@ -1635,7 +1629,7 @@ static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv, int arg if (s == NULL || rsm.sm_match->endp[i] == NULL) { s = NULL; } else { - s = vim_strnsave(s, rsm.sm_match->endp[i] - s); + s = vim_strnsave(s, (size_t)(rsm.sm_match->endp[i] - s)); } TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; TV_LIST_ITEM_TV(li)->vval.v_string = (char *)s; @@ -1655,7 +1649,7 @@ static void clear_submatch_list(staticList10_T *sl) /// vim_regexec_multi() match. /// /// If "flags" has REGSUB_COPY really copy into "dest[destlen]". -/// Oterwise nothing is copied, only compue the length of the result. +/// Otherwise nothing is copied, only compute the length of the result. /// /// If "flags" has REGSUB_MAGIC then behave like 'magic' is set. /// @@ -1922,7 +1916,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des iemsg("vim_regsub_both(): not enough space"); return 0; } - *dst++ = c; + *dst++ = (char_u)c; *dst++ = *src++; *dst++ = *src++; } else { @@ -2197,7 +2191,7 @@ char_u *reg_submatch(int no) if (round == 2) { STRCPY(retval + len, s); } - len += STRLEN(s); + len += (ssize_t)STRLEN(s); if (round == 2) { retval[len] = '\n'; } @@ -2215,7 +2209,7 @@ char_u *reg_submatch(int no) } if (retval == NULL) { - retval = xmalloc(len); + retval = xmalloc((size_t)len); } } } else { @@ -2223,7 +2217,7 @@ char_u *reg_submatch(int no) if (s == NULL || rsm.sm_match->endp[no] == NULL) { retval = NULL; } else { - retval = vim_strnsave(s, rsm.sm_match->endp[no] - s); + retval = vim_strnsave(s, (size_t)(rsm.sm_match->endp[no] - s)); } } @@ -2328,7 +2322,7 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags) regprog_T *prog = NULL; char_u *expr = (char_u *)expr_arg; - regexp_engine = p_re; + regexp_engine = (int)p_re; // Check for prefix "\%#=", that sets the regexp engine if (STRNCMP(expr, "\\%#=", 4) == 0) { @@ -2396,8 +2390,8 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags) if (prog != NULL) { // Store the info needed to call regcomp() again when the engine turns out // to be very slow when executing it. - prog->re_engine = regexp_engine; - prog->re_flags = re_flags; + prog->re_engine = (unsigned)regexp_engine; + prog->re_flags = (unsigned)re_flags; } return prog; @@ -2475,8 +2469,8 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool // NFA engine aborted because it's very slow, use backtracking engine instead. if (rmp->regprog->re_engine == AUTOMATIC_ENGINE && result == NFA_TOO_EXPENSIVE) { - int save_p_re = p_re; - int re_flags = rmp->regprog->re_flags; + int save_p_re = (int)p_re; + int re_flags = (int)rmp->regprog->re_flags; char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern); p_re = BACKTRACKING_ENGINE; @@ -2560,15 +2554,14 @@ long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, } rex_in_use = true; - int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, - tm, timed_out); + long result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, tm, timed_out); rmp->regprog->re_in_use = false; // NFA engine aborted because it's very slow, use backtracking engine instead. if (rmp->regprog->re_engine == AUTOMATIC_ENGINE && result == NFA_TOO_EXPENSIVE) { - int save_p_re = p_re; - int re_flags = rmp->regprog->re_flags; + int save_p_re = (int)p_re; + int re_flags = (int)rmp->regprog->re_flags; char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern); p_re = BACKTRACKING_ENGINE; @@ -2589,8 +2582,7 @@ long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, vim_regfree(prev_prog); rmp->regprog->re_in_use = true; - result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, - tm, timed_out); + result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, tm, timed_out); rmp->regprog->re_in_use = false; } diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index 272429bb91..fff5ae9b7f 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -492,7 +492,7 @@ static void regc(int b) if (regcode == JUST_CALC_SIZE) { regsize++; } else { - *regcode++ = b; + *regcode++ = (char_u)b; } } @@ -1493,7 +1493,7 @@ static char_u *regnode(int op) if (ret == JUST_CALC_SIZE) { regsize += 3; } else { - *regcode++ = op; + *regcode++ = (char_u)op; *regcode++ = NUL; // Null "next" pointer. *regcode++ = NUL; } @@ -1610,7 +1610,7 @@ static void reginsert(int op, char_u *opnd) } place = opnd; // Op node, where operand used to be. - *place++ = op; + *place++ = (char_u)op; *place++ = NUL; *place = NUL; } @@ -1637,7 +1637,7 @@ static void reginsert_nr(int op, long val, char_u *opnd) } place = opnd; // Op node, where operand used to be. - *place++ = op; + *place++ = (char_u)op; *place++ = NUL; *place++ = NUL; assert(val >= 0 && (uintmax_t)val <= UINT32_MAX); @@ -1668,7 +1668,7 @@ static void reginsert_limits(int op, long minval, long maxval, char_u *opnd) } place = opnd; // Op node, where operand used to be. - *place++ = op; + *place++ = (char_u)op; *place++ = NUL; *place++ = NUL; assert(minval >= 0 && (uintmax_t)minval <= UINT32_MAX); @@ -1689,7 +1689,7 @@ static int seen_endbrace(int refnum) // Trick: check if "@<=" or "@<!" follows, in which case // the \1 can appear before the referenced match. - for (p = regparse; *p != NUL; p++) { + for (p = (char_u *)regparse; *p != NUL; p++) { if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) { break; } @@ -2071,7 +2071,7 @@ static char_u *regatom(int *flagp) EMSG2_RET_NULL(_("E678: Invalid character after %s%%[dxouU]"), reg_magic == MAGIC_ALL); } - if (use_multibytecode(i)) { + if (use_multibytecode((int)i)) { ret = regnode(MULTIBYTECODE); } else { ret = regnode(EXACTLY); @@ -2079,7 +2079,7 @@ static char_u *regatom(int *flagp) if (i == 0) { regc(0x0a); } else { - regmbc(i); + regmbc((int)i); } regc(NUL); *flagp |= HASWIDTH; @@ -2111,8 +2111,8 @@ static char_u *regatom(int *flagp) if (ret == JUST_CALC_SIZE) { regsize += 2; } else { - *regcode++ = c; - *regcode++ = cmp; + *regcode++ = (char_u)c; + *regcode++ = (char_u)cmp; } break; } else if (c == 'l' || c == 'c' || c == 'v') { @@ -2123,7 +2123,7 @@ static char_u *regatom(int *flagp) } if (c == 'l') { if (cur) { - n = curwin->w_cursor.lnum; + n = (uint32_t)curwin->w_cursor.lnum; } ret = regnode(RE_LNUM); if (save_prev_at_start) { @@ -2131,7 +2131,7 @@ static char_u *regatom(int *flagp) } } else if (c == 'c') { if (cur) { - n = curwin->w_cursor.col; + n = (uint32_t)curwin->w_cursor.col; n++; } ret = regnode(RE_COL); @@ -2139,7 +2139,7 @@ static char_u *regatom(int *flagp) if (cur) { colnr_T vcol = 0; getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); - n = ++vcol; + n = (uint32_t)(++vcol); } ret = regnode(RE_VCOL); } @@ -2149,7 +2149,7 @@ static char_u *regatom(int *flagp) // put the number and the optional // comparator after the opcode regcode = re_put_uint32(regcode, n); - *regcode++ = cmp; + *regcode++ = (char_u)cmp; } break; } @@ -2469,7 +2469,7 @@ do_multibyte: // Need to get composing character too. for (;;) { l = utf_ptr2len((char *)regparse); - if (!utf_composinglike(regparse, regparse + l)) { + if (!utf_composinglike((char_u *)regparse, (char_u *)regparse + l)) { break; } regmbc(utf_ptr2char((char *)regparse)); @@ -2910,7 +2910,7 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags) } // Allocate space. - bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + regsize); + bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + (size_t)regsize); r->re_in_use = false; // Second pass: emit code. @@ -2938,7 +2938,7 @@ static regprog_T *bt_regcomp(char_u *expr, int re_flags) r->regflags |= RF_LOOKBH; } // Remember whether this pattern has any \z specials in it. - r->reghasz = re_has_z; + r->reghasz = (char_u)re_has_z; scan = r->program + 1; // First BRANCH. if (OP(regnext(scan)) == END) { // Only one top-level choice. scan = OPERAND(scan); @@ -3027,7 +3027,7 @@ static int coll_get_char(void) regparse--; nr = '\\'; } - return nr; + return (int)nr; } /* @@ -3505,7 +3505,7 @@ static regitem_T *regstack_push(regstate_T state, char_u *scan) rp->rs_state = state; rp->rs_scan = scan; - regstack.ga_len += sizeof(regitem_T); + regstack.ga_len += (int)sizeof(regitem_T); return rp; } @@ -3519,7 +3519,7 @@ static void regstack_pop(char_u **scan) rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1; *scan = rp->rs_scan; - regstack.ga_len -= sizeof(regitem_T); + regstack.ga_len -= (int)sizeof(regitem_T); } // Save the current subexpr to "bp", so that they can be restored @@ -3704,9 +3704,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) int mark = OPERAND(scan)[0]; int cmp = OPERAND(scan)[1]; pos_T *pos; - size_t col = REG_MULTI ? rex.input - rex.line : 0; - - // fm will be NULL if the mark is not set in reg_buf + size_t col = REG_MULTI ? (size_t)(rex.input - rex.line) : 0; fmark_T *fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, mark); // Line may have been freed, get it again. @@ -4170,7 +4168,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, &rex.reg_startpos[no], &rex.reg_startp[no]); // We simply continue and handle the result when done. @@ -4200,7 +4198,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, ®_startzpos[no], ®_startzp[no]); // We simply continue and handle the result when done. @@ -4223,7 +4221,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]); // We simply continue and handle the result when done. } @@ -4244,7 +4242,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; save_se(&rp->rs_un.sesave, ®_endzpos[no], ®_endzp[no]); // We simply continue and handle the result when done. @@ -4380,7 +4378,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; reg_save(&rp->rs_un.regsave, &backpos); next = OPERAND(scan); // We continue and handle the result when done. @@ -4396,7 +4394,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = no; + rp->rs_no = (int16_t)no; reg_save(&rp->rs_un.regsave, &backpos); next = OPERAND(scan); // We continue and handle the result when done. @@ -4465,7 +4463,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) status = RA_FAIL; } else { ga_grow(®stack, sizeof(regstar_T)); - regstack.ga_len += sizeof(regstar_T); + regstack.ga_len += (int)sizeof(regstar_T); rp = regstack_push(rst.minval <= rst.maxval ? RS_STAR_LONG : RS_STAR_SHORT, scan); if (rp == NULL) { status = RA_FAIL; @@ -4487,7 +4485,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (rp == NULL) { status = RA_FAIL; } else { - rp->rs_no = op; + rp->rs_no = (int16_t)op; reg_save(&rp->rs_un.regsave, &backpos); next = OPERAND(scan); // We continue and handle the result when done. @@ -4502,7 +4500,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) status = RA_FAIL; } else { ga_grow(®stack, sizeof(regbehind_T)); - regstack.ga_len += sizeof(regbehind_T); + regstack.ga_len += (int)sizeof(regbehind_T); rp = regstack_push(RS_BEHIND1, scan); if (rp == NULL) { status = RA_FAIL; @@ -4511,7 +4509,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) // when there is a match but we don't use it. save_subexpr(((regbehind_T *)rp) - 1); - rp->rs_no = op; + rp->rs_no = (int16_t)op; reg_save(&rp->rs_un.regsave, &backpos); // First try if what follows matches. If it does then we // check the behind match by looping. @@ -4691,7 +4689,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) case RS_BEHIND1: if (status == RA_NOMATCH) { regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); + regstack.ga_len -= (int)sizeof(regbehind_T); } else { // The stuff after BEHIND/NOBEHIND matches. Now try if // the behind part does (not) match before the current @@ -4734,7 +4732,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) restore_subexpr(((regbehind_T *)rp) - 1); } regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); + regstack.ga_len -= (int)sizeof(regbehind_T); } else { long limit; @@ -4808,7 +4806,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) } } regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); + regstack.ga_len -= (int)sizeof(regbehind_T); } } break; @@ -4819,7 +4817,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (status == RA_MATCH) { regstack_pop(&scan); - regstack.ga_len -= sizeof(regstar_T); + regstack.ga_len -= (int)sizeof(regstar_T); break; } @@ -4885,7 +4883,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out) if (status != RA_CONT) { // Failed. regstack_pop(&scan); - regstack.ga_len -= sizeof(regstar_T); + regstack.ga_len -= (int)sizeof(regstar_T); status = RA_NOMATCH; } } @@ -4976,15 +4974,13 @@ static long regtry(bt_regprog_T *prog, colnr_T col, proftime_T *tm, int *timed_o && reg_endzpos[i].lnum == reg_startzpos[i].lnum && reg_endzpos[i].col >= reg_startzpos[i].col) { re_extmatch_out->matches[i] = - vim_strnsave(reg_getline(reg_startzpos[i].lnum) - + reg_startzpos[i].col, - reg_endzpos[i].col - - reg_startzpos[i].col); + vim_strnsave(reg_getline(reg_startzpos[i].lnum) + reg_startzpos[i].col, + (size_t)(reg_endzpos[i].col - reg_startzpos[i].col)); } } else { if (reg_startzp[i] != NULL && reg_endzp[i] != NULL) { re_extmatch_out->matches[i] = - vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]); + vim_strnsave(reg_startzp[i], (size_t)(reg_endzp[i] - reg_startzp[i])); } } } diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 870af3eafc..1a5c250664 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -545,7 +545,7 @@ static char_u *nfa_get_match_text(nfa_state_T *start) return NULL; } - ret = xmalloc(len); + ret = xmalloc((size_t)len); p = start->out->out; // skip first char, it goes into regstart s = ret; while (p->c > 0) { @@ -565,7 +565,7 @@ static void realloc_post_list(void) { // For weird patterns the number of states can be very high. Increasing by // 50% seems a reasonable compromise between memory use and speed. - const size_t new_max = (post_end - post_start) * 3 / 2; + const size_t new_max = (size_t)(post_end - post_start) * 3 / 2; int *new_start = xrealloc(post_start, new_max * sizeof(int)); post_ptr = new_start + (post_ptr - post_start); post_end = new_start + new_max; @@ -1807,7 +1807,7 @@ static int nfa_regatom(void) int got_coll_char; char_u *p; char_u *endp; - char_u *old_regparse = regparse; + char_u *old_regparse = (char_u *)regparse; int extra = 0; int emit_range; int negated; @@ -1905,7 +1905,7 @@ static int nfa_regatom(void) // When '.' is followed by a composing char ignore the dot, so that // the composing char is matched here. if (c == Magic('.') && utf_iscomposing(peekchr())) { - old_regparse = regparse; + old_regparse = (char_u *)regparse; c = getchr(); goto nfa_do_multibyte; } @@ -2079,7 +2079,7 @@ static int nfa_regatom(void) } // A NUL is stored in the text as NL // TODO(vim): what if a composing character follows? - EMIT(nr == 0 ? 0x0a : nr); + EMIT(nr == 0 ? 0x0a : (int)nr); } break; @@ -2226,16 +2226,15 @@ collection: * - ranges, two characters followed by NFA_RANGE. */ - p = regparse; - endp = skip_anyof(p); + p = (char_u *)regparse; + endp = skip_anyof((char *)p); if (*endp == ']') { /* * Try to reverse engineer character classes. For example, * recognize that [0-9] stands for \d and [A-Za-z_] for \h, * and perform the necessary substitutions in the NFA. */ - int result = nfa_recognize_char_class(regparse, endp, - extra == NFA_ADD_NL); + int result = nfa_recognize_char_class((char_u *)regparse, endp, extra == NFA_ADD_NL); if (result != FAIL) { if (result >= NFA_FIRST_NL && result <= NFA_LAST_NL) { EMIT(result - NFA_ADD_NL); @@ -2244,7 +2243,7 @@ collection: } else { EMIT(result); } - regparse = endp; + regparse = (char *)endp; MB_PTR_ADV(regparse); return OK; } @@ -2269,7 +2268,7 @@ collection: } // Emit the OR branches for each character in the [] emit_range = false; - while (regparse < endp) { + while ((char_u *)regparse < endp) { int oldstartc = startc; startc = -1; got_coll_char = false; @@ -2376,7 +2375,7 @@ collection: // accepts "\t", "\e", etc., but only when the 'l' flag in // 'cpoptions' is not included. if (*regparse == '\\' - && regparse + 1 <= endp + && (char_u *)regparse + 1 <= endp && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL || (!reg_cpo_lit && vim_strchr(REGEXP_ABBR, regparse[1]) @@ -2479,7 +2478,7 @@ collection: } // skip the trailing ] - regparse = endp; + regparse = (char *)endp; MB_PTR_ADV(regparse); // Mark end of the collection. @@ -2531,7 +2530,7 @@ nfa_do_multibyte: c = utf_ptr2char((char *)old_regparse + i); } EMIT(NFA_COMPOSING); - regparse = old_regparse + plen; + regparse = (char *)old_regparse + plen; } else { c = no_Magic(c); EMIT(c); @@ -2646,7 +2645,7 @@ static int nfa_regpiece(void) EMIT(i); if (i == NFA_PREV_ATOM_JUST_BEFORE || i == NFA_PREV_ATOM_JUST_BEFORE_NEG) { - EMIT(c2); + EMIT((int)c2); } break; @@ -3850,7 +3849,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size) if (nfa_calc_size == false) { // Allocate space for the stack. Max states on the stack: "nstate". - stack = xmalloc((nstate + 1) * sizeof(Frag_T)); + stack = xmalloc((size_t)(nstate + 1) * sizeof(Frag_T)); stackp = stack; stack_end = stack + (nstate + 1); } @@ -4472,10 +4471,9 @@ static void clear_sub(regsub_T *sub) { if (REG_MULTI) { // Use 0xff to set lnum to -1 - memset(sub->list.multi, 0xff, - sizeof(struct multipos) * rex.nfa_nsubexpr); + memset(sub->list.multi, 0xff, sizeof(struct multipos) * (size_t)rex.nfa_nsubexpr); } else { - memset(sub->list.line, 0, sizeof(struct linepos) * rex.nfa_nsubexpr); + memset(sub->list.line, 0, sizeof(struct linepos) * (size_t)rex.nfa_nsubexpr); } sub->in_use = 0; } @@ -4489,13 +4487,11 @@ static void copy_sub(regsub_T *to, regsub_T *from) if (from->in_use > 0) { // Copy the match start and end positions. if (REG_MULTI) { - memmove(&to->list.multi[0], - &from->list.multi[0], - sizeof(struct multipos) * from->in_use); + memmove(&to->list.multi[0], &from->list.multi[0], + sizeof(struct multipos) * (size_t)from->in_use); } else { - memmove(&to->list.line[0], - &from->list.line[0], - sizeof(struct linepos) * from->in_use); + memmove(&to->list.line[0], &from->list.line[0], + sizeof(struct linepos) * (size_t)from->in_use); } } } @@ -4511,13 +4507,11 @@ static void copy_sub_off(regsub_T *to, regsub_T *from) if (from->in_use > 1) { // Copy the match start and end positions. if (REG_MULTI) { - memmove(&to->list.multi[1], - &from->list.multi[1], - sizeof(struct multipos) * (from->in_use - 1)); + memmove(&to->list.multi[1], &from->list.multi[1], + sizeof(struct multipos) * (size_t)(from->in_use - 1)); } else { - memmove(&to->list.line[1], - &from->list.line[1], - sizeof(struct linepos) * (from->in_use - 1)); + memmove(&to->list.line[1], &from->list.line[1], + sizeof(struct linepos) * (size_t)(from->in_use - 1)); } } } @@ -4964,7 +4958,7 @@ skip_add: // be (a lot) bigger than anticipated. if (l->n == l->len) { const int newlen = l->len * 3 / 2 + 50; - const size_t newsize = newlen * sizeof(nfa_thread_T); + const size_t newsize = (size_t)newlen * sizeof(nfa_thread_T); if ((long)(newsize >> 10) >= p_mmp) { emsg(_(e_maxmempat)); @@ -5255,7 +5249,7 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su // not enough space to move the new states, reallocate the list // and move the states to the right position const int newlen = l->len * 3 / 2 + 50; - const size_t newsize = newlen * sizeof(nfa_thread_T); + const size_t newsize = (size_t)newlen * sizeof(nfa_thread_T); if ((long)(newsize >> 10) >= p_mmp) { emsg(_(e_maxmempat)); @@ -5265,13 +5259,13 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su l->len = newlen; memmove(&(newl[0]), &(l->t[0]), - sizeof(nfa_thread_T) * listidx); + sizeof(nfa_thread_T) * (size_t)listidx); memmove(&(newl[listidx]), &(l->t[l->n - count]), - sizeof(nfa_thread_T) * count); + sizeof(nfa_thread_T) * (size_t)count); memmove(&(newl[listidx + count]), &(l->t[listidx + 1]), - sizeof(nfa_thread_T) * (l->n - count - listidx - 1)); + sizeof(nfa_thread_T) * (size_t)(l->n - count - listidx - 1)); xfree(l->t); l->t = newl; } else { @@ -5279,10 +5273,10 @@ static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *su // end to the current position memmove(&(l->t[listidx + count]), &(l->t[listidx + 1]), - sizeof(nfa_thread_T) * (l->n - listidx - 1)); + sizeof(nfa_thread_T) * (size_t)(l->n - listidx - 1)); memmove(&(l->t[listidx]), &(l->t[l->n - 1]), - sizeof(nfa_thread_T) * count); + sizeof(nfa_thread_T) * (size_t)count); } } --l->n; @@ -5621,7 +5615,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T // values and clear them. if (*listids == NULL || *listids_len < prog->nstate) { xfree(*listids); - *listids = xmalloc(sizeof(**listids) * prog->nstate); + *listids = xmalloc(sizeof(**listids) * (size_t)prog->nstate); *listids_len = prog->nstate; } nfa_save_listids(prog, *listids); @@ -5888,7 +5882,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) rex.reg_startpos[0].lnum = rex.lnum; rex.reg_startpos[0].col = col; rex.reg_endpos[0].lnum = rex.lnum; - rex.reg_endpos[0].col = s2 - rex.line; + rex.reg_endpos[0].col = (colnr_T)(s2 - rex.line); } else { rex.reg_startp[0] = rex.line + col; rex.reg_endp[0] = s2; @@ -5969,7 +5963,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm nfa_match = false; // Allocate memory for the lists of nodes. - size_t size = (prog->nstate + 1) * sizeof(nfa_thread_T); + size_t size = (size_t)(prog->nstate + 1) * sizeof(nfa_thread_T); list[0].t = xmalloc(size); list[0].len = prog->nstate + 1; list[1].t = xmalloc(size); @@ -6930,10 +6924,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm case NFA_MARK: case NFA_MARK_GT: case NFA_MARK_LT: { - fmark_T *fm; - size_t col = REG_MULTI ? rex.input - rex.line : 0; - // fm will be NULL if the mark is not set, doesn't belong to reg_buf - fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, t->state->val); + size_t col = REG_MULTI ? (size_t)(rex.input - rex.line) : 0; + fmark_T *fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, t->state->val); // Line may have been freed, get it again. if (REG_MULTI) { @@ -7371,14 +7363,14 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm, int *ti && mpos->end_col >= mpos->start_col) { re_extmatch_out->matches[i] = vim_strnsave(reg_getline(mpos->start_lnum) + mpos->start_col, - mpos->end_col - mpos->start_col); + (size_t)(mpos->end_col - mpos->start_col)); } } else { struct linepos *lpos = &subs.synt.list.line[i]; if (lpos->start != NULL && lpos->end != NULL) { re_extmatch_out->matches[i] = - vim_strnsave(lpos->start, lpos->end - lpos->start); + vim_strnsave(lpos->start, (size_t)(lpos->end - lpos->start)); } } } @@ -7569,7 +7561,7 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags) post2nfa(postfix, post_ptr, true); // allocate the regprog with space for the compiled regexp - size_t prog_size = sizeof(nfa_regprog_T) + sizeof(nfa_state_T) * (nstate - 1); + size_t prog_size = sizeof(nfa_regprog_T) + sizeof(nfa_state_T) * (size_t)(nstate - 1); prog = xmalloc(prog_size); state_ptr = prog->state; prog->re_in_use = false; @@ -7653,7 +7645,7 @@ static int nfa_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col, bool line_ rex.reg_ic = rmp->rm_ic; rex.reg_icombine = false; rex.reg_maxcol = 0; - return nfa_regexec_both(line, col, NULL, NULL); + return (int)nfa_regexec_both(line, col, NULL, NULL); } /// Matches a regexp against multiple lines. diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 045cee2439..5d28d624fe 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -74,14 +74,14 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, { char_u *tail; int num_files; - char_u **files; + char **files; int i; bool did_one = false; // Make a copy of 'runtimepath'. Invoking the callback may change the // value. char_u *rtp_copy = vim_strsave(path); - char_u *buf = xmallocz(MAXPATHL); + char *buf = xmallocz(MAXPATHL); { if (p_verbose > 10 && name != NULL) { verbose_enter(); @@ -93,7 +93,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, char_u *rtp = rtp_copy; while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) { // Copy the path from 'runtimepath' to buf[]. - copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ","); + copy_option_part((char **)&rtp, buf, MAXPATHL, ","); size_t buflen = STRLEN(buf); // Skip after or non-after directories. @@ -107,18 +107,19 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, } if (name == NULL) { - (*callback)((char *)buf, cookie); + (*callback)(buf, cookie); did_one = true; } else if (buflen + STRLEN(name) + 2 < MAXPATHL) { - add_pathsep((char *)buf); - tail = buf + STRLEN(buf); + add_pathsep(buf); + tail = (char_u *)buf + STRLEN(buf); // Loop over all patterns in "name" char_u *np = (char_u *)name; while (*np != NUL && ((flags & DIP_ALL) || !did_one)) { // Append the pattern from "name" to buf[]. - assert(MAXPATHL >= (tail - buf)); - copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t "); + assert(MAXPATHL >= (tail - (char_u *)buf)); + copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - (char_u *)buf)), + "\t "); if (p_verbose > 10) { verbose_enter(); @@ -132,7 +133,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, // Expand wildcards, invoke the callback for each match. if (gen_expand_wildcards(1, &buf, &num_files, &files, ew_flags) == OK) { for (i = 0; i < num_files; i++) { - (*callback)((char *)files[i], cookie); + (*callback)(files[i], cookie); did_one = true; if (!(flags & DIP_ALL)) { break; @@ -211,7 +212,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void { char_u *tail; int num_files; - char_u **files; + char **files; int i; bool did_one = false; @@ -263,10 +264,10 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void | (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0; // Expand wildcards, invoke the callback for each match. - char_u *(pat[]) = { buf }; + char *(pat[]) = { (char *)buf }; if (gen_expand_wildcards(1, pat, &num_files, &files, ew_flags) == OK) { for (i = 0; i < num_files; i++) { - (*callback)((char *)files[i], cookie); + (*callback)(files[i], cookie); did_one = true; if (!(flags & DIP_ALL)) { break; @@ -458,11 +459,11 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_ } int num_files; - char_u **files; - char_u *(pat[]) = { (char_u *)entry }; + char **files; + char *(pat[]) = { entry }; if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { for (int i = 0; i < num_files; i++) { - push_path(search_path, rtp_used, (char *)files[i], after); + push_path(search_path, rtp_used, files[i], after); } FreeWild(num_files, files); } @@ -488,7 +489,7 @@ static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle } } -static bool path_is_after(char_u *buf, size_t buflen) +static bool path_is_after(char *buf, size_t buflen) { // NOTE: we only consider dirs exactly matching "after" to be an AFTER dir. // vim8 considers all dirs like "foo/bar_after", "Xafter" etc, as an @@ -525,7 +526,7 @@ RuntimeSearchPath runtime_search_path_build(void) copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ","); size_t buflen = STRLEN(buf); - if (path_is_after(buf, buflen)) { + if (path_is_after((char *)buf, buflen)) { rtp_entry = cur_entry; break; } @@ -557,7 +558,7 @@ RuntimeSearchPath runtime_search_path_build(void) // "after" dirs in rtp for (; *rtp_entry != NUL;) { copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ","); - expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after(buf, STRLEN(buf))); + expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after((char *)buf, STRLEN(buf))); } // strings are not owned @@ -640,14 +641,14 @@ int source_in_path(char_u *path, char_u *name, int flags) // Expand wildcards in "pat" and invoke do_source()/nlua_exec_file() // for each match. -static void source_all_matches(char_u *pat) +static void source_all_matches(char *pat) { int num_files; - char_u **files; + char **files; if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) { for (int i = 0; i < num_files; i++) { - (void)do_source((char *)files[i], false, DOSO_NONE); + (void)do_source(files[i], false, DOSO_NONE); } FreeWild(num_files, files); } @@ -811,9 +812,9 @@ static int load_pack_plugin(bool opt, char_u *fname) char_u *pat = xmallocz(len); vim_snprintf((char *)pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT - source_all_matches(pat); + source_all_matches((char *)pat); vim_snprintf((char *)pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT - source_all_matches(pat); + source_all_matches((char *)pat); char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes"); @@ -822,9 +823,9 @@ static int load_pack_plugin(bool opt, char_u *fname) if (opt && eval_to_number((char *)cmd) > 0) { do_cmdline_cmd("augroup filetypedetect"); vim_snprintf((char *)pat, len, ftpat, ffname); - source_all_matches(pat); + source_all_matches((char *)pat); vim_snprintf((char *)pat, len, "%s/ftdetect/*.lua", ffname); // NOLINT - source_all_matches(pat); + source_all_matches((char *)pat); do_cmdline_cmd("augroup END"); } xfree(cmd); @@ -886,8 +887,8 @@ void add_pack_start_dirs(void) static bool pack_has_entries(char_u *buf) { int num_files; - char_u **files; - char_u *(pat[]) = { buf }; + char **files; + char *(pat[]) = { (char *)buf }; if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { FreeWild(num_files, files); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index e99f9b9153..0b7b58dc40 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -92,6 +92,7 @@ #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" +#include "nvim/insexpand.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/lua/executor.h" @@ -406,7 +407,7 @@ int update_screen(int type) curwin->w_redr_status = true; } } - msg_grid_set_pos(Rows - p_ch, false); + msg_grid_set_pos(Rows - (int)p_ch, false); msg_grid_invalid = false; } else if (msg_scrolled > Rows - 5) { // clearing is faster type = CLEAR; @@ -470,7 +471,7 @@ int update_screen(int type) // After disabling msgsep the grid might not have been deallocated yet, // hence we also need to check msg_grid.chars if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) { - grid_fill(&default_grid, Rows - p_ch, Rows, 0, Columns, ' ', ' ', 0); + grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0); } ui_comp_set_screen_valid(true); @@ -904,7 +905,7 @@ win_update_start: for (i = 0; i < wp->w_lines_valid; ++i) { j += wp->w_lines[i].wl_size; if (j >= wp->w_upd_rows) { - top_end = j; + top_end = (int)j; break; } } @@ -977,7 +978,7 @@ win_update_start: // Move the entries that were scrolled, disable // the entries for the lines to be redrawn. - if ((wp->w_lines_valid += j) > wp->w_grid.rows) { + if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) { wp->w_lines_valid = wp->w_grid.rows; } for (idx = wp->w_lines_valid; idx - j >= 0; idx--) { @@ -1063,8 +1064,8 @@ win_update_start: // Correct the first entry for filler lines at the top // when it won't get updated below. if (win_may_fill(wp) && bot_start > 0) { - wp->w_lines[0].wl_size = (plines_win_nofill(wp, wp->w_topline, true) - + wp->w_topfill); + wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) + + wp->w_topfill); } } } @@ -1183,7 +1184,7 @@ win_update_start: pos.lnum += cursor_above ? 1 : -1) { colnr_T t; - pos.col = STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false)); + pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false)); getvvcol(wp, &pos, NULL, NULL, &t); if (toc < t) { toc = t; @@ -1284,7 +1285,7 @@ win_update_start: } if (VIsual_active && buf == curwin->w_buffer) { - wp->w_old_visual_mode = VIsual_mode; + wp->w_old_visual_mode = (char)VIsual_mode; wp->w_old_cursor_lnum = curwin->w_cursor.lnum; wp->w_old_visual_lnum = VIsual.lnum; wp->w_old_visual_col = VIsual.col; @@ -1480,14 +1481,14 @@ win_update_start: for (;;) { // stop at last valid entry in w_lines[] if (i >= wp->w_lines_valid) { - wp->w_lines_valid = j; + wp->w_lines_valid = (int)j; break; } wp->w_lines[j] = wp->w_lines[i]; // stop at a line that won't fit if (x + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { - wp->w_lines_valid = j + 1; + wp->w_lines_valid = (int)j + 1; break; } x += wp->w_lines[j++].wl_size; @@ -1499,7 +1500,7 @@ win_update_start: } else { // j > i // move entries in w_lines[] downwards j -= i; - wp->w_lines_valid += j; + wp->w_lines_valid += (linenr_T)j; if (wp->w_lines_valid > wp->w_grid.rows) { wp->w_lines_valid = wp->w_grid.rows; } @@ -1569,13 +1570,13 @@ win_update_start: if (row > wp->w_grid.rows) { // past end of grid // we may need the size of that too long line later on if (dollar_vcol == -1) { - wp->w_lines[idx].wl_size = plines_win(wp, lnum, true); + wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true); } idx++; break; } if (dollar_vcol == -1) { - wp->w_lines[idx].wl_size = row - srow; + wp->w_lines[idx].wl_size = (uint16_t)(row - srow); } idx++; lnum += foldinfo.fi_lines + 1; @@ -1702,7 +1703,7 @@ win_update_start: // Send win_extmarks if needed for (size_t n = 0; n < kv_size(win_extmark_arr); n++) { ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, - kv_A(win_extmark_arr, n).ns_id, kv_A(win_extmark_arr, n).mark_id, + kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id, kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col); } @@ -1807,7 +1808,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i } } - int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl)); + int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, (int)hl)); if (wp->w_p_rl) { grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n, @@ -1837,7 +1838,7 @@ static bool advance_color_col(int vcol, int **color_cols) static int compute_foldcolumn(win_T *wp, int col) { int fdc = win_fdccol_count(wp); - int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw; + int wmw = wp == curwin && p_wmw == 0 ? 1 : (int)p_wmw; int wwidth = wp->w_grid.cols; if (fdc > wwidth - (col + wmw)) { @@ -1866,7 +1867,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b } goto done; } else if (*p < 0x80 && u8cc[0] == 0) { - schar_from_ascii(dest[0], *p); + schar_from_ascii(dest[0], (char)(*p)); s->prev_c = u8c; } else { if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { @@ -1923,7 +1924,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ int len = 0; bool closed = foldinfo.fi_lines > 0; // Init to all spaces. - memset(p, ' ', MAX_MCO * fdc + 1); + memset(p, ' ', MAX_MCO * (size_t)fdc + 1); level = foldinfo.fi_level; @@ -1947,7 +1948,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ } len = utf_char2bytes(symbol, (char *)&p[char_counter]); - char_counter += len; + char_counter += (size_t)len; if (first_level + i >= level) { i++; break; @@ -1957,14 +1958,14 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ if (closed) { if (symbol != 0) { // rollback previous write - char_counter -= len; - memset(&p[char_counter], ' ', len); + char_counter -= (size_t)len; + memset(&p[char_counter], ' ', (size_t)len); } len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, (char *)&p[char_counter]); - char_counter += len; + char_counter += (size_t)len; } - return MAX(char_counter + (fdc - i), (size_t)fdc); + return MAX(char_counter + (size_t)(fdc - i), (size_t)fdc); } static inline void provider_err_virt_text(linenr_T lnum, char *err) @@ -1974,7 +1975,7 @@ static inline void provider_err_virt_text(linenr_T lnum, char *err) kv_push(err_decor.virt_text, ((VirtTextChunk){ .text = provider_err, .hl_id = hl_err })); - err_decor.virt_text_width = mb_string2cells(err); + err_decor.virt_text_width = (int)mb_string2cells(err); decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0); } @@ -2118,7 +2119,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc bool has_decor = false; // this buffer has decoration int win_col_offset = 0; // offset for window columns - char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext + char_u buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext bool area_active = false; @@ -2211,7 +2212,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Check for columns to display for 'colorcolumn'. color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; if (color_cols != NULL) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } if (wp->w_p_spell @@ -2447,10 +2448,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc nextlinecol = 0; memmove(nextline, line, (size_t)v); STRMOVE(nextline + v, nextline + SPWORDLEN); - nextline_idx = v + 1; + nextline_idx = (int)v + 1; } else { // Long line, use only the last SPWORDLEN bytes. - nextlinecol = v - SPWORDLEN; + nextlinecol = (int)v - SPWORDLEN; memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512 nextline_idx = SPWORDLEN + 1; } @@ -2529,7 +2530,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // If the character fits on the screen, don't need to skip it. // Except for a TAB. if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) { - n_skip = v - vcol; + n_skip = (int)(v - vcol); } } @@ -2540,7 +2541,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (tocol <= vcol) { fromcol = 0; } else if (fromcol >= 0 && fromcol < vcol) { - fromcol = vcol; + fromcol = (int)vcol; } // When w_skipcol is non-zero, first line needs 'showbreak' @@ -2616,7 +2617,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc ptr = line + v; // "line" may have been updated } - unsigned off = 0; // Offset relative start of line + int off = 0; // Offset relative start of line int col = 0; // Visual column on screen. if (wp->w_p_rl) { // Rightleft window: process the text in the normal direction, but put @@ -2668,7 +2669,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Draw the 'foldcolumn'. Allocate a buffer, "extra" may // already be in use. xfree(p_extra_free); - p_extra_free = xmalloc(MAX_MCO * fdc + 1); + p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1); n_extra = (int)fill_foldcolumn(p_extra_free, wp, foldinfo, lnum); p_extra_free[n_extra] = NUL; p_extra = p_extra_free; @@ -2731,7 +2732,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_u *p2 = (char_u *)skipwhite((char *)extra); p2 = skiptowhite(p2) - 1; for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) { - const int t = *p1; + const char_u t = *p1; *p1 = *p2; *p2 = t; } @@ -2770,7 +2771,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_attr = 0; if (diff_hlf != (hlf_T)0) { - char_attr = win_hl_attr(wp, diff_hlf); + char_attr = win_hl_attr(wp, (int)diff_hlf); } p_extra = NULL; c_extra = ' '; @@ -2853,7 +2854,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (has_decor && row == startrow + filler_lines) { // hide virt_text on text hidden by 'nowrap' - decor_redraw_col(wp->w_buffer, vcol, off, true, &decor_state); + decor_redraw_col(wp->w_buffer, (int)vcol, off, true, &decor_state); } if (saved_n_extra) { @@ -2908,7 +2909,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (draw_state == WL_LINE && has_fold - && vcol == 0 + && col == win_col_offset && n_extra == 0 && row == startrow) { char_attr = win_hl_attr(wp, HLF_FL); @@ -2916,7 +2917,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc linenr_T lnume = lnum + foldinfo.fi_lines - 1; memset(buf_fold, ' ', FOLD_TEXT_LEN); p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); - n_extra = STRLEN(p_extra); + n_extra = (int)STRLEN(p_extra); if (p_extra != buf_fold) { xfree(p_extra_free); @@ -2992,7 +2993,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && n_extra == 0) { diff_hlf = HLF_CHD; // changed line } - line_attr = win_hl_attr(wp, diff_hlf); + line_attr = win_hl_attr(wp, (int)diff_hlf); // Overlay CursorLine onto diff-mode highlight. if (cul_attr) { line_attr = 0 != line_attr_lowprio // Low-priority CursorLine @@ -3299,7 +3300,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange); assert(tmplen <= INT_MAX); len = (int)tmplen; - word_end = v + len; + word_end = (int)v + len; /* In Insert mode only highlight a word that * doesn't touch the cursor. */ @@ -3390,7 +3391,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } if (c == TAB && n_extra + col > grid->cols) { - n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts, + n_extra = tabstop_padding((colnr_T)vcol, wp->w_buffer->b_p_ts, wp->w_buffer->b_p_vts_array) - 1; } c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; @@ -3494,7 +3495,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc vcol_adjusted = vcol - mb_charlen(sbr); } // tab amount depends on current column - tab_len = tabstop_padding(vcol_adjusted, + tab_len = tabstop_padding((colnr_T)vcol_adjusted, wp->w_buffer->b_p_ts, wp->w_buffer->b_p_vts_array) - 1; @@ -3526,8 +3527,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc len += n_extra - tab_len; } c = wp->w_p_lcs_chars.tab1; - p = xmalloc(len + 1); - memset(p, ' ', len); + p = xmalloc((size_t)len + 1); + memset(p, ' ', (size_t)len); p[len] = NUL; xfree(p_extra_free); p_extra_free = p; @@ -3657,9 +3658,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_u *p; c = *p_extra; - p = xmalloc(n_extra + 1); - memset(p, ' ', n_extra); - STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); + p = xmalloc((size_t)n_extra + 1); + memset(p, ' ', (size_t)n_extra); + STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); // NOLINT(runtime/printf) p[n_extra] = NUL; xfree(p_extra_free); p_extra_free = p_extra = p; @@ -3835,8 +3836,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } if (n != 0) { - /* At the window boundary, highlight the last character - * instead (better than nothing). */ + // At the window boundary, highlight the last character + // instead (better than nothing). off += n; col += n; } else { @@ -3881,7 +3882,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // boguscols = 0; // Disabled because value never read after this if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } bool has_virttext = false; @@ -3926,7 +3927,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc diff_hlf = HLF_CHD; } if (diff_hlf != 0) { - diff_attr = win_hl_attr(wp, diff_hlf); + diff_attr = win_hl_attr(wp, (int)diff_hlf); } int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr); @@ -3940,7 +3941,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc schar_from_ascii(linebuf_char[off], ' '); col += col_stride; if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } int col_attr = base_attr; @@ -4026,7 +4027,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // advance to the next 'colorcolumn' if (draw_color_col) { - draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols); } // Highlight the cursor column if 'cursorcolumn' is set. But don't @@ -4073,7 +4074,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (mb_utf8) { schar_from_cc(linebuf_char[off], mb_c, u8cc); } else { - schar_from_ascii(linebuf_char[off], c); + schar_from_ascii(linebuf_char[off], (char)c); } if (multi_attr) { linebuf_attr[off] = multi_attr; @@ -4217,7 +4218,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (filler_todo > 0) { int index = filler_todo - (filler_lines - n_virt_lines); if (index > 0) { - int i = kv_size(virt_lines) - index; + int i = (int)kv_size(virt_lines) - index; assert(i >= 0); int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset; draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line, @@ -4331,7 +4332,7 @@ void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_co if (item->decor.ui_watched) { // send mark position to UI col = item->win_col; - WinExtmark m = { item->ns_id, item->mark_id, win_row, col }; + WinExtmark m = { (NS)item->ns_id, item->mark_id, win_row, col }; kv_push(win_extmark_arr, m); } if (kv_size(item->decor.virt_text)) { @@ -4505,12 +4506,12 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att // full cell width? assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra))); // symbol(s) bytes + (filling spaces) (one byte each) - *n_extrap = symbol_blen + - (win_signcol_width(wp) - mb_string2cells((char *)(*pp_extra))); + *n_extrap = symbol_blen + win_signcol_width(wp) - + (int)mb_string2cells((char *)(*pp_extra)); assert(extra_size > (size_t)symbol_blen); memset(extra, ' ', extra_size); - memcpy(extra, *pp_extra, symbol_blen); + memcpy(extra, *pp_extra, (size_t)symbol_blen); *pp_extra = extra; (*pp_extra)[*n_extrap] = NUL; @@ -4533,7 +4534,7 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att void rl_mirror(char_u *str) { char_u *p1, *p2; - int t; + char_u t; for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2) { t = *p1; @@ -4763,8 +4764,7 @@ static int skip_status_match_char(expand_T *xp, char_u *s) /// If inversion is possible we use it. Else '=' characters are used. /// /// @param matches list of matches -void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, int match, - int showtail) +void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int match, int showtail) { #define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m]) int row; @@ -4788,14 +4788,14 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in return; } - buf = xmalloc(Columns * MB_MAXBYTES + 1); + buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1); if (match == -1) { // don't show match but original text match = 0; highlight = false; } // count 1 for the ending ">" - clen = status_match_len(xp, L_MATCH(match)) + 3; + clen = status_match_len(xp, (char_u *)L_MATCH(match)) + 3; if (match == 0) { first_match = 0; } else if (match < first_match) { @@ -4804,8 +4804,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in add_left = true; } else { // check if match fits on the screen - for (i = first_match; i < match; ++i) { - clen += status_match_len(xp, L_MATCH(i)) + 2; + for (i = first_match; i < match; i++) { + clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2; } if (first_match > 0) { clen += 2; @@ -4815,8 +4815,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in first_match = match; // if showing the last match, we can add some on the left clen = 2; - for (i = match; i < num_matches; ++i) { - clen += status_match_len(xp, L_MATCH(i)) + 2; + for (i = match; i < num_matches; i++) { + clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2; if ((long)clen >= Columns) { break; } @@ -4828,7 +4828,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in } if (add_left) { while (first_match > 0) { - clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2; + clen += status_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2; if ((long)clen >= Columns) { break; } @@ -4848,13 +4848,13 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in clen = len; i = first_match; - while (clen + status_match_len(xp, L_MATCH(i)) + 2 < Columns) { + while (clen + status_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) { if (i == match) { selstart = buf + len; selstart_col = clen; } - s = L_MATCH(i); + s = (char_u *)L_MATCH(i); // Check for menu separators - replace with '|' emenu = (xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES); @@ -4915,8 +4915,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in // Set 'winminheight' to zero to avoid that the window is // resized. if (lastwin->w_status_height == 0 && global_stl_height() == 0) { - save_p_ls = p_ls; - save_p_wmh = p_wmh; + save_p_ls = (int)p_ls; + save_p_wmh = (int)p_wmh; p_ls = 2; p_wmh = 0; last_status(false); @@ -4995,19 +4995,19 @@ static void win_redr_status(win_T *wp) *(p + len++) = ' '; } if (bt_help(wp->w_buffer)) { - snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Help]")); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]")); len += (int)STRLEN(p + len); } if (wp->w_p_pvw) { - snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Preview]")); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]")); len += (int)STRLEN(p + len); } if (bufIsChanged(wp->w_buffer)) { - snprintf((char *)p + len, MAXPATHL - len, "%s", "[+]"); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]"); len += (int)STRLEN(p + len); } if (wp->w_buffer->b_p_ro) { - snprintf((char *)p + len, MAXPATHL - len, "%s", _("[RO]")); + snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]")); // len += (int)STRLEN(p + len); // dead assignment } @@ -5038,7 +5038,7 @@ static void win_redr_status(win_T *wp) } } - row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); + row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); col = is_stl_global ? 0 : wp->w_wincol; grid_puts(&default_grid, p, row, col, attr); grid_fill(&default_grid, row, row + 1, len + col, @@ -5047,7 +5047,7 @@ static void win_redr_status(win_T *wp) if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { grid_puts(&default_grid, NameBuff, row, - (int)(this_ru_col - STRLEN(NameBuff) - 1), attr); + (int)((size_t)this_ru_col - STRLEN(NameBuff) - 1), attr); } win_redr_ruler(wp, true); @@ -5256,7 +5256,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len) p = "lang"; } } - if (vim_snprintf(buf, len, fmt, p) > len - 1) { + if (vim_snprintf(buf, (size_t)len, fmt, p) > len - 1) { buf[0] = NUL; } xfree(s); @@ -5323,7 +5323,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) maxwidth = wp->w_width_inner; use_sandbox = was_set_insecurely(wp, "winbar", 0); - stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); // Allocate / resize the click definitions array for winbar if needed. if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) { xfree(wp->w_winbar_click_defs); @@ -5331,15 +5331,15 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord)); } } else { - row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); + row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); maxwidth = is_stl_global ? Columns : wp->w_width; - stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); // Allocate / resize the click definitions array for statusline if needed. if (wp->w_status_click_defs_size < (size_t)maxwidth) { xfree(wp->w_status_click_defs); - wp->w_status_click_defs_size = maxwidth; + wp->w_status_click_defs_size = (size_t)maxwidth; wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord)); } @@ -5406,7 +5406,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) // Make all characters printable. p = transstr(buf, true); - len = STRLCPY(buf, p, sizeof(buf)); + len = (int)STRLCPY(buf, p, sizeof(buf)); len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; xfree(p); @@ -5799,13 +5799,11 @@ void screenclear(void) /// Copy part of a grid line for vertically split window. static void linecopy(ScreenGrid *grid, int to, int from, int col, int width) { - unsigned off_to = grid->line_offset[to] + col; - unsigned off_from = grid->line_offset[from] + col; + unsigned off_to = (unsigned)(grid->line_offset[to] + (size_t)col); + unsigned off_from = (unsigned)(grid->line_offset[from] + (size_t)col); - memmove(grid->chars + off_to, grid->chars + off_from, - width * sizeof(schar_T)); - memmove(grid->attrs + off_to, grid->attrs + off_from, - width * sizeof(sattr_T)); + memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T)); + memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T)); } /// Set cursor to its position in the current window. @@ -5902,11 +5900,11 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, linecopy(grid, j + line_count, j, col, width); } j += line_count; - grid_clear_line(grid, grid->line_offset[j] + col, width, false); + grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false); grid->line_wraps[j] = false; } else { j = end - 1 - i; - temp = grid->line_offset[j]; + temp = (unsigned)grid->line_offset[j]; while ((j -= line_count) >= row) { grid->line_offset[j + line_count] = grid->line_offset[j]; grid->line_wraps[j + line_count] = grid->line_wraps[j]; @@ -5951,12 +5949,12 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, linecopy(grid, j - line_count, j, col, width); } j -= line_count; - grid_clear_line(grid, grid->line_offset[j] + col, width, false); + grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false); grid->line_wraps[j] = false; } else { // whole width, moving the line pointers is faster j = row + i; - temp = grid->line_offset[j]; + temp = (unsigned)grid->line_offset[j]; while ((j += line_count) <= end - 1) { grid->line_offset[j - line_count] = grid->line_offset[j]; grid->line_wraps[j - line_count] = grid->line_wraps[j]; @@ -6058,7 +6056,7 @@ int showmode(void) if (edit_submode_extra != NULL) { msg_puts_attr(" ", attr); // Add a space in between. if ((int)edit_submode_highl < HLF_COUNT) { - sub_attr = win_hl_attr(curwin, edit_submode_highl); + sub_attr = win_hl_attr(curwin, (int)edit_submode_highl); } else { sub_attr = attr; } @@ -6079,7 +6077,11 @@ int showmode(void) msg_puts_attr(_(" INSERT"), attr); } else if (restart_edit == 'I' || restart_edit == 'i' || restart_edit == 'a' || restart_edit == 'A') { - msg_puts_attr(_(" (insert)"), attr); + if (curbuf->terminal) { + msg_puts_attr(_(" (terminal)"), attr); + } else { + msg_puts_attr(_(" (insert)"), attr); + } } else if (restart_edit == 'R') { msg_puts_attr(_(" (replace)"), attr); } else if (restart_edit == 'V') { @@ -6195,10 +6197,6 @@ void unshowmode(bool force) // Clear the mode message. void clearmode(void) { - if (p_ch <= 0 && !ui_has(kUIMessages)) { - return; - } - const int save_msg_row = msg_row; const int save_msg_col = msg_col; @@ -6216,10 +6214,6 @@ void clearmode(void) static void recording_mode(int attr) { - if (p_ch <= 0 && !ui_has(kUIMessages)) { - return; - } - msg_puts_attr(_("recording"), attr); if (!shortmess(SHM_RECORDING)) { char s[4]; @@ -6524,8 +6518,7 @@ int redrawing(void) */ int messaging(void) { - return !(p_lz && char_avail() && !KeyTyped) - && (p_ch > 0 || ui_has(kUIMessages)); + return !(p_lz && char_avail() && !KeyTyped) && ui_has_messages(); } /// Show current status info in ruler and various other places @@ -6562,7 +6555,7 @@ static void win_redr_ruler(win_T *wp, bool always) bool is_stl_global = global_stl_height() > 0; static bool did_show_ext_ruler = false; - // If 'ruler' off or redrawing disabled, don't do anything + // If 'ruler' off, don't do anything if (!p_ru) { return; } @@ -6626,7 +6619,7 @@ static void win_redr_ruler(win_T *wp, bool always) width = wp->w_width; part_of_status = true; } else if (is_stl_global) { - row = Rows - p_ch - 1; + row = Rows - (int)p_ch - 1; fillchar = fillchar_status(&attr, wp); off = 0; width = Columns; @@ -6639,7 +6632,7 @@ static void win_redr_ruler(win_T *wp, bool always) off = 0; } - if (!part_of_status && p_ch < 1 && !ui_has(kUIMessages)) { + if (!part_of_status && !ui_has_messages()) { return; } @@ -6727,7 +6720,7 @@ static void win_redr_ruler(win_T *wp, bool always) wp->w_ru_cursor = wp->w_cursor; wp->w_ru_virtcol = wp->w_virtcol; - wp->w_ru_empty = empty_line; + wp->w_ru_empty = (char)empty_line; wp->w_ru_topline = wp->w_topline; wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count; wp->w_ru_topfill = wp->w_topfill; @@ -6765,7 +6758,7 @@ int number_width(win_T *wp) // 'numberwidth' gives the minimal width plus one if (n < wp->w_p_nuw - 1) { - n = wp->w_p_nuw - 1; + n = (int)wp->w_p_nuw - 1; } // If 'signcolumn' is set to 'number' and there is a sign to display, then diff --git a/src/nvim/search.c b/src/nvim/search.c index f3061b4dc4..403e2f3aa4 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -18,6 +18,7 @@ #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/funcs.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" @@ -27,6 +28,7 @@ #include "nvim/getchar.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -424,10 +426,10 @@ int last_csearch_until(void) void set_last_csearch(int c, char_u *s, int len) { - *lastc = c; + *lastc = (char_u)c; lastc_bytelen = len; if (len) { - memcpy(lastc_bytes, s, len); + memcpy(lastc_bytes, s, (size_t)len); } else { memset(lastc_bytes, 0, sizeof(lastc_bytes)); } @@ -973,7 +975,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, void set_search_direction(int cdir) { - spats[0].off.dir = cdir; + spats[0].off.dir = (char)cdir; } static void set_vv_searchforward(void) @@ -1062,7 +1064,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, if (dirc == 0) { dirc = (char_u)spats[0].off.dir; } else { - spats[0].off.dir = dirc; + spats[0].off.dir = (char)dirc; set_vv_searchforward(); } if (options & SEARCH_REV) { @@ -1183,7 +1185,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, if (!cmd_silent && (spats[0].off.line || spats[0].off.end || spats[0].off.off)) { p = off_buf; // -V507 - *p++ = dirc; + *p++ = (char_u)dirc; if (spats[0].off.end) { *p++ = 'e'; } else if (!spats[0].off.line) { @@ -1194,7 +1196,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, } *p = NUL; if (spats[0].off.off != 0 || spats[0].off.line) { - snprintf((char *)p, sizeof(off_buf) - 1 - (p - off_buf), + snprintf((char *)p, sizeof(off_buf) - 1 - (size_t)(p - off_buf), "%" PRId64, spats[0].off.off); } off_len = STRLEN(off_buf); @@ -1215,10 +1217,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, len = 0; // adjusted below } else if (msg_scrolled != 0 && !cmd_silent) { // Use all the columns. - len = (Rows - msg_row) * Columns - 1; + len = (size_t)((Rows - msg_row) * Columns - 1); } else { // Use up to 'showcmd' column. - len = (Rows - msg_row - 1) * Columns + sc_col - 1; + len = (size_t)((Rows - msg_row - 1) * Columns + sc_col - 1); } if (len < STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3) { len = STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3; @@ -1236,7 +1238,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, // do not fill the msgbuf buffer, if cmd_silent is set, leave it // empty for the search_stat feature. if (!cmd_silent) { - msgbuf[0] = dirc; + msgbuf[0] = (char_u)dirc; if (utf_iscomposing(utf_ptr2char((char *)p))) { // Use a space to draw the composing char on. msgbuf[1] = ' '; @@ -1266,13 +1268,13 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, while (*r == ' ') { r++; } - size_t pat_len = msgbuf + STRLEN(msgbuf) - r; + size_t pat_len = (size_t)(msgbuf + STRLEN(msgbuf) - r); memmove(msgbuf, r, pat_len); // overwrite old text if ((size_t)(r - msgbuf) >= pat_len) { memset(r, ' ', pat_len); } else { - memset(msgbuf + pat_len, ' ', r - msgbuf); + memset(msgbuf + pat_len, ' ', (size_t)(r - msgbuf)); } } msg_outtrans((char *)msgbuf); @@ -1326,7 +1328,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, RE_LAST, sia); if (dircp != NULL) { - *dircp = search_delim; // restore second '/' or '?' for normal_cmd() + *dircp = (char_u)search_delim; // restore second '/' or '?' for normal_cmd() } if (!shortmess(SHM_SEARCH) @@ -1361,7 +1363,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, } else if (c > curbuf->b_ml.ml_line_count) { pos.lnum = curbuf->b_ml.ml_line_count; } else { - pos.lnum = c; + pos.lnum = (linenr_T)c; } pos.col = 0; @@ -1535,7 +1537,7 @@ int searchc(cmdarg_T *cap, int t_cmd) if (c != NUL) { // normal search: remember args for repeat if (!KeyStuffed) { // don't remember when redoing - *lastc = c; + *lastc = (char_u)c; set_csearch_direction(dir); set_csearch_until(t_cmd); lastc_bytelen = utf_char2bytes(c, (char *)lastc_bytes); @@ -1657,14 +1659,12 @@ static bool check_prevcol(char_u *linep, int col, int ch, int *prevcol) static bool 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; 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); + size_t delim_len = (size_t)((p - linep) - startpos->col - 1); + char_u *delim_copy = vim_strnsave(linep + startpos->col + 1, delim_len); bool found = false; for (lnum = startpos->lnum; lnum <= endpos->lnum; lnum++) { char_u *line = ml_get(lnum); @@ -2434,9 +2434,9 @@ void showmatch(int c) * available. */ if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) { - os_delay(p_mat * 100L + 8, true); + os_delay((uint64_t)p_mat * 100L + 8, true); } else if (!char_avail()) { - os_delay(p_mat * 100L + 9, false); + os_delay((uint64_t)p_mat * 100L + 9, false); } curwin->w_cursor = save_cursor; // restore cursor position *so = save_so; @@ -3665,16 +3665,15 @@ again: curwin->w_cursor = old_pos; goto theend; } - const size_t spat_len = len + 39; + const size_t spat_len = (size_t)len + 39; char *const spat = xmalloc(spat_len); - const size_t epat_len = len + 9; + const size_t epat_len = (size_t)len + 9; char *const epat = xmalloc(epat_len); snprintf(spat, spat_len, "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p); snprintf(epat, epat_len, "</%.*s>\\c", len, p); - const int r = do_searchpair(spat, "", epat, FORWARD, NULL, - 0, NULL, (linenr_T)0, 0L); + const int r = (int)do_searchpair(spat, "", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L); xfree(spat); xfree(epat); @@ -3795,7 +3794,7 @@ extend: } else { dir = FORWARD; } - for (i = count; --i >= 0;) { + for (i = (int)count; --i >= 0;) { if (start_lnum == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) { retval = FAIL; @@ -3863,8 +3862,8 @@ extend: ++end_lnum; } - --end_lnum; - i = count; + end_lnum--; + i = (int)count; if (!include && white_in_front) { --i; } @@ -4435,9 +4434,9 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct // start and end are in the same position. do { regmatch.startpos[0].col++; - nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, regmatch.startpos[0].col, - NULL, NULL); + nmatched = (int)vim_regexec_multi(®match, curwin, curbuf, + pos.lnum, regmatch.startpos[0].col, + NULL, NULL); if (nmatched != 0) { break; } @@ -4624,7 +4623,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst if (done_search) { xfree(lastpat); lastpat = vim_strsave(spats[last_idx].pat); - chgtick = buf_get_changedtick(curbuf); + chgtick = (int)buf_get_changedtick(curbuf); lbuf = curbuf; lastpos = p; } @@ -4704,21 +4703,21 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) } li = tv_list_find(di->di_tv.vval.v_list, 0L); if (li != NULL) { - pos.lnum = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + pos.lnum = (linenr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); if (error) { return; } } li = tv_list_find(di->di_tv.vval.v_list, 1L); if (li != NULL) { - pos.col = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error) - 1; + pos.col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error) - 1; if (error) { return; } } li = tv_list_find(di->di_tv.vval.v_list, 2L); if (li != NULL) { - pos.coladd = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + pos.coladd = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); if (error) { return; } @@ -4841,7 +4840,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, int score = 100; // Apply leading letter penalty - int penalty = LEADING_LETTER_PENALTY * matches[0]; + int penalty = LEADING_LETTER_PENALTY * (int)matches[0]; if (penalty < MAX_LEADING_LETTER_PENALTY) { penalty = MAX_LEADING_LETTER_PENALTY; } @@ -4862,7 +4861,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, if (currIdx == prevIdx + 1) { score += SEQUENTIAL_BONUS; } else { - score += GAP_PENALTY * (currIdx - prevIdx); + score += GAP_PENALTY * (int)(currIdx - prevIdx); } } @@ -4936,7 +4935,7 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32 // "Copy-on-Write" srcMatches into matches if (first_match && srcMatches != NULL) { - memcpy(matches, srcMatches, nextMatch * sizeof(srcMatches[0])); + memcpy(matches, srcMatches, (size_t)nextMatch * sizeof(srcMatches[0])); first_match = false; } @@ -4976,7 +4975,7 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32 // Return best result if (recursiveMatch && (!matched || bestRecursiveScore > *outScore)) { // Recursive score is better than "this" - memcpy(matches, bestRecursiveMatches, maxMatches * sizeof(matches[0])); + memcpy(matches, bestRecursiveMatches, (size_t)maxMatches * sizeof(matches[0])); *outScore = bestRecursiveScore; return nextMatch; } else if (matched) { @@ -5095,7 +5094,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m len = max_matches; } - fuzzyItem_T *const items = xcalloc(len, sizeof(fuzzyItem_T)); + fuzzyItem_T *const items = xcalloc((size_t)len, sizeof(fuzzyItem_T)); long match_count = 0; uint32_t matches[MAX_FUZZY_MATCHES]; @@ -5136,7 +5135,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m int score; if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches, MAX_FUZZY_MATCHES)) { - items[match_count].idx = match_count; + items[match_count].idx = (int)match_count; items[match_count].item = li; items[match_count].score = score; @@ -5161,7 +5160,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m if (match_count > 0) { // Sort the list by the descending order of the match score - qsort(items, match_count, sizeof(fuzzyItem_T), fuzzy_match_item_compare); + qsort(items, (size_t)match_count, sizeof(fuzzyItem_T), fuzzy_match_item_compare); // For matchfuzzy(), return a list of matched strings. // ['str1', 'str2', 'str3'] @@ -5394,7 +5393,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo } def_regmatch.rm_ic = FALSE; // don't ignore case in define pat. } - files = xcalloc(max_path_depth, sizeof(SearchedFile)); + files = xcalloc((size_t)max_path_depth, sizeof(SearchedFile)); old_files = max_path_depth; depth = depth_displayed = -1; @@ -5537,7 +5536,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo if (new_fname != NULL) { // Push the new file onto the file stack if (depth + 1 == old_files) { - bigger = xmalloc(max_path_depth * 2 * sizeof(SearchedFile)); + bigger = xmalloc((size_t)max_path_depth * 2 * sizeof(SearchedFile)); for (i = 0; i <= depth; i++) { bigger[i] = files[i]; } @@ -5849,7 +5848,7 @@ exit_matched: if (action == ACTION_EXPAND) { ins_compl_check_keys(30, false); } - if (got_int || compl_interrupted) { + if (got_int || ins_compl_interrupted()) { break; } @@ -5911,7 +5910,7 @@ exit_matched: } } else if (!found && action != ACTION_EXPAND) { - if (got_int || compl_interrupted) { + if (got_int || ins_compl_interrupted()) { emsg(_(e_interr)); } else if (type == FIND_DEFINE) { emsg(_("E388: Couldn't find definition")); diff --git a/src/nvim/search.h b/src/nvim/search.h index 53059cc1ea..ff843bb59e 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -5,7 +5,6 @@ #include <stdint.h> #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/normal.h" #include "nvim/os/time.h" diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 9a4b304d6c..1640d0167e 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -10,6 +10,7 @@ #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/edit.h" +#include "nvim/eval/funcs.h" #include "nvim/ex_docmd.h" #include "nvim/fold.h" #include "nvim/highlight_group.h" diff --git a/src/nvim/sign.h b/src/nvim/sign.h index c61e5d20ef..ba84cd71a4 100644 --- a/src/nvim/sign.h +++ b/src/nvim/sign.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" -#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/sign_defs.h" diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 2aadc2258e..ceb35af4b8 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -95,6 +95,7 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -291,7 +292,7 @@ int did_set_spelltab; // structure used to store soundfolded words that add_sound_suggest() has // handled already. typedef struct { - short sft_score; // lowest score used + int16_t sft_score; // lowest score used char_u sft_word[1]; // soundfolded word, actually longer } sftword_T; @@ -743,9 +744,8 @@ static void find_word(matchinf_T *mip, int mode) // prefix ID. // Repeat this if there are more flags/region alternatives until there // is a match. - for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; - --len, ++arridx) { - uint32_t flags = idxs[arridx]; + for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; len--, arridx++) { + uint32_t flags = (uint32_t)idxs[arridx]; // For the fold-case tree check that the case of the checked word // matches with what the word in the tree requires. @@ -760,7 +760,7 @@ static void find_word(matchinf_T *mip, int mode) } if (mip->mi_capflags == WF_KEEPCAP - || !spell_valid_case(mip->mi_capflags, flags)) { + || !spell_valid_case(mip->mi_capflags, (int)flags)) { continue; } } @@ -769,7 +769,7 @@ static void find_word(matchinf_T *mip, int mode) // mip->mi_prefarridx that find_prefix() filled. else if (mode == FIND_PREFIX && !prefix_found) { c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, - flags, + (int)flags, mip->mi_word + mip->mi_cprefixlen, slang, false); if (c == 0) { @@ -828,10 +828,8 @@ static void find_word(matchinf_T *mip, int mode) } // Quickly check if compounding is possible with this flag. - if (!byte_in_str(mip->mi_complen == 0 - ? slang->sl_compstartflags - : slang->sl_compallflags, - ((unsigned)flags >> 24))) { + if (!byte_in_str(mip->mi_complen == 0 ? slang->sl_compstartflags : slang->sl_compallflags, + (int)((unsigned)flags >> 24))) { continue; } @@ -879,7 +877,7 @@ static void find_word(matchinf_T *mip, int mode) // If the word ends the sequence of compound flags of the // words must match with one of the COMPOUNDRULE items and // the number of syllables must not be too large. - mip->mi_compflags[mip->mi_complen] = ((unsigned)flags >> 24); + mip->mi_compflags[mip->mi_complen] = (char_u)((unsigned)flags >> 24); mip->mi_compflags[mip->mi_complen + 1] = NUL; if (word_ends) { char_u fword[MAXWLEN] = { 0 }; @@ -1005,7 +1003,7 @@ static void find_word(matchinf_T *mip, int mode) res = SP_BANNED; } else if (flags & WF_REGION) { // Check region. - if ((mip->mi_lp->lp_region & (flags >> 16)) != 0) { + if (((unsigned)mip->mi_lp->lp_region & (flags >> 16)) != 0) { res = SP_OK; } else { res = SP_LOCAL; @@ -1119,7 +1117,7 @@ static bool can_be_compound(trystate_T *sp, slang_T *slang, char_u *compflags, i // possibly can form a match with COMPOUNDRULE patterns. This only // makes sense when we have two or more words. if (slang->sl_comprules != NULL && sp->ts_complen > sp->ts_compsplit) { - compflags[sp->ts_complen] = flag; + compflags[sp->ts_complen] = (char_u)flag; compflags[sp->ts_complen + 1] = NUL; bool v = match_compoundrule(slang, compflags + sp->ts_compsplit); compflags[sp->ts_complen] = NUL; @@ -1195,10 +1193,9 @@ static int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word { int prefcnt; int pidx; - int prefid; - prefid = (unsigned)flags >> 24; - for (prefcnt = totprefcnt - 1; prefcnt >= 0; --prefcnt) { + int prefid = (int)((unsigned)flags >> 24); + for (prefcnt = totprefcnt - 1; prefcnt >= 0; prefcnt--) { pidx = slang->sl_pidxs[arridx + prefcnt]; // Check the prefix ID. @@ -1646,7 +1643,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen) // concatenate. n = (int)(p - line) + 1; if (n < maxlen - 1) { - memset(buf, ' ', n); + memset(buf, ' ', (size_t)n); STRLCPY(buf + n, p, maxlen - n); } } @@ -1872,7 +1869,7 @@ static void spell_load_cb(char *fname, void *cookie) /// @param[in] word added to common words hashtable /// @param[in] len length of word or -1 for NUL terminated /// @param[in] count 1 to count once, 10 to init -void count_common_word(slang_T *lp, char_u *word, int len, int count) +void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count) { hash_T hash; hashitem_T *hi; @@ -1899,7 +1896,8 @@ void count_common_word(slang_T *lp, char_u *word, int len, int count) hash_add_item(&lp->sl_wordcount, hi, wc->wc_word, hash); } else { wc = HI2WC(hi); - if ((wc->wc_count += count) < (unsigned)count) { // check for overflow + wc->wc_count = (uint16_t)(wc->wc_count + count); + if (wc->wc_count < count) { // check for overflow wc->wc_count = MAXWORDCOUNT; } } @@ -2108,7 +2106,7 @@ char *did_set_spelllang(win_T *wp) if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2]) && !ASCII_ISALPHA(p[3])) { STRLCPY(region_cp, p + 1, 3); - memmove(p, p + 3, len - (p - lang) - 2); + memmove(p, p + 3, (size_t)(len - (p - lang) - 2)); region = region_cp; } else { dont_use_region = true; @@ -2360,11 +2358,11 @@ static void use_midword(slang_T *lp, win_T *wp) wp->w_s->b_spell_ismw[c] = true; } else if (wp->w_s->b_spell_ismw_mb == NULL) { // First multi-byte char in "b_spell_ismw_mb". - wp->w_s->b_spell_ismw_mb = vim_strnsave(p, l); + wp->w_s->b_spell_ismw_mb = vim_strnsave(p, (size_t)l); } else { // Append multi-byte chars to "b_spell_ismw_mb". const int n = (int)STRLEN(wp->w_s->b_spell_ismw_mb); - char_u *bp = vim_strnsave(wp->w_s->b_spell_ismw_mb, n + l); + char_u *bp = vim_strnsave(wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l); xfree(wp->w_s->b_spell_ismw_mb); wp->w_s->b_spell_ismw_mb = bp; STRLCPY(bp + n, p, l + 1); @@ -2616,9 +2614,9 @@ void clear_spell_chartab(spelltab_T *sp) memset(sp->st_isw, false, sizeof(sp->st_isw)); memset(sp->st_isu, false, sizeof(sp->st_isu)); - for (i = 0; i < 256; ++i) { - sp->st_fold[i] = i; - sp->st_upper[i] = i; + for (i = 0; i < 256; i++) { + sp->st_fold[i] = (char_u)i; + sp->st_upper[i] = (char_u)i; } // We include digits. A word shouldn't start with a digit, but handling @@ -2629,11 +2627,11 @@ void clear_spell_chartab(spelltab_T *sp) for (i = 'A'; i <= 'Z'; ++i) { sp->st_isw[i] = true; sp->st_isu[i] = true; - sp->st_fold[i] = i + 0x20; + sp->st_fold[i] = (char_u)(i + 0x20); } for (i = 'a'; i <= 'z'; ++i) { sp->st_isw[i] = true; - sp->st_upper[i] = i - 0x20; + sp->st_upper[i] = (char_u)(i - 0x20); } } @@ -2656,8 +2654,8 @@ void init_spell_chartab(void) // The folded/upper-cased value is different between latin1 and // utf8 for 0xb5, causing E763 for no good reason. Use the latin1 // value for utf-8 to avoid this. - spelltab.st_fold[i] = (f < 256) ? f : i; - spelltab.st_upper[i] = (u < 256) ? u : i; + spelltab.st_fold[i] = (f < 256) ? (char_u)f : (char_u)i; + spelltab.st_upper[i] = (u < 256) ? (char_u)u : (char_u)i; } } @@ -2967,12 +2965,11 @@ void spell_suggest(int count) stp = &SUG(sug.su_ga, i); // The suggested word may replace only part of the bad word, add - // the not replaced part. + // the not replaced part. But only when it's not getting too long. STRLCPY(wcopy, stp->st_word, MAXWLEN + 1); - if (sug.su_badlen > stp->st_orglen) { - STRLCPY(wcopy + stp->st_wordlen, - sug.su_badptr + stp->st_orglen, - sug.su_badlen - stp->st_orglen + 1); + int el = sug.su_badlen - stp->st_orglen; + if (el > 0 && stp->st_wordlen + el <= MAXWLEN) { + STRLCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, el + 1); } vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1); if (cmdmsg_rl) { @@ -3036,21 +3033,21 @@ void spell_suggest(int count) if (sug.su_badlen > stp->st_orglen) { // Replacing less than "su_badlen", append the remainder to // repl_to. - repl_from = vim_strnsave(sug.su_badptr, sug.su_badlen); + repl_from = vim_strnsave(sug.su_badptr, (size_t)sug.su_badlen); vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word, sug.su_badlen - stp->st_orglen, sug.su_badptr + stp->st_orglen); repl_to = vim_strsave(IObuff); } else { // Replacing su_badlen or more, use the whole word. - repl_from = vim_strnsave(sug.su_badptr, stp->st_orglen); + repl_from = vim_strnsave(sug.su_badptr, (size_t)stp->st_orglen); repl_to = vim_strsave(stp->st_word); } // Replace the word. - p = xmalloc(STRLEN(line) - stp->st_orglen + stp->st_wordlen + 1); + p = xmalloc(STRLEN(line) - (size_t)stp->st_orglen + (size_t)stp->st_wordlen + 1); c = (int)(sug.su_badptr - line); - memmove(p, line, c); + memmove(p, line, (size_t)c); STRCPY(p + c, stp->st_word); STRCAT(p, sug.su_badptr + stp->st_orglen); @@ -3171,8 +3168,8 @@ void ex_spellrepall(exarg_T *eap) line = get_cursor_line_ptr(); if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col, repl_to, STRLEN(repl_to)) != 0) { - p = xmalloc(STRLEN(line) + addlen + 1); - memmove(p, line, curwin->w_cursor.col); + p = xmalloc(STRLEN(line) + (size_t)addlen + 1); + memmove(p, line, (size_t)curwin->w_cursor.col); STRCPY(p + curwin->w_cursor.col, repl_to); STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from)); ml_replace(curwin->w_cursor.lnum, (char *)p, false); @@ -3219,8 +3216,7 @@ void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap // The suggested word may replace only part of "word", add the not // replaced part. - wcopy = xmalloc(stp->st_wordlen - + STRLEN(sug.su_badptr + stp->st_orglen) + 1); + wcopy = xmalloc((size_t)stp->st_wordlen + STRLEN(sug.su_badptr + stp->st_orglen) + 1); STRCPY(wcopy, stp->st_word); STRCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen); ((char_u **)gap->ga_data)[gap->ga_len++] = wcopy; @@ -3562,7 +3558,7 @@ static void allcap_copy(char_u *word, char_u *wcopy) if (d - wcopy >= MAXWLEN - 1) { break; } - *d++ = c; + *d++ = (char_u)c; } else { c = SPELL_TOUPPER(c); } @@ -3578,14 +3574,12 @@ static void allcap_copy(char_u *word, char_u *wcopy) // Try finding suggestions by recognizing specific situations. static void suggest_try_special(suginfo_T *su) { - char_u *p; - size_t len; int c; char_u word[MAXWLEN]; // Recognize a word that is repeated: "the the". - p = skiptowhite(su->su_fbadword); - len = p - su->su_fbadword; + char_u *p = skiptowhite(su->su_fbadword); + size_t len = (size_t)(p - su->su_fbadword); p = (char_u *)skipwhite((char *)p); if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) { // Include badflags: if the badword is onecap or allcap @@ -3593,7 +3587,7 @@ static void suggest_try_special(suginfo_T *su) c = su->su_fbadword[len]; su->su_fbadword[len] = NUL; make_case_word(su->su_fbadword, word, su->su_badflags); - su->su_fbadword[len] = c; + su->su_fbadword[len] = (char_u)c; // Give a soundalike score of 0, compute the score as if deleting one // character. @@ -3820,13 +3814,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_prefixdepth == PFD_PREFIXTREE) { // Skip over the NUL bytes, we use them later. for (n = 0; n < len && byts[arridx + n] == 0; n++) {} - sp->ts_curi += n; + sp->ts_curi = (int16_t)(sp->ts_curi + n); // Always past NUL bytes now. n = (int)sp->ts_state; PROF_STORE(sp->ts_state) sp->ts_state = STATE_ENDNUL; - sp->ts_save_badflags = su->su_badflags; + sp->ts_save_badflags = (char_u)su->su_badflags; // At end of a prefix or at start of prefixtree: check for // following word. @@ -3843,7 +3837,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so go_deeper(stack, depth, 0); ++depth; sp = &stack[depth]; - sp->ts_prefixdepth = depth - 1; + sp->ts_prefixdepth = (char_u)(depth - 1); byts = fbyts; idxs = fidxs; sp->ts_arridx = 0; @@ -3863,7 +3857,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Past bytes in node and/or past NUL bytes. PROF_STORE(sp->ts_state) sp->ts_state = STATE_ENDNUL; - sp->ts_save_badflags = su->su_badflags; + sp->ts_save_badflags = (char_u)su->su_badflags; break; } @@ -3966,7 +3960,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so break; } - compflags[sp->ts_complen] = ((unsigned)flags >> 24); + compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24); compflags[sp->ts_complen + 1] = NUL; STRLCPY(preword + sp->ts_prewordlen, tword + sp->ts_splitoff, @@ -4048,7 +4042,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so newscore = 0; if (!soundfold) { // soundfold words don't have flags if ((flags & WF_REGION) - && (((unsigned)flags >> 16) & lp->lp_region) == 0) { + && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) { newscore += SCORE_REGION; } if (flags & WF_RARE) { @@ -4166,10 +4160,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so && (slang->sl_compsylmax < MAXWLEN || sp->ts_complen + 1 - sp->ts_compsplit < slang->sl_compmax) - && (can_be_compound(sp, slang, - compflags, ((unsigned)flags >> 24)))) { + && (can_be_compound(sp, slang, compflags, (int)((unsigned)flags >> 24)))) { try_compound = true; - compflags[sp->ts_complen] = ((unsigned)flags >> 24); + compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24); compflags[sp->ts_complen + 1] = NUL; } @@ -4188,7 +4181,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so --sp->ts_curi; // do the same NUL again compflags[sp->ts_complen] = NUL; } else { - sp->ts_flags &= ~TSF_DIDSPLIT; + sp->ts_flags &= (char_u) ~TSF_DIDSPLIT; } if (try_split || try_compound) { @@ -4234,7 +4227,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so } #endif // Save things to be restored at STATE_SPLITUNDO. - sp->ts_save_badflags = su->su_badflags; + sp->ts_save_badflags = (char_u)su->su_badflags; PROF_STORE(sp->ts_state) sp->ts_state = STATE_SPLITUNDO; @@ -4265,14 +4258,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so l = utfc_ptr2len((char *)fword + sp->ts_fidx); if (fword_ends) { // Copy the skipped character to preword. - memmove(preword + sp->ts_prewordlen, - fword + sp->ts_fidx, l); - sp->ts_prewordlen += l; + memmove(preword + sp->ts_prewordlen, fword + sp->ts_fidx, (size_t)l); + sp->ts_prewordlen = (char_u)(sp->ts_prewordlen + l); preword[sp->ts_prewordlen] = NUL; } else { sp->ts_score -= SCORE_SPLIT - SCORE_SUBST; } - sp->ts_fidx += l; + sp->ts_fidx = (char_u)(sp->ts_fidx + l); } // When compounding include compound flag in @@ -4385,7 +4377,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (fword[sp->ts_fidx] != NUL) { sp->ts_fidx++; } - tword[sp->ts_twordlen++] = c; + tword[sp->ts_twordlen++] = (char_u)c; sp->ts_arridx = idxs[arridx]; if (newscore == SCORE_SUBST) { sp->ts_isdiff = DIFF_YES; @@ -4397,7 +4389,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // First byte. sp->ts_tcharidx = 0; sp->ts_tcharlen = MB_BYTE2LEN(c); - sp->ts_fcharstart = sp->ts_fidx - 1; + sp->ts_fcharstart = (char_u)(sp->ts_fidx - 1); sp->ts_isdiff = (newscore != 0) ? DIFF_YES : DIFF_NONE; } else if (sp->ts_isdiff == DIFF_INSERT && sp->ts_fidx > 0) { @@ -4410,8 +4402,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_isdiff == DIFF_YES) { // Correct ts_fidx for the byte length of the // character (we didn't check that before). - sp->ts_fidx = sp->ts_fcharstart - + utfc_ptr2len((char *)fword + sp->ts_fcharstart); + sp->ts_fidx = (char_u)(sp->ts_fcharstart + + utfc_ptr2len((char *)fword + sp->ts_fcharstart)); // For changing a composing character adjust // the score from SCORE_SUBST to @@ -4498,7 +4490,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // a bit illogical for soundfold tree but it does give better // results. c = utf_ptr2char((char *)fword + sp->ts_fidx); - stack[depth].ts_fidx += utfc_ptr2len((char *)fword + sp->ts_fidx); + stack[depth].ts_fidx = + (char_u)(stack[depth].ts_fidx + utfc_ptr2len((char *)fword + sp->ts_fidx)); if (utf_iscomposing(c)) { stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP; } else if (c == utf_ptr2char((char *)fword + stack[depth].ts_fidx)) { @@ -4571,14 +4564,14 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so #endif ++depth; sp = &stack[depth]; - tword[sp->ts_twordlen++] = c; + tword[sp->ts_twordlen++] = (char_u)c; sp->ts_arridx = idxs[n]; fl = MB_BYTE2LEN(c); if (fl > 1) { // There are following bytes for the same character. // We must find all bytes before trying // delete/insert/swap/etc. - sp->ts_tcharlen = fl; + sp->ts_tcharlen = (char_u)fl; sp->ts_tcharidx = 1; sp->ts_isdiff = DIFF_INSERT; } @@ -4652,9 +4645,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_state = STATE_UNSWAP; depth++; fl = utf_char2len(c2); - memmove(p, p + n, fl); + memmove(p, p + n, (size_t)fl); utf_char2bytes(c, (char *)p + fl); - stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl); } else { // If this swap doesn't work then SWAP3 won't either. PROF_STORE(sp->ts_state) @@ -4667,7 +4660,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so p = fword + sp->ts_fidx; n = utfc_ptr2len((char *)p); c = utf_ptr2char((char *)p + n); - memmove(p + utfc_ptr2len((char *)p + n), p, n); + memmove(p + utfc_ptr2len((char *)p + n), p, (size_t)n); utf_char2bytes(c, (char *)p); FALLTHROUGH; @@ -4708,10 +4701,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_state = STATE_UNSWAP3; depth++; tl = utf_char2len(c3); - memmove(p, p + n + fl, tl); + memmove(p, p + n + fl, (size_t)tl); utf_char2bytes(c2, (char *)p + tl); utf_char2bytes(c, (char *)p + fl + tl); - stack[depth].ts_fidxtry = sp->ts_fidx + n + fl + tl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl + tl); } else { PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; @@ -4726,7 +4719,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so fl = utfc_ptr2len((char *)p + n); c = utf_ptr2char((char *)p + n + fl); tl = utfc_ptr2len((char *)p + n + fl); - memmove(p + fl + tl, p, n); + memmove(p + fl + tl, p, (size_t)n); utf_char2bytes(c, (char *)p); utf_char2bytes(c2, (char *)p + tl); p = p + tl; @@ -4757,9 +4750,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so c = utf_ptr2char((char *)p); fl = utf_ptr2len((char *)p + n); fl += utf_ptr2len((char *)p + n + fl); - memmove(p, p + n, fl); + memmove(p, p + n, (size_t)fl); utf_char2bytes(c, (char *)p + fl); - stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl); } else { PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; @@ -4773,7 +4766,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so n += utfc_ptr2len((char *)p + n); c = utf_ptr2char((char *)p + n); tl = utfc_ptr2len((char *)p + n); - memmove(p + tl, p, n); + memmove(p + tl, p, (size_t)n); utf_char2bytes(c, (char *)p); // Rotate three bytes right: "123" -> "312". We change "fword" @@ -4794,9 +4787,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so n += utf_ptr2len((char *)p + n); c = utf_ptr2char((char *)p + n); tl = utf_ptr2len((char *)p + n); - memmove(p + tl, p, n); + memmove(p + tl, p, (size_t)n); utf_char2bytes(c, (char *)p); - stack[depth].ts_fidxtry = sp->ts_fidx + n + tl; + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + tl); } else { PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; @@ -4810,7 +4803,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so tl = utfc_ptr2len((char *)p); n = utfc_ptr2len((char *)p + tl); n += utfc_ptr2len((char *)p + tl + n); - memmove(p, p + tl, n); + memmove(p, p + tl, (size_t)n); utf_char2bytes(c, (char *)p + n); FALLTHROUGH; @@ -4862,7 +4855,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so ftp = (fromto_T *)gap->ga_data + sp->ts_curi++; if (*ftp->ft_from != *p) { // past possible matching entries - sp->ts_curi = gap->ga_len; + sp->ts_curi = (char_u)gap->ga_len; break; } if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0 @@ -4885,8 +4878,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so STRMOVE(p + tl, p + fl); repextra += tl - fl; } - memmove(p, ftp->ft_to, tl); - stack[depth].ts_fidxtry = sp->ts_fidx + tl; + memmove(p, ftp->ft_to, (size_t)tl); + stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + tl); stack[depth].ts_tcharlen = 0; break; } @@ -4915,7 +4908,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so STRMOVE(p + fl, p + tl); repextra -= tl - fl; } - memmove(p, ftp->ft_from, fl); + memmove(p, ftp->ft_from, (size_t)fl); PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP; break; @@ -5410,7 +5403,7 @@ static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_ hash); if (HASHITEM_EMPTY(hi)) { sft = xmalloc(sizeof(sftword_T) + goodword_len); - sft->sft_score = score; + sft->sft_score = (int16_t)score; memcpy(sft->sft_word, goodword, goodword_len + 1); hash_add_item(&slang->sl_sounddone, hi, sft->sft_word, hash); } else { @@ -5418,7 +5411,7 @@ static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_ if (score >= sft->sft_score) { return; } - sft->sft_score = score; + sft->sft_score = (int16_t)score; } // Find the word nr in the soundfold tree. @@ -5511,7 +5504,7 @@ badword: } else { // Add a penalty for words in another region. if ((flags & WF_REGION) - && (((unsigned)flags >> 16) & lp->lp_region) == 0) { + && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) { goodscore = SCORE_REGION; } else { goodscore = 0; @@ -5781,7 +5774,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword, if (i < 0) { // Add a suggestion. stp = GA_APPEND_VIA_PTR(suggest_T, gap); - stp->st_word = vim_strnsave(goodword, goodlen); + stp->st_word = vim_strnsave(goodword, (size_t)goodlen); stp->st_wordlen = goodlen; stp->st_score = score; stp->st_altscore = altscore; @@ -5831,8 +5824,7 @@ static void check_suggestions(suginfo_T *su, garray_T *gap) xfree(stp[i].st_word); --gap->ga_len; if (i < gap->ga_len) { - memmove(stp + i, stp + i + 1, - sizeof(suggest_T) * (gap->ga_len - i)); + memmove(stp + i, stp + i + 1, sizeof(suggest_T) * (size_t)(gap->ga_len - i)); } } } @@ -6281,8 +6273,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) } } if (k > k0) { - memmove(word + i + k0, word + i + k, - sizeof(int) * (wordlen - (i + k) + 1)); + memmove(word + i + k0, word + i + k, sizeof(int) * (size_t)(wordlen - (i + k) + 1)); } // new "actual letter" @@ -6310,8 +6301,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) if (c != NUL) { wres[reslen++] = c; } - memmove(word, word + i + 1, - sizeof(int) * (wordlen - (i + 1) + 1)); + memmove(word, word + i + 1, sizeof(int) * (size_t)(wordlen - (i + 1) + 1)); i = 0; z0 = 1; } @@ -6610,7 +6600,7 @@ static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) // We use "cnt" as an array: CNT(badword_idx, goodword_idx). #define CNT(a, b) cnt[(a) + (b) * (badlen + 1)] - cnt = xmalloc(sizeof(int) * (badlen + 1) * (goodlen + 1)); + cnt = xmalloc(sizeof(int) * ((size_t)badlen + 1) * ((size_t)goodlen + 1)); CNT(0, 0) = 0; for (j = 1; j <= goodlen; ++j) { @@ -6879,7 +6869,7 @@ void ex_spelldump(exarg_T *eap) if (no_spell_checking(curwin)) { return; } - get_option_value("spl", &dummy, &spl, OPT_LOCAL); + (void)get_option_value("spl", &dummy, &spl, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); @@ -7012,7 +7002,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) arridx[0] = 0; curi[0] = 1; while (depth >= 0 && !got_int - && (pat == NULL || !compl_interrupted)) { + && (pat == NULL || !ins_compl_interrupted())) { if (curi[depth] > byts[arridx[depth]]) { // Done all bytes at this node, go up one level. --depth; @@ -7035,7 +7025,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) && (do_region || (flags & WF_REGION) == 0 || (((unsigned)flags >> 16) - & lp->lp_region) != 0)) { + & (unsigned)lp->lp_region) != 0)) { word[depth] = NUL; if (!do_region) { flags &= ~WF_REGION; @@ -7043,7 +7033,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) // Dump the basic word if there is no prefix or // when it's the first one. - c = (unsigned)flags >> 24; + c = (int)((unsigned)flags >> 24); if (c == 0 || curi[depth] == 2) { dump_word(slang, word, pat, dir, dumpflags, flags, lnum); @@ -7060,7 +7050,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) } } else { // Normal char, go one level deeper. - word[depth++] = c; + word[depth++] = (char_u)c; arridx[depth] = idxs[n]; curi[depth] = 1; @@ -7256,7 +7246,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi } } else { // Normal char, go one level deeper. - prefix[depth++] = c; + prefix[depth++] = (char_u)c; arridx[depth] = idxs[n]; curi[depth] = 1; } @@ -7329,7 +7319,7 @@ void spell_expand_check_cap(colnr_T col) // Used for Insert mode completion CTRL-X ?. // Returns the number of matches. The matches are in "matchp[]", array of // allocated strings. -int expand_spelling(linenr_T lnum, char_u *pat, char_u ***matchp) +int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp) { garray_T ga; diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h index 7e85b5bf03..222d103f5d 100644 --- a/src/nvim/spell_defs.h +++ b/src/nvim/spell_defs.h @@ -237,7 +237,7 @@ typedef struct trystate_S { state_T ts_state; // state at this level, STATE_ int ts_score; // score idx_T ts_arridx; // index in tree array, start of node - short ts_curi; // index in list of child nodes + int16_t ts_curi; // index in list of child nodes char_u ts_fidx; // index in fword[], case-folded bad word char_u ts_fidxtry; // ts_fidx at which bytes may be changed char_u ts_twordlen; // valid length of tword[] diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 9d2fd2637d..9f21e24d4c 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -903,8 +903,8 @@ void suggest_load_files(void) } // <SUGHEADER>: <fileID> <versionnr> <timestamp> - for (i = 0; i < VIMSUGMAGICL; ++i) { - buf[i] = getc(fd); // <fileID> + for (i = 0; i < VIMSUGMAGICL; i++) { + buf[i] = (char_u)getc(fd); // <fileID> } if (STRNCMP(buf, VIMSUGMAGIC, VIMSUGMAGICL) != 0) { semsg(_("E778: This does not look like a .sug file: %s"), @@ -965,7 +965,7 @@ someerror: if (c < 0) { goto someerror; } - GA_APPEND(char_u, &ga, c); + GA_APPEND(char_u, &ga, (char_u)c); if (c == NUL) { break; } @@ -1009,7 +1009,7 @@ static char_u *read_cnt_string(FILE *fd, int cnt_bytes, int *cntp) *cntp = SP_TRUNCERROR; return NULL; } - cnt = (cnt << 8) + (unsigned)c; + cnt = (int)(((unsigned)cnt << 8) + (unsigned)c); } *cntp = cnt; if (cnt == 0) { @@ -1081,7 +1081,7 @@ static int read_prefcond_section(FILE *fd, slang_T *lp) return SP_FORMERROR; } - lp->sl_prefprog = xcalloc(cnt, sizeof(regprog_T *)); + lp->sl_prefprog = xcalloc((size_t)cnt, sizeof(regprog_T *)); lp->sl_prefixcnt = cnt; for (int i = 0; i < cnt; i++) { @@ -1146,7 +1146,7 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first) for (int i = 0; i < gap->ga_len; ++i) { ftp = &((fromto_T *)gap->ga_data)[i]; if (first[*ftp->ft_from] == -1) { - first[*ftp->ft_from] = i; + first[*ftp->ft_from] = (int16_t)i; } } return 0; @@ -1193,7 +1193,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) if (ccnt < 0) { return SP_TRUNCERROR; } - p = xmalloc(ccnt + 2); + p = xmalloc((size_t)ccnt + 2); smp->sm_lead = p; // Read up to the first special char into sm_lead. @@ -1203,7 +1203,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) if (vim_strchr("0123456789(-<^$", c) != NULL) { break; } - *p++ = c; + *p++ = (char_u)c; } smp->sm_leadlen = (int)(p - smp->sm_lead); *p++ = NUL; @@ -1216,7 +1216,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) if (c == ')') { break; } - *p++ = c; + *p++ = (char_u)c; } *p++ = NUL; if (++i < ccnt) { @@ -1230,7 +1230,7 @@ static int read_sal_section(FILE *fd, slang_T *slang) smp->sm_rules = p; if (i < ccnt) { // store the char we got while checking for end of sm_lead - *p++ = c; + *p++ = (char_u)c; } i++; if (i < ccnt) { @@ -1302,7 +1302,7 @@ static int read_words_section(FILE *fd, slang_T *lp, int len) if (c == EOF) { return SP_TRUNCERROR; } - word[i] = c; + word[i] = (char_u)c; if (word[i] == NUL) { break; } @@ -1363,11 +1363,6 @@ static int read_compound(FILE *fd, slang_T *slang, int len) int todo = len; int c; int atstart; - char_u *pat; - char_u *pp; - char_u *cp; - char_u *ap; - char_u *crp; int cnt; garray_T *gap; @@ -1432,25 +1427,25 @@ static int read_compound(FILE *fd, slang_T *slang, int len) // Conversion to utf-8 may double the size. c = todo * 2 + 7; c += todo * 2; - pat = xmalloc(c); + char_u *pat = xmalloc((size_t)c); // We also need a list of all flags that can appear at the start and one // for all flags. - cp = xmalloc(todo + 1); + char_u *cp = xmalloc((size_t)todo + 1); slang->sl_compstartflags = cp; *cp = NUL; - ap = xmalloc(todo + 1); + char_u *ap = xmalloc((size_t)todo + 1); slang->sl_compallflags = ap; *ap = NUL; // And a list of all patterns in their original form, for checking whether // compounding may work in match_compoundrule(). This is freed when we // encounter a wildcard, the check doesn't work then. - crp = xmalloc(todo + 1); + char_u *crp = xmalloc((size_t)todo + 1); slang->sl_comprules = crp; - pp = pat; + char_u *pp = pat; *pp++ = '^'; *pp++ = '\\'; *pp++ = '('; @@ -1466,7 +1461,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len) // Add all flags to "sl_compallflags". if (vim_strchr("?*+[]/", c) == NULL && !byte_in_str(slang->sl_compallflags, c)) { - *ap++ = c; + *ap++ = (char_u)c; *ap = NUL; } @@ -1479,7 +1474,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len) atstart = 0; } else { if (!byte_in_str(slang->sl_compstartflags, c)) { - *cp++ = c; + *cp++ = (char_u)c; *cp = NUL; } if (atstart == 1) { @@ -1494,7 +1489,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len) XFREE_CLEAR(slang->sl_comprules); crp = NULL; } else { - *crp++ = c; + *crp++ = (char_u)c; } } @@ -1561,7 +1556,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // Allocate the lists. for (int i = 0; i < 256; i++) { if (lp->sl_sal_first[i] > 0) { - p = xmalloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1)); + p = xmalloc(sizeof(int) * (size_t)(lp->sl_sal_first[i] * 2 + 1)); ((int **)gap->ga_data)[i] = (int *)p; *(int *)p = 0; } @@ -1631,7 +1626,7 @@ static void set_sal_first(slang_T *lp) i++; n--; tsal = smp[i + n]; - memmove(smp + i + 1, smp + i, sizeof(salitem_T) * n); + memmove(smp + i + 1, smp + i, sizeof(salitem_T) * (size_t)n); smp[i] = tsal; } } @@ -1645,7 +1640,7 @@ static int *mb_str2wide(char_u *s) { int i = 0; - int *res = xmalloc((mb_charlen(s) + 1) * sizeof(int)); + int *res = xmalloc(((size_t)mb_charlen(s) + 1) * sizeof(int)); for (char_u *p = s; *p != NUL;) { res[i++] = mb_ptr2char_adv((const char_u **)&p); } @@ -1682,18 +1677,18 @@ static int spell_read_tree(FILE *fd, char_u **bytsp, long *bytsp_len, idx_T **id } if (len > 0) { // Allocate the byte array. - bp = xmalloc(len); + bp = xmalloc((size_t)len); *bytsp = bp; if (bytsp_len != NULL) { *bytsp_len = len; } // Allocate the index array. - ip = xcalloc(len, sizeof(*ip)); + ip = xcalloc((size_t)len, sizeof(*ip)); *idxsp = ip; // Recursively read the tree and store it in the array. - idx = read_tree_node(fd, bp, ip, len, 0, prefixtree, prefixcnt); + idx = read_tree_node(fd, bp, ip, (int)len, 0, prefixtree, prefixcnt); if (idx < 0) { return idx; } @@ -1733,7 +1728,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx if (startidx + len >= maxidx) { return SP_FORMERROR; } - byts[idx++] = len; + byts[idx++] = (char_u)len; // Read the byte values, flag/region bytes and shared indexes. for (i = 1; i <= len; ++i) { @@ -1793,7 +1788,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx c = getc(fd); // <xbyte> } } - byts[idx++] = c; + byts[idx++] = (char_u)c; } // Recursively read the children for non-shared siblings. @@ -1867,7 +1862,7 @@ static long compress_added = 500000; // word count // Sets "sps_flags". int spell_check_msm(void) { - char_u *p = p_msm; + char *p = (char *)p_msm; long start = 0; long incr = 0; long added = 0; @@ -2257,7 +2252,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) if (compflags != NULL) { l += (int)STRLEN(compflags) + 1; } - p = getroom(spin, l, false); + p = getroom(spin, (size_t)l, false); if (compflags != NULL) { STRCPY(p, compflags); STRCAT(p, "/"); @@ -2885,7 +2880,7 @@ static unsigned get_affitem(int flagtype, char_u **pp) res = mb_ptr2char_adv((const char_u **)pp) + (res << 16); } } - return res; + return (unsigned)res; } // Process the "compflags" string used in an affix file and append it to @@ -2911,7 +2906,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla if (spin->si_compflags != NULL) { len += (int)STRLEN(spin->si_compflags) + 1; } - p = getroom(spin, len, false); + p = getroom(spin, (size_t)len, false); if (spin->si_compflags != NULL) { STRCPY(p, spin->si_compflags); STRCAT(p, "/"); @@ -2947,7 +2942,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla ci->ci_newID = id; hash_add(&aff->af_comp, ci->ci_key); } - *tp++ = id; + *tp++ = (char_u)id; } if (aff->af_flagtype == AFT_NUM && *p == ',') { ++p; @@ -2978,15 +2973,15 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) switch (flagtype) { case AFT_CHAR: - return vim_strchr((char *)afflist, flag) != NULL; + return vim_strchr((char *)afflist, (int)flag) != NULL; case AFT_CAPLONG: case AFT_LONG: for (p = afflist; *p != NUL;) { - n = mb_ptr2char_adv((const char_u **)&p); + n = (unsigned)mb_ptr2char_adv((const char_u **)&p); if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) && *p != NUL) { - n = mb_ptr2char_adv((const char_u **)&p) + (n << 16); + n = (unsigned)mb_ptr2char_adv((const char_u **)&p) + (n << 16); } if (n == flag) { return true; @@ -3362,7 +3357,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist if (!HASHITEM_EMPTY(hi)) { id = HI2AH(hi)->ah_newID; if (id != 0) { - store_afflist[cnt++] = id; + store_afflist[cnt++] = (char_u)id; } } } @@ -3393,7 +3388,7 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl STRLCPY(key, prevp, p - prevp + 1); hi = hash_find(&affile->af_comp, (char *)key); if (!HASHITEM_EMPTY(hi)) { - store_afflist[cnt++] = HI2CI(hi)->ci_newID; + store_afflist[cnt++] = (char_u)HI2CI(hi)->ci_newID; } } if (affile->af_flagtype == AFT_NUM && *p == ',') { @@ -3856,11 +3851,10 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align) if (align && bl != NULL) { // Round size up for alignment. On some systems structures need to be // aligned to the size of a pointer (e.g., SPARC). - bl->sb_used = (bl->sb_used + sizeof(char *) - 1) - & ~(sizeof(char *) - 1); + bl->sb_used = (int)(((size_t)bl->sb_used + sizeof(char *) - 1) & ~(sizeof(char *) - 1)); } - if (bl == NULL || bl->sb_used + len > SBLOCKSIZE) { + if (bl == NULL || (size_t)bl->sb_used + len > SBLOCKSIZE) { // Allocate a block of memory. It is not freed until much later. bl = xcalloc(1, (sizeof(sblock_T) + SBLOCKSIZE)); bl->sb_next = spin->si_blocks; @@ -4072,9 +4066,9 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int } if (word[i] == NUL) { - node->wn_flags = flags; - node->wn_region |= region; - node->wn_affixID = affixID; + node->wn_flags = (uint16_t)flags; + node->wn_region |= (int16_t)region; + node->wn_affixID = (char_u)affixID; break; } prev = &node->wn_child; @@ -4298,12 +4292,12 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo // Make a hash key for the node and its siblings, so that we can quickly // find a lookalike node. This must be done after compressing the sibling // list, otherwise the hash key would become invalid by the compression. - node->wn_u1.hashkey[0] = len; + node->wn_u1.hashkey[0] = (char_u)len; nr = 0; for (np = node; np != NULL; np = np->wn_sibling) { if (np->wn_byte == NUL) { // end node: use wn_flags, wn_region and wn_affixID - n = np->wn_flags + (np->wn_region << 8) + (np->wn_affixID << 16); + n = (unsigned)(np->wn_flags + (np->wn_region << 8) + (np->wn_affixID << 16)); } else { // byte node: use the byte value and the child pointer n = (unsigned)(np->wn_byte + ((uintptr_t)np->wn_child << 8)); @@ -4313,13 +4307,13 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo // Avoid NUL bytes, it terminates the hash key. n = nr & 0xff; - node->wn_u1.hashkey[1] = n == 0 ? 1 : n; + node->wn_u1.hashkey[1] = n == 0 ? 1 : (char_u)n; n = (nr >> 8) & 0xff; - node->wn_u1.hashkey[2] = n == 0 ? 1 : n; + node->wn_u1.hashkey[2] = n == 0 ? 1 : (char_u)n; n = (nr >> 16) & 0xff; - node->wn_u1.hashkey[3] = n == 0 ? 1 : n; + node->wn_u1.hashkey[3] = n == 0 ? 1 : (char_u)n; n = (nr >> 24) & 0xff; - node->wn_u1.hashkey[4] = n == 0 ? 1 : n; + node->wn_u1.hashkey[4] = n == 0 ? 1 : (char_u)n; node->wn_u1.hashkey[5] = NUL; // Check for CTRL-C pressed now and then. @@ -4885,7 +4879,7 @@ static int put_node(FILE *fd, wordnode_T *node, int idx, int regionmask, bool pr void ex_mkspell(exarg_T *eap) { int fcount; - char_u **fnames; + char **fnames; char_u *arg = (char_u *)eap->arg; bool ascii = false; @@ -5034,7 +5028,7 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) // We use the "flags" field for the MSB of the wordnr, // "region" for the LSB of the wordnr. if (tree_add_word(spin, tsalword, spin->si_foldroot, - words_done >> 16, words_done & 0xffff, + (int)(words_done >> 16), words_done & 0xffff, 0) == FAIL) { return FAIL; } @@ -5054,7 +5048,7 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) } } else { // Normal char, go one level deeper. - tword[depth++] = c; + tword[depth++] = (char_u)c; arridx[depth] = idxs[n]; curi[depth] = 1; wordcount[depth] = 0; @@ -5170,25 +5164,25 @@ static int offset2bytes(int nr, char_u *buf) b4 = rem / 255 + 1; if (b4 > 1 || b3 > 0x1f) { // 4 bytes - buf[0] = 0xe0 + b4; - buf[1] = b3; - buf[2] = b2; - buf[3] = b1; + buf[0] = (char_u)(0xe0 + b4); + buf[1] = (char_u)b3; + buf[2] = (char_u)b2; + buf[3] = (char_u)b1; return 4; } if (b3 > 1 || b2 > 0x3f) { // 3 bytes - buf[0] = 0xc0 + b3; - buf[1] = b2; - buf[2] = b1; + buf[0] = (char_u)(0xc0 + b3); + buf[1] = (char_u)b2; + buf[2] = (char_u)b1; return 3; } if (b2 > 1 || b1 > 0x7f) { // 2 bytes - buf[0] = 0x80 + b2; - buf[1] = b1; + buf[0] = (char_u)(0x80 + b2); + buf[1] = (char_u)b1; return 2; } // 1 byte - buf[0] = b1; + buf[0] = (char_u)b1; return 1; } @@ -5276,11 +5270,11 @@ theend: /// @param ascii -ascii argument given /// @param over_write overwrite existing output file /// @param added_word invoked through "zg" -static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bool added_word) +static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool added_word) { char_u *fname = NULL; char_u *wfname; - char_u **innames; + char **innames; int incount; afffile_T *(afile[MAXREGIONS]); int i; @@ -5373,9 +5367,8 @@ static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bo semsg(_("E755: Invalid region in %s"), innames[i]); goto theend; } - spin.si_region_name[i * 2] = TOLOWER_ASC(innames[i][len - 2]); - spin.si_region_name[i * 2 + 1] = - TOLOWER_ASC(innames[i][len - 1]); + spin.si_region_name[i * 2] = (char_u)TOLOWER_ASC(innames[i][len - 2]); + spin.si_region_name[i * 2 + 1] = (char_u)TOLOWER_ASC(innames[i][len - 1]); } } spin.si_region_count = incount; @@ -5418,7 +5411,7 @@ static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bo } else { // No .aff file, try reading the file as a word list. Store // the words in the trees. - if (spell_read_wordfile(&spin, innames[i]) == FAIL) { + if (spell_read_wordfile(&spin, (char_u *)innames[i]) == FAIL) { error = true; } } @@ -5529,7 +5522,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo FILE *fd = NULL; buf_T *buf = NULL; bool new_spf = false; - char_u *fname; + char *fname; char_u *fnamebuf = NULL; char_u line[MAXWLEN * 2]; long fpos, fpos_next = 0; @@ -5548,7 +5541,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo return; } } - fname = int_wordlist; + fname = (char *)int_wordlist; } else { // If 'spellfile' isn't set figure out a good default value. if (*curwin->w_s->b_p_spf == NUL) { @@ -5585,13 +5578,13 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo return; } - fname = fnamebuf; + fname = (char *)fnamebuf; } if (what == SPELL_ADD_BAD || undo) { // When the word appears as good word we need to remove that one, // since its flags sort before the one with WF_BANNED. - fd = os_fopen((char *)fname, "r"); + fd = os_fopen(fname, "r"); if (fd != NULL) { while (!vim_fgets(line, MAXWLEN * 2, fd)) { fpos = fpos_next; @@ -5605,14 +5598,14 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo // the start of the line. Mixing reading and writing // doesn't work for all systems, close the file first. fclose(fd); - fd = os_fopen((char *)fname, "r+"); + fd = os_fopen(fname, "r+"); if (fd == NULL) { break; } if (fseek(fd, fpos, SEEK_SET) == 0) { fputc('#', fd); if (undo) { - home_replace(NULL, (char *)fname, (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, fname, (char *)NameBuff, MAXPATHL, true); smsg(_("Word '%.*s' removed from %s"), len, word, NameBuff); } } @@ -5629,7 +5622,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo } if (!undo) { - fd = os_fopen((char *)fname, "a"); + fd = os_fopen(fname, "a"); if (fd == NULL && new_spf) { char_u *p; @@ -5637,16 +5630,16 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo // file. We may need to create the "spell" directory first. We // already checked the runtime directory is writable in // init_spellfile(). - if (!dir_of_file_exists(fname) - && (p = (char_u *)path_tail_with_sep((char *)fname)) != fname) { + if (!dir_of_file_exists((char_u *)fname) + && (p = (char_u *)path_tail_with_sep(fname)) != (char_u *)fname) { int c = *p; // The directory doesn't exist. Try creating it and opening // the file again. *p = NUL; - os_mkdir((char *)fname, 0755); - *p = c; - fd = os_fopen((char *)fname, "a"); + os_mkdir(fname, 0755); + *p = (char_u)c; + fd = os_fopen(fname, "a"); } } @@ -5662,7 +5655,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo } fclose(fd); - home_replace(NULL, (char *)fname, (char *)NameBuff, MAXPATHL, true); + home_replace(NULL, fname, (char *)NameBuff, MAXPATHL, true); smsg(_("Word '%.*s' added to %s"), len, word, NameBuff); } } @@ -5727,19 +5720,19 @@ static void init_spellfile(void) } else { // Create the "spell" directory if it doesn't exist yet. l = (int)STRLEN(buf); - vim_snprintf((char *)buf + l, MAXPATHL - l, "/spell"); + vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, "/spell"); if (os_file_is_writable((char *)buf) != 2) { os_mkdir((char *)buf, 0755); } l = (int)STRLEN(buf); - vim_snprintf((char *)buf + l, MAXPATHL - l, + vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, "/%.*s", (int)(lend - lstart), lstart); } l = (int)STRLEN(buf); fname = LANGP_ENTRY(curwin->w_s->b_langp, 0) ->lp_slang->sl_fname; - vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", + vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, ".%s.add", ((fname != NULL && strstr(path_tail((char *)fname), ".ascii.") != NULL) ? "ascii" @@ -5776,9 +5769,9 @@ static void set_spell_charflags(char_u *flags, int cnt, char_u *fol) if (*p != NUL) { c = mb_ptr2char_adv((const char_u **)&p); - new_st.st_fold[i + 128] = c; + new_st.st_fold[i + 128] = (char_u)c; if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) { - new_st.st_upper[c] = i + 128; + new_st.st_upper[c] = (char_u)(i + 128); } } } @@ -5878,11 +5871,10 @@ static void set_map_str(slang_T *lp, char_u *map) if (c >= 256) { int cl = utf_char2len(c); int headcl = utf_char2len(headc); - char *b; hash_T hash; hashitem_T *hi; - b = xmalloc(cl + headcl + 2); + char *b = xmalloc((size_t)(cl + headcl) + 2); utf_char2bytes(c, b); b[cl] = NUL; utf_char2bytes(headc, b + cl + 1); diff --git a/src/nvim/state.c b/src/nvim/state.c index 6475105192..d6cca71ad8 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -5,10 +5,10 @@ #include "nvim/ascii.h" #include "nvim/autocmd.h" -#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/ex_docmd.h" #include "nvim/getchar.h" +#include "nvim/insexpand.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/main.h" @@ -211,12 +211,15 @@ void get_mode(char *buf) buf[i++] = 'o'; // to be able to detect force-linewise/blockwise/charwise operations buf[i++] = (char)motion_force; + } else if (curbuf->terminal) { + buf[i++] = 't'; + if (restart_edit == 'I') { + buf[i++] = 'T'; + } } else if (restart_edit == 'I' || restart_edit == 'R' || restart_edit == 'V') { buf[i++] = 'i'; buf[i++] = (char)restart_edit; - } else if (curbuf->terminal) { - buf[i++] = 't'; } } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 5c2721536d..22effaade0 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -501,7 +501,7 @@ static int sort_compare(const void *s1, const void *s2) return STRCMP(*(char **)s1, *(char **)s2); } -void sort_strings(char_u **files, int count) +void sort_strings(char **files, int count) { qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare); } @@ -1528,3 +1528,45 @@ char_u *reverse_text(char_u *s) return rev; } + +/// Replace all occurrences of "what" with "rep" in "src". If no replacement happens then NULL is +/// returned otherwise return a newly allocated string. +/// +/// @param[in] src Source text +/// @param[in] what Substring to replace +/// @param[in] rep Substring to replace with +/// +/// @return [allocated] Copy of the string. +char *strrep(const char *src, const char *what, const char *rep) +{ + char *pos = (char *)src; + size_t whatlen = STRLEN(what); + + // Count occurrences + size_t count = 0; + while ((pos = strstr(pos, what)) != NULL) { + count++; + pos += whatlen; + } + + if (count == 0) { + return NULL; + } + + size_t replen = STRLEN(rep); + char *ret = xmalloc(STRLEN(src) + count * (replen - whatlen) + 1); + char *ptr = ret; + while ((pos = strstr(src, what)) != NULL) { + size_t idx = (size_t)(pos - src); + memcpy(ptr, src, idx); + ptr += idx; + STRCPY(ptr, rep); + ptr += replen; + src = pos + whatlen; + } + + // Copy remaining + STRCPY(ptr, src); + + return ret; +} diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 43dbeccf01..4ec4a57d68 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -18,6 +18,7 @@ #include "nvim/charset.h" #include "nvim/cursor_shape.h" #include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/fileio.h" @@ -226,12 +227,10 @@ static int current_sub_char = 0; #define MAX_SYN_INC_TAG 999 // maximum before the above overflow #define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER) -/* - * Annoying Hack(TM): ":syn include" needs this pointer to pass to - * expand_filename(). Most of the other syntax commands don't need it, so - * instead of passing it to them, we stow it here. - */ -static char_u **syn_cmdlinep; +// Annoying Hack(TM): ":syn include" needs this pointer to pass to +// expand_filename(). Most of the other syntax commands don't need it, so +// instead of passing it to them, we stow it here. +static char **syn_cmdlinep; /* * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d @@ -343,7 +342,7 @@ void syntax_start(win_T *wp, linenr_T lnum) linenr_T parsed_lnum; linenr_T first_stored; int dist; - static int changedtick = 0; // remember the last change ID + static varnumber_T changedtick = 0; // remember the last change ID current_sub_char = NUL; @@ -993,11 +992,10 @@ void syn_stack_free_all(synblock_T *block) */ static void syn_stack_alloc(void) { - long len; synstate_T *to, *from; synstate_T *sstp; - len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2; + int len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2; if (len < SST_MIN_ENTRIES) { len = SST_MIN_ENTRIES; } else if (len > SST_MAX_ENTRIES) { @@ -1024,7 +1022,7 @@ static void syn_stack_alloc(void) } assert(len >= 0); - sstp = xcalloc(len, sizeof(synstate_T)); + sstp = xcalloc((size_t)len, sizeof(synstate_T)); to = sstp - 1; if (syn_block->b_sst_array != NULL) { @@ -1304,7 +1302,7 @@ static synstate_T *store_current_state(void) } for (i = 0; i < sp->sst_stacksize; ++i) { bp[i].bs_idx = CUR_STATE(i).si_idx; - bp[i].bs_flags = CUR_STATE(i).si_flags; + bp[i].bs_flags = (int)CUR_STATE(i).si_flags; bp[i].bs_seqnr = CUR_STATE(i).si_seqnr; bp[i].bs_cchar = CUR_STATE(i).si_cchar; bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch); @@ -1392,24 +1390,21 @@ static bool syn_stack_equal(synstate_T *sp) if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch) { continue; } - // When the extmatch pointers are different, the strings in - // them can still be the same. Check if the extmatch - // references are equal. + // When the extmatch pointers are different, the strings in them can + // still be the same. Check if the extmatch references are equal. bsx = bp[i].bs_extmatch; six = CUR_STATE(i).si_extmatch; - // If one of the extmatch pointers is NULL the states are - // different. + // If one of the extmatch pointers is NULL the states are different. if (bsx == NULL || six == NULL) { break; } int j; for (j = 0; j < NSUBEXP; j++) { - // Check each referenced match string. They must all be - // equal. + // Check each referenced match string. They must all be equal. if (bsx->matches[j] != six->matches[j]) { - // If the pointer is different it can still be the - // same text. Compare the strings, ignore case when - // the start item has the sp_ic flag set. + // If the pointer is different it can still be the same text. + // Compare the strings, ignore case when the start item has the + // sp_ic flag set. if (bsx->matches[j] == NULL || six->matches[j] == NULL) { break; } @@ -1424,11 +1419,7 @@ static bool syn_stack_equal(synstate_T *sp) break; } } - if (i < 0) { - return true; - } - - return false; + return i < 0 ? true : false; } /* @@ -2043,7 +2034,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con current_attr = sip->si_attr; current_id = sip->si_id; current_trans_id = sip->si_trans_id; - current_flags = sip->si_flags; + current_flags = (int)sip->si_flags; current_seqnr = sip->si_seqnr; current_sub_char = sip->si_cchar; break; @@ -2065,7 +2056,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP); } else { sps.inc_tag = 0; - sps.id = syn_block->b_nospell_cluster_id; + sps.id = (int16_t)syn_block->b_nospell_cluster_id; sps.cont_in_list = NULL; *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0); } @@ -2078,12 +2069,12 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP); } else { sps.inc_tag = 0; - sps.id = syn_block->b_spell_cluster_id; + sps.id = (int16_t)syn_block->b_spell_cluster_id; sps.cont_in_list = NULL; *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0); if (syn_block->b_nospell_cluster_id != 0) { - sps.id = syn_block->b_nospell_cluster_id; + sps.id = (int16_t)syn_block->b_nospell_cluster_id; if (in_id_list(sip, sip->si_cont_list, &sps, 0)) { *can_spell = false; } @@ -2289,7 +2280,7 @@ static void check_state_ends(void) // handle next_list, unless at end of line and no "skipnl" or // "skipempty" current_next_list = cur_si->si_next_list; - current_next_flags = cur_si->si_flags; + current_next_flags = (int)cur_si->si_flags; if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)) && syn_getcurline()[current_col] == NUL) { current_next_list = NULL; @@ -2899,7 +2890,6 @@ static char_u *syn_getcurline(void) */ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st) { - int r; int timed_out = 0; proftime_T pt; const bool l_syn_time_on = syn_time_on; @@ -2915,9 +2905,8 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T return false; } - rmp->rmm_maxcol = syn_buf->b_p_smc; - r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, - syn_tm, &timed_out); + rmp->rmm_maxcol = (colnr_T)syn_buf->b_p_smc; + long r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, syn_tm, &timed_out); if (l_syn_time_on) { pt = profile_end(pt); @@ -3294,9 +3283,8 @@ static void syn_remove_pattern(synblock_T *block, int idx) --block->b_syn_folditems; } syn_clear_pattern(block, idx); - memmove(spp, spp + 1, - sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1)); - --block->b_syn_patterns.ga_len; + memmove(spp, spp + 1, sizeof(synpat_T) * (size_t)(block->b_syn_patterns.ga_len - idx - 1)); + block->b_syn_patterns.ga_len--; } /* @@ -3382,7 +3370,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list); } } else { - id = syn_name2id_len((char *)arg, (int)(arg_end - arg)); + id = syn_name2id_len((char *)arg, (size_t)(arg_end - arg)); if (id == 0) { semsg(_(e_nogroup), arg); break; @@ -3553,7 +3541,7 @@ static void syn_cmd_list(exarg_T *eap, int syncing) syn_list_cluster(id - SYNID_CLUSTER); } } else { - int id = syn_name2id_len((char *)arg, (int)(arg_end - arg)); + int id = syn_name2id_len((char *)arg, (size_t)(arg_end - arg)); if (id == 0) { semsg(_(e_nogroup), arg); } else { @@ -4010,7 +3998,7 @@ static void add_keyword(char_u *const name, const int id, const int flags, keyentry_T *const kp = xmalloc(sizeof(keyentry_T) + STRLEN(name_ic)); STRCPY(kp->keyword, name_ic); - kp->k_syn.id = id; + kp->k_syn.id = (int16_t)id; kp->k_syn.inc_tag = current_syn_inc_tag; kp->flags = flags; kp->k_char = conceal_char; @@ -4193,7 +4181,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha if (gname_start == arg) { return NULL; } - gname = vim_strnsave(gname_start, arg - gname_start); + gname = vim_strnsave(gname_start, (size_t)(arg - gname_start)); if (STRCMP(gname, "NONE") == 0) { *opt->sync_idx = NONE_IDX; } else { @@ -4242,7 +4230,7 @@ static void syn_incl_toplevel(int id, int *flagsp) int16_t *grp_list = xmalloc(2 * sizeof(*grp_list)); int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER; - grp_list[0] = id; + grp_list[0] = (int16_t)id; grp_list[1] = 0; syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list, CLUSTER_ADD); @@ -4345,7 +4333,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) if (eap->skip) { syn_id = -1; } else { - syn_id = syn_check_group((char *)arg, (int)(group_name_end - arg)); + syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg)); } if (syn_id != 0) { // Allocate a buffer, for removing backslashes in the keyword. @@ -4411,7 +4399,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) } const int l = utfc_ptr2len((char *)p + 1); - memmove(p, p + 1, l); + memmove(p, p + 1, (size_t)l); p += l; } } @@ -4482,7 +4470,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing) if (!ends_excmd(*rest) || eap->skip) { rest = NULL; } else { - if ((syn_id = syn_check_group((char *)arg, (int)(group_name_end - arg))) != 0) { + if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) { syn_incl_toplevel(syn_id, &syn_opt_arg.flags); /* * Store the pattern in the syn_items list @@ -4492,7 +4480,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing) *spp = item; spp->sp_syncing = syncing; spp->sp_type = SPTYPE_MATCH; - spp->sp_syn.id = syn_id; + spp->sp_syn.id = (int16_t)syn_id; spp->sp_syn.inc_tag = current_syn_inc_tag; spp->sp_flags = syn_opt_arg.flags; spp->sp_sync_idx = sync_idx; @@ -4598,7 +4586,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) ++key_end; } xfree(key); - key = vim_strnsave_up(rest, key_end - rest); + key = vim_strnsave_up(rest, (size_t)(key_end - rest)); if (STRCMP(key, "MATCHGROUP") == 0) { item = ITEM_MATCHGROUP; } else if (STRCMP(key, "START") == 0) { @@ -4631,7 +4619,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) { matchgroup_id = 0; } else { - matchgroup_id = syn_check_group((char *)rest, (int)(p - rest)); + matchgroup_id = syn_check_group((char *)rest, (size_t)(p - rest)); if (matchgroup_id == 0) { illegal = true; break; @@ -4690,7 +4678,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) rest = NULL; } else { ga_grow(&(curwin->w_s->b_syn_patterns), pat_count); - if ((syn_id = syn_check_group((char *)arg, (int)(group_name_end - arg))) != 0) { + if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) { syn_incl_toplevel(syn_id, &syn_opt_arg.flags); /* * Store the start/skip/end in the syn_items list @@ -4704,11 +4692,10 @@ static void syn_cmd_region(exarg_T *eap, int syncing) (item == ITEM_START) ? SPTYPE_START : (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END; SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags; - SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id; + SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = (int16_t)syn_id; SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag; - SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id = - ppp->pp_matchgroup_id; + SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id = (int16_t)ppp->pp_matchgroup_id; SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char; if (item == ITEM_START) { SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = @@ -4878,7 +4865,7 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, con clstr = NULL; break; } - clstr = xmalloc((count + 1) * sizeof(*clstr)); + clstr = xmalloc(((size_t)count + 1) * sizeof(*clstr)); clstr[count] = 0; } } @@ -4913,7 +4900,7 @@ static int syn_scl_name2id(char_u *name) */ static int syn_scl_namen2id(char_u *linep, int len) { - char_u *name = vim_strnsave(linep, len); + char_u *name = vim_strnsave(linep, (size_t)len); int id = syn_scl_name2id(name); xfree(name); @@ -4926,12 +4913,8 @@ static int syn_scl_namen2id(char_u *linep, int len) // Return 0 for failure. static int syn_check_cluster(char_u *pp, int len) { - int id; - char_u *name; - - name = vim_strnsave(pp, len); - - id = syn_scl_name2id(name); + char_u *name = vim_strnsave(pp, (size_t)len); + int id = syn_scl_name2id(name); if (id == 0) { // doesn't exist yet id = syn_add_cluster(name); } else { @@ -5081,7 +5064,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) return NULL; } // store the pattern and compiled regexp program - ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1); + ci->sp_pattern = vim_strnsave(arg + 1, (size_t)(end - arg) - 1); // Make 'cpoptions' empty, to avoid the 'l' flag cpo_save = p_cpo; @@ -5120,7 +5103,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) } } if (idx >= 0) { - ci->sp_off_flags |= (1 << idx); + ci->sp_off_flags |= (int16_t)(1 << idx); if (idx == SPO_LC_OFF) { // lc=99 end += 3; *p = getdigits_int((char **)&end, true, 0); @@ -5164,9 +5147,8 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) char_u *arg_end; char_u *key = NULL; char_u *next_arg; - int illegal = FALSE; - int finished = FALSE; - long n; + int illegal = false; + int finished = false; char *cpo_save; if (ends_excmd(*arg_start)) { @@ -5178,7 +5160,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) arg_end = skiptowhite(arg_start); next_arg = (char_u *)skipwhite((char *)arg_end); xfree(key); - key = vim_strnsave_up(arg_start, arg_end - arg_start); + key = vim_strnsave_up(arg_start, (size_t)(arg_end - arg_start)); if (STRCMP(key, "CCOMMENT") == 0) { if (!eap->skip) { curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; @@ -5186,11 +5168,12 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) if (!ends_excmd(*next_arg)) { arg_end = skiptowhite(next_arg); if (!eap->skip) { - curwin->w_s->b_syn_sync_id = syn_check_group((char *)next_arg, (int)(arg_end - next_arg)); + curwin->w_s->b_syn_sync_id = + (int16_t)syn_check_group((char *)next_arg, (size_t)(arg_end - next_arg)); } next_arg = (char_u *)skipwhite((char *)arg_end); } else if (!eap->skip) { - curwin->w_s->b_syn_sync_id = syn_name2id("Comment"); + curwin->w_s->b_syn_sync_id = (int16_t)syn_name2id("Comment"); } } else if (STRNCMP(key, "LINES", 5) == 0 || STRNCMP(key, "MINLINES", 8) == 0 @@ -5207,7 +5190,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) illegal = TRUE; break; } - n = getdigits_long(&arg_end, false, 0); + linenr_T n = getdigits_int32((char **)&arg_end, false, 0); if (!eap->skip) { if (key[4] == 'B') { curwin->w_s->b_syn_sync_linebreaks = n; @@ -5241,7 +5224,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) if (!eap->skip) { // store the pattern and compiled regexp program curwin->w_s->b_syn_linecont_pat = - vim_strnsave(next_arg + 1, arg_end - next_arg - 1); + vim_strnsave(next_arg + 1, (size_t)(arg_end - next_arg) - 1); curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; // Make 'cpoptions' empty, to avoid the 'l' flag @@ -5326,7 +5309,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis int count = 0; do { for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {} - char *const name = xmalloc(end - p + 3); // leave room for "^$" + char *const name = xmalloc((size_t)(end - p) + 3); // leave room for "^$" STRLCPY(name + 1, p, end - p + 1); if (STRCMP(name + 1, "ALLBUT") == 0 || STRCMP(name + 1, "ALL") == 0 @@ -5367,7 +5350,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis * Handle full group name. */ if (strpbrk(name + 1, "\\.*^$~[") == NULL) { - id = syn_check_group((name + 1), (int)(end - p)); + id = syn_check_group((name + 1), (size_t)(end - p)); } else { // Handle match of regexp with group names. *name = '^'; @@ -5392,7 +5375,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis xfree(retval); round = 1; } else { - retval[count] = i + 1; // -V522 + retval[count] = (int16_t)(i + 1); // -V522 } } count++; @@ -5415,7 +5398,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis xfree(retval); round = 1; } else { - retval[count] = id; + retval[count] = (int16_t)id; } } ++count; @@ -5430,7 +5413,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis break; } if (round == 1) { - retval = xmalloc((count + 1) * sizeof(*retval)); + retval = xmalloc(((size_t)count + 1) * sizeof(*retval)); retval[count] = 0; // zero means end of the list total_count = count; } @@ -5461,7 +5444,7 @@ static int16_t *copy_id_list(const int16_t *const list) int count; for (count = 0; list[count]; count++) {} - const size_t len = (count + 1) * sizeof(int16_t); + const size_t len = ((size_t)count + 1) * sizeof(int16_t); int16_t *const retval = xmalloc(len); memmove(retval, list, len); @@ -5610,11 +5593,11 @@ void ex_syntax(exarg_T *eap) char_u *arg = (char_u *)eap->arg; char_u *subcmd_end; - syn_cmdlinep = (char_u **)eap->cmdlinep; + syn_cmdlinep = eap->cmdlinep; // isolate subcommand name for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) {} - char_u *const subcmd_name = vim_strnsave(arg, subcmd_end - arg); + char_u *const subcmd_name = vim_strnsave(arg, (size_t)(subcmd_end - arg)); if (eap->skip) { // skip error messages for all subcommands emsg_skip++; } @@ -5785,7 +5768,7 @@ char *get_syntax_name(expand_T *xp, int idx) /// @param trans remove transparency /// @param spellp return: can do spell checking /// @param keep_state keep state of char at "col" -int syn_get_id(win_T *wp, long lnum, colnr_T col, int trans, bool *spellp, int keep_state) +int syn_get_id(win_T *wp, linenr_T lnum, colnr_T col, int trans, bool *spellp, int keep_state) { // When the position is not after the current position and in the same // line of the same window with the same buffer, need to restart parsing. @@ -5867,10 +5850,8 @@ static int syn_cur_foldlevel(void) return level; } -/* - * Function called to get folding level for line "lnum" in window "wp". - */ -int syn_get_foldlevel(win_T *wp, long lnum) +/// Function called to get folding level for line "lnum" in window "wp". +int syn_get_foldlevel(win_T *wp, linenr_T lnum) { int level = 0; @@ -5900,7 +5881,7 @@ int syn_get_foldlevel(win_T *wp, long lnum) } } if (level > wp->w_p_fdn) { - level = wp->w_p_fdn; + level = (int)wp->w_p_fdn; if (level < 0) { level = 0; } @@ -6000,11 +5981,11 @@ static void syntime_report(void) p = GA_APPEND_VIA_PTR(time_entry_T, &ga); p->total = spp->sp_time.total; total_total = profile_add(total_total, spp->sp_time.total); - p->count = spp->sp_time.count; - p->match = spp->sp_time.match; - total_count += spp->sp_time.count; + p->count = (int)spp->sp_time.count; + p->match = (int)spp->sp_time.match; + total_count += (int)spp->sp_time.count; p->slowest = spp->sp_time.slowest; - proftime_T tm = profile_divide(spp->sp_time.total, spp->sp_time.count); + proftime_T tm = profile_divide(spp->sp_time.total, (int)spp->sp_time.count); p->average = tm; p->id = spp->sp_syn.id; p->pattern = spp->sp_pattern; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 28b3b6c1ef..5b799be381 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -26,6 +26,7 @@ #include "nvim/garray.h" #include "nvim/if_cscope.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -164,7 +165,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) fmark_T saved_fmark; bool jumped_to_tag = false; int new_num_matches; - char_u **new_matches; + char **new_matches; int use_tagstack; int skip_msg = false; char_u *buf_ffname = (char_u *)curbuf->b_ffname; // name for priority computation @@ -173,7 +174,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) // remember the matches for the last used tag static int num_matches = 0; static int max_num_matches = 0; // limit used for match search - static char_u **matches = NULL; + static char **matches = NULL; static int flags; if (tfu_in_use) { @@ -497,15 +498,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) // Find the position of each old match in the new list. Need // to use parse_match() to find the tag line. for (j = 0; j < num_matches; j++) { - parse_match(matches[j], &tagp); - for (i = idx; i < new_num_matches; ++i) { - parse_match(new_matches[i], &tagp2); + parse_match((char_u *)matches[j], &tagp); + for (i = idx; i < new_num_matches; i++) { + parse_match((char_u *)new_matches[i], &tagp2); if (STRCMP(tagp.tagname, tagp2.tagname) == 0) { - char_u *p = new_matches[i]; + char_u *p = (char_u *)new_matches[i]; for (k = i; k > idx; k--) { new_matches[k] = new_matches[k - 1]; } - new_matches[idx++] = p; + new_matches[idx++] = (char *)p; break; } } @@ -580,7 +581,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) tagstack[tagstackidx].cur_fnum = cur_fnum; // store user-provided data originating from tagfunc - if (use_tfu && parse_match(matches[cur_match], &tagp2) == OK + if (use_tfu && parse_match((char_u *)matches[cur_match], &tagp2) == OK && tagp2.user_data) { XFREE_CLEAR(tagstack[tagstackidx].user_data); tagstack[tagstackidx].user_data = vim_strnsave(tagp2.user_data, @@ -639,7 +640,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) /* * Jump to the desired match. */ - i = jumpto_tag(matches[cur_match], forceit, type != DT_CSCOPE); + i = jumpto_tag((char_u *)matches[cur_match], forceit, type != DT_CSCOPE); set_vim_var_string(VV_SWAPCOMMAND, NULL, -1); @@ -686,10 +687,8 @@ end_do_tag: return jumped_to_tag; } -// // List all the matching tags. -// -static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_u **matches) +static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char **matches) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -702,7 +701,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_ // Assume that the first match indicates how long the tags can // be, and align the file names to that. - parse_match(matches[0], &tagp); + parse_match((char_u *)matches[0], &tagp); taglen = (int)(tagp.tagname_end - tagp.tagname + 2); if (taglen < 18) { taglen = 18; @@ -720,7 +719,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_ msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); for (i = 0; i < num_matches && !got_int; i++) { - parse_match(matches[i], &tagp); + parse_match((char_u *)matches[i], &tagp); if (!new_tag && ( (g_do_tagpreview != 0 && i == ptag_entry.cur_match) @@ -873,11 +872,9 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_ } } -// -// Add the matching tags to the location list for the current -// window. -// -static int add_llist_tags(char_u *tag, int num_matches, char_u **matches) +/// Add the matching tags to the location list for the current +/// window. +static int add_llist_tags(char_u *tag, int num_matches, char **matches) { list_T *list; char_u tag_name[128 + 1]; @@ -896,7 +893,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char_u **matches) long lnum; dict_T *dict; - parse_match(matches[i], &tagp); + parse_match((char_u *)matches[i], &tagp); // Save the tag name len = (int)(tagp.tagname_end - tagp.tagname); @@ -1365,7 +1362,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl /// @param matchesp return: array of matches found /// @param mincount MAXCOL: find all matches other: minimal number of matches */ /// @param buf_ffname name of buffer for priority -int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int mincount, +int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mincount, char_u *buf_ffname) { FILE *fp; @@ -1419,7 +1416,7 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int hashtab_T ht_match[MT_COUNT]; // stores matches by key hash_T hash = 0; int match_count = 0; // number of matches found - char_u **matches; + char **matches; int mtt; int help_save; int help_pri = 0; @@ -1648,7 +1645,7 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int if ((flags & TAG_INS_COMP)) { // Double brackets for gcc ins_compl_check_keys(30, false); } - if (got_int || compl_interrupted) { + if (got_int || ins_compl_interrupted()) { stop_searching = true; break; } @@ -2283,7 +2280,7 @@ findtag_end: } } } - matches[match_count++] = mfp; + matches[match_count++] = (char *)mfp; } } @@ -3093,7 +3090,7 @@ static void tagstack_clear_entry(taggy_T *item) } /// @param tagnames expand tag names -int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file) +int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file) { int i; int extra_flag; @@ -3124,7 +3121,7 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file) for (i = 0; i < *num_file; i++) { size_t len; - parse_match((*file)[i], &t_p); + parse_match((char_u *)(*file)[i], &t_p); len = (size_t)(t_p.tagname_end - t_p.tagname); if (len > name_buf_size - 3) { char_u *buf; @@ -3195,7 +3192,7 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta int get_tags(list_T *list, char_u *pat, char_u *buf_fname) { int num_matches, i, ret; - char_u **matches; + char **matches; char_u *full_fname; dict_T *dict; tagptrs_T tp; @@ -3204,8 +3201,8 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname) ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname); if (ret == OK && num_matches > 0) { - for (i = 0; i < num_matches; ++i) { - int parse_result = parse_match(matches[i], &tp); + for (i = 0; i < num_matches; i++) { + int parse_result = parse_match((char_u *)matches[i], &tp); // Avoid an unused variable warning in release builds. (void)parse_result; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index be49048aec..c23aff00cb 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -82,6 +82,7 @@ typedef struct terminal_state { int save_rd; // saved value of RedrawingDisabled bool close; bool got_bsl; // if the last input was <C-\> + bool got_bsl_o; // if left terminal mode with <c-\><c-o> } TerminalState; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -388,12 +389,11 @@ void terminal_check_size(Terminal *term) } /// Implements MODE_TERMINAL state. :help Terminal-mode -void terminal_enter(void) +bool terminal_enter(void) { buf_T *buf = curbuf; assert(buf->terminal); // Should only be called when curbuf has a terminal. - TerminalState state, *s = &state; - memset(s, 0, sizeof(TerminalState)); + TerminalState s[1] = { 0 }; s->term = buf->terminal; stop_insert_mode = false; @@ -443,7 +443,9 @@ void terminal_enter(void) s->state.check = terminal_check; state_enter(&s->state); - restart_edit = 0; + if (!s->got_bsl_o) { + restart_edit = 0; + } State = save_state; RedrawingDisabled = s->save_rd; apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf); @@ -451,7 +453,7 @@ void terminal_enter(void) if (save_curwin == curwin->handle) { // Else: window was closed. curwin->w_p_cul = save_w_p_cul; if (save_w_p_culopt) { - xfree(curwin->w_p_culopt); + free_string_option(curwin->w_p_culopt); curwin->w_p_culopt = save_w_p_culopt; } curwin->w_p_culopt_flags = save_w_p_culopt_flags; @@ -459,7 +461,7 @@ void terminal_enter(void) curwin->w_p_so = save_w_p_so; curwin->w_p_siso = save_w_p_siso; } else if (save_w_p_culopt) { - xfree(save_w_p_culopt); + free_string_option(save_w_p_culopt); } // draw the unfocused cursor @@ -467,7 +469,11 @@ void terminal_enter(void) if (curbuf->terminal == s->term && !s->close) { terminal_check_cursor(); } - unshowmode(true); + if (restart_edit) { + showmode(); + } else { + unshowmode(true); + } ui_busy_stop(); if (s->close) { bool wipe = s->term->buf_handle != 0; @@ -477,6 +483,8 @@ void terminal_enter(void) do_cmdline_cmd("bwipeout!"); } } + + return s->got_bsl_o; } static void terminal_check_cursor(void) @@ -564,6 +572,14 @@ static int terminal_execute(VimState *state, int key) } FALLTHROUGH; + case Ctrl_O: + if (s->got_bsl) { + s->got_bsl_o = true; + restart_edit = 'I'; + return 0; + } + FALLTHROUGH; + default: if (key == Ctrl_BSL && !s->got_bsl) { s->got_bsl = true; @@ -1384,7 +1400,7 @@ static void fetch_row(Terminal *term, int row, int end_col) fetch_cell(term, row, col, &cell); if (cell.chars[0]) { int cell_len = 0; - for (int i = 0; cell.chars[i]; i++) { + for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell.chars[i]; i++) { cell_len += utf_char2bytes((int)cell.chars[i], ptr + cell_len); } ptr += cell_len; diff --git a/src/nvim/testdir/README.txt b/src/nvim/testdir/README.txt new file mode 100644 index 0000000000..b8bc52f1e6 --- /dev/null +++ b/src/nvim/testdir/README.txt @@ -0,0 +1,121 @@ +This directory contains tests for various Vim features. +For testing an indent script see runtime/indent/testdir/README.txt. + +If it makes sense, add a new test method to an already existing file. You may +want to separate it from other tests with comment lines. + +TO ADD A NEW STYLE TEST: + +1) Create a test_<subject>.vim file. +2) Add test_<subject>.res to NEW_TESTS_RES in Make_all.mak in alphabetical + order. +3) Also add an entry "test_<subject>" to NEW_TESTS in Make_all.mak. +4) Use make test_<subject> to run a single test. + +At 2), instead of running the test separately, it can be included in +"test_alot". Do this for quick tests without side effects. The test runs a +bit faster, because Vim doesn't have to be started, one Vim instance runs many +tests. + +At 4), to run a test in GUI, add "GUI_FLAG=-g" to the make command. + + +What you can use (see test_assert.vim for an example): + +- Call assert_equal(), assert_true(), assert_false(), etc. + +- Use assert_fails() to check for expected errors. + +- Use try/catch to avoid an exception aborts the test. + +- Use test_alloc_fail() to have memory allocation fail. This makes it possible + to check memory allocation failures are handled gracefully. You need to + change the source code to add an ID to the allocation. Add a new one to + alloc_id_T, before aid_last. + +- Use test_override() to make Vim behave differently, e.g. if char_avail() + must return FALSE for a while. E.g. to trigger the CursorMovedI autocommand + event. See test_cursor_func.vim for an example. + +- If the bug that is being tested isn't fixed yet, you can throw an exception + with "Skipped" so that it's clear this still needs work. E.g.: throw + "Skipped: Bug with <c-e> and popupmenu not fixed yet" + +- The following environment variables are recognized and can be set to + influence the behavior of the test suite (see runtest.vim for details) + + - $TEST_MAY_FAIL=Test_channel_one - ignore those failing tests + - $TEST_FILTER=Test_channel - only run test that match this pattern + - $TEST_SKIP_PAT=Test_channel - skip tests that match this pattern + - $TEST_NO_RETRY=yes - do not try to re-run failing tests + You can also set them in Vim: + :let $TEST_MAY_FAIL = 'Test_channel_one' + :let $TEST_FILTER = '_set_mode' + :let $TEST_SKIP_PAT = 'Test_loop_forever' + :let $TEST_NO_RETRY = 'yes' + Use an empty string to revert, e.g.: + :let $TEST_FILTER = '' + +- See the start of runtest.vim for more help. + + +TO ADD A SCREEN DUMP TEST: + +Mostly the same as writing a new style test. Additionally, see help on +"terminal-dumptest". Put the reference dump in "dumps/Test_func_name.dump". + + +OLD STYLE TESTS: + +There are a few tests that are used when Vim was built without the +eval +feature. These cannot use the "assert" functions, therefore they consist of a +.in file that contains Normal mode commands between STARTTEST and ENDTEST. +They modify the file and the result gets written in the test.out file. This +is then compared with the .ok file. If they are equal the test passed. If +they differ the test failed. + + +RUNNING THE TESTS: + +To run a single test from the src directory: + + $ make test_<name> + +The below commands should be run from the src/testdir directory. + +To run a single test: + + $ make test_<name>.res + +The file 'messages' contains the messages generated by the test script. If a +test fails, then the test.log file contains the error messages. If all the +tests are successful, then this file will be an empty file. + +- To run a single test function from a test script: + + $ ../vim -u NONE -S runtest.vim <test_file>.vim <function_name> + +- To execute only specific test functions, add a second argument: + + $ ../vim -u NONE -S runtest.vim test_channel.vim open_delay + + +- To run all the tests: + + $ make + +- To run the test on MS-Windows using the MSVC nmake: + + > nmake -f Make_dos.mak + +- To run the tests with GUI Vim: + + $ make GUI_FLAG=-g + + or + + $ make VIMPROG=../gvim + +- To cleanup the temporary files after running the tests: + + $ make clean diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index e6c0762729..9d6fc5e526 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -4,6 +4,7 @@ if exists('s:did_load') set complete=.,w,b,u,t,i set directory& set directory^=. + set display= set fillchars=vert:\|,fold:- set formatoptions=tcq set fsync diff --git a/src/nvim/testdir/term_util.vim b/src/nvim/testdir/term_util.vim index 3a838a3a1f..5ff09e25b4 100644 --- a/src/nvim/testdir/term_util.vim +++ b/src/nvim/testdir/term_util.vim @@ -9,3 +9,5 @@ func CanRunVimInTerminal() " Nvim: always false, we use Lua screen-tests instead. return 0 endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 43a519bc84..966b8ef571 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -10,7 +10,6 @@ source test_ex_z.vim source test_ex_mode.vim source test_expand.vim source test_expand_func.vim -source test_feedkeys.vim source test_file_perm.vim source test_fnamemodify.vim source test_ga.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 438851a0ad..1c2f86a584 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2170,6 +2170,57 @@ func Test_autocmd_nested() call assert_fails('au WinNew * nested nested echo bad', 'E983:') endfunc +func Test_autocmd_nested_cursor_invalid() + set laststatus=0 + copen + cclose + call setline(1, ['foo', 'bar', 'baz']) + 3 + augroup nested_inv + autocmd User foo ++nested copen + autocmd BufAdd * let &laststatus = 2 - &laststatus + augroup END + doautocmd User foo + + augroup nested_inv + au! + augroup END + set laststatus& + cclose + bwipe! +endfunc + +func Test_autocmd_nested_keeps_cursor_pos() + enew + call setline(1, 'foo') + autocmd User foo ++nested normal! $a + autocmd InsertLeave * : + doautocmd User foo + call assert_equal([0, 1, 3, 0], getpos('.')) + + bwipe! +endfunc + +func Test_autocmd_nested_switch_window() + " run this in a separate Vim so that SafeState works + CheckRunVimInTerminal + + let lines =<< trim END + vim9script + ['()']->writefile('Xautofile') + autocmd VimEnter * ++nested edit Xautofile | split + autocmd BufReadPost * autocmd SafeState * ++once foldclosed('.') + autocmd WinEnter * matchadd('ErrorMsg', 'pat') + END + call writefile(lines, 'Xautoscript') + let buf = RunVimInTerminal('-S Xautoscript', {'rows': 10}) + call VerifyScreenDump(buf, 'Test_autocmd_nested_switch', {}) + + call StopVimInTerminal(buf) + call delete('Xautofile') + call delete('Xautoscript') +endfunc + func Test_autocmd_once() " Without ++once WinNew triggers twice let g:did_split = 0 @@ -2851,6 +2902,110 @@ func Test_v_event_readonly() au! TextYankPost endfunc +" Test for ModeChanged pattern +func Test_mode_changes() + let g:index = 0 + let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] + func! TestMode() + call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode")) + call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode")) + call assert_equal(mode(1), get(v:event, "new_mode")) + let g:index += 1 + endfunc + + au ModeChanged * :call TestMode() + let g:n_to_any = 0 + au ModeChanged n:* let g:n_to_any += 1 + call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix') + + let g:V_to_v = 0 + au ModeChanged V:v let g:V_to_v += 1 + call feedkeys("Vv\<C-G>\<esc>", 'tnix') + call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any) + call assert_equal(1, g:V_to_v) + call assert_equal(len(g:mode_seq) - 1, g:index) + + let g:n_to_i = 0 + au ModeChanged n:i let g:n_to_i += 1 + let g:n_to_niI = 0 + au ModeChanged i:niI let g:n_to_niI += 1 + let g:niI_to_i = 0 + au ModeChanged niI:i let g:niI_to_i += 1 + let g:nany_to_i = 0 + au ModeChanged n*:i let g:nany_to_i += 1 + let g:i_to_n = 0 + au ModeChanged i:n let g:i_to_n += 1 + let g:nori_to_any = 0 + au ModeChanged [ni]:* let g:nori_to_any += 1 + let g:i_to_any = 0 + au ModeChanged i:* let g:i_to_any += 1 + let g:index = 0 + let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] + call feedkeys("a\<C-O>l\<esc>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(3, g:nori_to_any) + + if has('terminal') + let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] + call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(5, g:nori_to_any) + endif + + if has('cmdwin') + let g:n_to_c = 0 + au ModeChanged n:c let g:n_to_c += 1 + let g:c_to_n = 0 + au ModeChanged c:n let g:c_to_n += 1 + let g:mode_seq += ['c', 'n', 'c', 'n'] + call feedkeys("q:\<C-C>\<Esc>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(2, g:n_to_c) + call assert_equal(2, g:c_to_n) + unlet g:n_to_c + unlet g:c_to_n + endif + + au! ModeChanged + delfunc TestMode + unlet! g:mode_seq + unlet! g:index + unlet! g:n_to_any + unlet! g:V_to_v + unlet! g:n_to_i + unlet! g:n_to_niI + unlet! g:niI_to_i + unlet! g:nany_to_i + unlet! g:i_to_n + unlet! g:nori_to_any + unlet! g:i_to_any +endfunc + +func Test_recursive_ModeChanged() + au! ModeChanged * norm 0u + sil! norm + au! ModeChanged +endfunc + +func Test_ModeChanged_starts_visual() + " This was triggering ModeChanged before setting VIsual, causing a crash. + au! ModeChanged * norm 0u + sil! norm + + au! ModeChanged +endfunc func Test_noname_autocmd() augroup test_noname_autocmd_group diff --git a/src/nvim/testdir/test_changelist.vim b/src/nvim/testdir/test_changelist.vim index 3741f32e69..3bb22a89b8 100644 --- a/src/nvim/testdir/test_changelist.vim +++ b/src/nvim/testdir/test_changelist.vim @@ -1,12 +1,65 @@ " Tests for the changelist functionality +" When splitting a window the changelist position is wrong. +" Test the changelist position after splitting a window. +" Test for the bug fixed by 7.4.386 +func Test_changelist() + let save_ul = &ul + enew! + call append('$', ['1', '2']) + exe "normal i\<C-G>u" + exe "normal Gkylpa\<C-G>u" + set ul=100 + exe "normal Gylpa\<C-G>u" + set ul=100 + normal gg + vsplit + normal g; + call assert_equal([3, 2], [line('.'), col('.')]) + normal g; + call assert_equal([2, 2], [line('.'), col('.')]) + call assert_fails('normal g;', 'E662:') + new + call assert_fails('normal g;', 'E664:') + %bwipe! + let &ul = save_ul +endfunc + +" Moving a split should not change its changelist index. +func Test_changelist_index_move_split() + exe "norm! iabc\<C-G>u\ndef\<C-G>u\nghi" + vsplit + normal 99g; + call assert_equal(0, getchangelist('%')[1]) + wincmd L + call assert_equal(0, getchangelist('%')[1]) +endfunc + " Tests for the getchangelist() function -func Test_getchangelist() - if !has("jumplist") - return - endif +func Test_changelist_index() + edit Xfile1.txt + exe "normal iabc\<C-G>u\ndef\<C-G>u\nghi" + call assert_equal(3, getchangelist('%')[1]) + " Move one step back in the changelist. + normal 2g; + + hide edit Xfile2.txt + exe "normal iabcd\<C-G>u\ndefg\<C-G>u\nghij" + call assert_equal(3, getchangelist('%')[1]) + " Move to the beginning of the changelist. + normal 99g; + + " Check the changelist indices. + call assert_equal(0, getchangelist('%')[1]) + call assert_equal(1, getchangelist('#')[1]) bwipe! + call delete('Xfile1.txt') + call delete('Xfile2.txt') +endfunc + +func Test_getchangelist() + bwipe! enew call assert_equal([], 10->getchangelist()) call assert_equal([[], 0], getchangelist()) @@ -15,6 +68,7 @@ func Test_getchangelist() call writefile(['line1', 'line2', 'line3'], 'Xfile2.txt') edit Xfile1.txt + let buf_1 = bufnr() exe "normal 1Goline\<C-G>u1.1" exe "normal 3Goline\<C-G>u2.1" exe "normal 5Goline\<C-G>u3.1" @@ -26,6 +80,7 @@ func Test_getchangelist() \ getchangelist('%')) hide edit Xfile2.txt + let buf_2 = bufnr() exe "normal 1GOline\<C-G>u1.0" exe "normal 2Goline\<C-G>u2.0" call assert_equal([[ @@ -37,10 +92,12 @@ func Test_getchangelist() call assert_equal([[ \ {'lnum' : 2, 'col' : 4, 'coladd' : 0}, \ {'lnum' : 4, 'col' : 4, 'coladd' : 0}, - \ {'lnum' : 6, 'col' : 4, 'coladd' : 0}], 3], getchangelist(2)) + \ {'lnum' : 6, 'col' : 4, 'coladd' : 0}], 2], + \ getchangelist(buf_1)) call assert_equal([[ \ {'lnum' : 1, 'col' : 6, 'coladd' : 0}, - \ {'lnum' : 3, 'col' : 6, 'coladd' : 0}], 2], getchangelist(3)) + \ {'lnum' : 3, 'col' : 6, 'coladd' : 0}], 2], + \ getchangelist(buf_2)) bwipe! call delete('Xfile1.txt') diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index db62fe5fa6..edf36b413b 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -1,16 +1,12 @@ " Tests for the +clientserver feature. -if !has('job') || !has('clientserver') - throw 'Skipped: job and/or clientserver feature missing' -endif +source check.vim +CheckFeature job +CheckFeature clientserver source shared.vim -func Test_client_server() - let cmd = GetVimCommand() - if cmd == '' - return - endif +func Check_X11_Connection() if has('x11') if empty($DISPLAY) throw 'Skipped: $DISPLAY is not set' @@ -19,11 +15,19 @@ func Test_client_server() call remote_send('xxx', '') catch if v:exception =~ 'E240:' - throw 'Skipped: no connection to the X server' + throw 'Skipped: no connection to the X server' endif " ignore other errors endtry endif +endfunc + +func Test_client_server() + let cmd = GetVimCommand() + if cmd == '' + return + endif + call Check_X11_Connection() let name = 'XVIMTEST' let cmd .= ' --servername ' . name @@ -34,6 +38,14 @@ func Test_client_server() " When using valgrind it takes much longer. call WaitForAssert({-> assert_match(name, serverlist())}) + if !has('win32') + if RunVim([], [], '--serverlist >Xtest_serverlist') + let lines = readfile('Xtest_serverlist') + call assert_true(index(lines, 'XVIMTEST') >= 0) + endif + call delete('Xtest_serverlist') + endif + eval name->remote_foreground() call remote_send(name, ":let testvar = 'yes'\<CR>") @@ -81,6 +93,10 @@ func Test_client_server() endif let g:testvar = 'myself' call assert_equal('myself', remote_expr(v:servername, 'testvar')) + call remote_send(v:servername, ":let g:testvar2 = 75\<CR>") + call feedkeys('', 'x') + call assert_equal(75, g:testvar2) + call assert_fails('let v = remote_expr(v:servername, "/2")', 'E449:') call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid') call assert_equal('got it', g:myserverid->remote_read(2)) @@ -101,6 +117,55 @@ func Test_client_server() call assert_equal('another', g:peek_result) call assert_equal('another', remote_read(g:myserverid, 2)) + if !has('gui_running') + " In GUI vim, the following tests display a dialog box + + let cmd = GetVimProg() .. ' --servername ' .. name + + " Run a separate instance to send a command to the server + call remote_expr(name, 'execute("only")') + call system(cmd .. ' --remote-send ":new Xfile<CR>"') + call assert_equal('2', remote_expr(name, 'winnr("$")')) + call assert_equal('Xfile', remote_expr(name, 'winbufnr(1)->bufname()')) + call remote_expr(name, 'execute("only")') + + " Invoke a remote-expr. On MS-Windows, the returned value has a carriage + " return. + let l = system(cmd .. ' --remote-expr "2 + 2"') + call assert_equal(['4'], split(l, "\n")) + + " Edit multiple files using --remote + call system(cmd .. ' --remote Xfile1 Xfile2 Xfile3') + call assert_equal("Xfile1\nXfile2\nXfile3\n", remote_expr(name, 'argv()')) + eval name->remote_send(":%bw!\<CR>") + + " Edit files in separate tab pages + call system(cmd .. ' --remote-tab Xfile1 Xfile2 Xfile3') + call assert_equal('3', remote_expr(name, 'tabpagenr("$")')) + call assert_equal('Xfile2', remote_expr(name, 'bufname(tabpagebuflist(2)[0])')) + eval name->remote_send(":%bw!\<CR>") + + " Edit a file using --remote-wait + eval name->remote_send(":source $VIMRUNTIME/plugin/rrhelper.vim\<CR>") + call system(cmd .. ' --remote-wait +enew Xfile1') + call assert_equal("Xfile1", remote_expr(name, 'bufname("#")')) + eval name->remote_send(":%bw!\<CR>") + + " Edit files using --remote-tab-wait + call system(cmd .. ' --remote-tabwait +tabonly\|enew Xfile1 Xfile2') + call assert_equal('1', remote_expr(name, 'tabpagenr("$")')) + eval name->remote_send(":%bw!\<CR>") + + " Error cases + if v:lang == "C" || v:lang =~ '^[Ee]n' + let l = split(system(cmd .. ' --remote +pwd'), "\n") + call assert_equal("Argument missing after: \"+pwd\"", l[1]) + endif + let l = system(cmd .. ' --remote-expr "abcd"') + call assert_match('^E449: ', l) + endif + + eval name->remote_send(":%bw!\<CR>") eval name->remote_send(":qa!\<CR>") try call WaitForAssert({-> assert_equal("dead", job_status(job))}) @@ -111,8 +176,8 @@ func Test_client_server() endif endtry - call assert_fails("let x=remote_peek([])", 'E730:') - call assert_fails("let x=remote_read('vim10')", 'E277:') + call assert_fails("let x = remote_peek([])", 'E730:') + call assert_fails("let x = remote_read('vim10')", 'E277:') endfunc " Uncomment this line to get a debugging log diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 276bb7fb71..7aac731709 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -642,6 +642,9 @@ func Test_cmdline_remove_char() call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx') call assert_equal('"def', @:, e) + + " This was going before the start in latin1. + call feedkeys(": \<C-W>\<CR>", 'tx') endfor let &encoding = encoding_save @@ -704,6 +707,16 @@ func Test_cmdline_complete_user_cmd() delcommand Foo endfunc +func Test_complete_user_cmd() + command FooBar echo 'global' + command -buffer FooBar echo 'local' + call feedkeys(":Foo\<C-A>\<Home>\"\<CR>", 'tx') + call assert_equal('"FooBar', @:) + + delcommand -buffer FooBar + delcommand FooBar +endfunc + func s:ScriptLocalFunction() echo 'yes' endfunc @@ -1846,4 +1859,20 @@ func Test_long_error_message() silent! norm Q00000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 endfunc +func Test_cmdline_redraw_tabline() + CheckRunVimInTerminal + + let lines =<< trim END + set showtabline=2 + autocmd CmdlineEnter * set tabline=foo + END + call writefile(lines, 'Xcmdline_redraw_tabline') + let buf = RunVimInTerminal('-S Xcmdline_redraw_tabline', #{rows: 6}) + call term_sendkeys(buf, ':') + call WaitForAssert({-> assert_match('^foo', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete('Xcmdline_redraw_tabline') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 8c20c647f1..1dbbe578c5 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -33,7 +33,8 @@ func Test_diff_fold_sync() call win_gotoid(winone) call assert_equal(23, getcurpos()[1]) - call assert_equal(1, g:update_count) + " depending on how redraw is done DiffUpdated may be triggered once or twice + call assert_inrange(1, 2, g:update_count) au! DiffUpdated windo diffoff @@ -1332,4 +1333,157 @@ func Test_diff_binary() set diffopt&vim endfunc +" Test for using the 'zi' command to invert 'foldenable' in diff windows (test +" for the issue fixed by patch 6.2.317) +func Test_diff_foldinvert() + %bw! + edit Xfile1 + new Xfile2 + new Xfile3 + windo diffthis + " open a non-diff window + botright new + 1wincmd w + call assert_true(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_false(getwinvar(2, '&foldenable')) + call assert_false(getwinvar(3, '&foldenable')) + normal zi + call assert_true(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + + " If the current window has 'noscrollbind', then 'zi' should not change + " 'foldenable' in other windows. + 1wincmd w + set noscrollbind + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + + " 'zi' should not change the 'foldenable' for windows with 'noscrollbind' + 1wincmd w + set scrollbind + normal zi + call setwinvar(2, '&scrollbind', v:false) + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_false(getwinvar(3, '&foldenable')) + + %bw! + set scrollbind& +endfunc + +" This was scrolling for 'cursorbind' but 'scrollbind' is more important +func Test_diff_scroll() + CheckScreendump + + let left =<< trim END + line 1 + line 2 + line 3 + line 4 + + // Common block + // one + // containing + // four lines + + // Common block + // two + // containing + // four lines + END + call writefile(left, 'Xleft') + let right =<< trim END + line 1 + line 2 + line 3 + line 4 + + Lorem + ipsum + dolor + sit + amet, + consectetur + adipiscing + elit. + Etiam + luctus + lectus + sodales, + dictum + + // Common block + // one + // containing + // four lines + + Vestibulum + tincidunt + aliquet + nulla. + + // Common block + // two + // containing + // four lines + END + call writefile(right, 'Xright') + let buf = RunVimInTerminal('-d Xleft Xright', {'rows': 12}) + call term_sendkeys(buf, "\<C-W>\<C-W>jjjj") + call VerifyScreenDump(buf, 'Test_diff_scroll_1', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_scroll_2', {}) + + call StopVimInTerminal(buf) + call delete('Xleft') + call delete('Xright') +endfunc + +" This was trying to update diffs for a buffer being closed +func Test_diff_only() + silent! lfile + set diff + lopen + norm o + silent! norm o + + set nodiff + %bwipe! +endfunc + +" This was causing invalid diff block values +" FIXME: somehow this causes a valgrind error when run directly but not when +" run as a test. +func Test_diff_manipulations() + set diff + split 0 + sil! norm R
doobdeuR
doobdeuR
doobdeu + + set nodiff + %bwipe! +endfunc + +" This was causing the line number in the diff block to go below one. +" FIXME: somehow this causes a valgrind error when run directly but not when +" run as a test. +func Test_diff_put_and_undo() + set diff + next 0 + split 00 + sil! norm o0gguudpo0ggJuudp + + bwipe! + bwipe! + set nodiff +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 42c77518e4..a09346a595 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1811,95 +1811,4 @@ func Test_read_invalid() set encoding=utf-8 endfunc -" Test for ModeChanged pattern -func Test_mode_changes() - let g:index = 0 - let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] - func! TestMode() - call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode")) - call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode")) - call assert_equal(mode(1), get(v:event, "new_mode")) - let g:index += 1 - endfunc - - au ModeChanged * :call TestMode() - let g:n_to_any = 0 - au ModeChanged n:* let g:n_to_any += 1 - call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix') - - let g:V_to_v = 0 - au ModeChanged V:v let g:V_to_v += 1 - call feedkeys("Vv\<C-G>\<esc>", 'tnix') - call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any) - call assert_equal(1, g:V_to_v) - call assert_equal(len(g:mode_seq) - 1, g:index) - - let g:n_to_i = 0 - au ModeChanged n:i let g:n_to_i += 1 - let g:n_to_niI = 0 - au ModeChanged i:niI let g:n_to_niI += 1 - let g:niI_to_i = 0 - au ModeChanged niI:i let g:niI_to_i += 1 - let g:nany_to_i = 0 - au ModeChanged n*:i let g:nany_to_i += 1 - let g:i_to_n = 0 - au ModeChanged i:n let g:i_to_n += 1 - let g:nori_to_any = 0 - au ModeChanged [ni]:* let g:nori_to_any += 1 - let g:i_to_any = 0 - au ModeChanged i:* let g:i_to_any += 1 - let g:index = 0 - let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] - call feedkeys("a\<C-O>l\<esc>", 'tnix') - call assert_equal(len(g:mode_seq) - 1, g:index) - call assert_equal(1, g:n_to_i) - call assert_equal(1, g:n_to_niI) - call assert_equal(1, g:niI_to_i) - call assert_equal(2, g:nany_to_i) - call assert_equal(1, g:i_to_n) - call assert_equal(2, g:i_to_any) - call assert_equal(3, g:nori_to_any) - - if has('terminal') - let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] - call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') - call assert_equal(len(g:mode_seq) - 1, g:index) - call assert_equal(1, g:n_to_i) - call assert_equal(1, g:n_to_niI) - call assert_equal(1, g:niI_to_i) - call assert_equal(2, g:nany_to_i) - call assert_equal(1, g:i_to_n) - call assert_equal(2, g:i_to_any) - call assert_equal(5, g:nori_to_any) - endif - - au! ModeChanged - delfunc TestMode - unlet! g:mode_seq - unlet! g:index - unlet! g:n_to_any - unlet! g:V_to_v - unlet! g:n_to_i - unlet! g:n_to_niI - unlet! g:niI_to_i - unlet! g:nany_to_i - unlet! g:i_to_n - unlet! g:nori_to_any - unlet! g:i_to_any -endfunc - -func Test_recursive_ModeChanged() - au! ModeChanged * norm 0u - sil! norm - au! -endfunc - -func Test_ModeChanged_starts_visual() - " This was triggering ModeChanged before setting VIsual, causing a crash. - au! ModeChanged * norm 0u - sil! norm - - au! ModeChanged -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index 122572f32a..2f734cba26 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -135,6 +135,30 @@ func Test_Ex_global() bwipe! endfunc +" Test for pressing Ctrl-C in :append inside a loop in Ex mode +" This used to hang Vim +func Test_Ex_append_in_loop() + CheckRunVimInTerminal + let buf = RunVimInTerminal('', {'rows': 6}) + + call term_sendkeys(buf, "gQ") + call term_sendkeys(buf, "for i in range(1)\<CR>") + call term_sendkeys(buf, "append\<CR>") + call WaitForAssert({-> assert_match(': append', term_getline(buf, 5))}, 1000) + call term_sendkeys(buf, "\<C-C>") + " Wait for input to be flushed + call term_wait(buf) + call term_sendkeys(buf, "foo\<CR>") + call WaitForAssert({-> assert_match('foo', term_getline(buf, 5))}, 1000) + call term_sendkeys(buf, ".\<CR>") + call WaitForAssert({-> assert_match('.', term_getline(buf, 5))}, 1000) + call term_sendkeys(buf, "endfor\<CR>") + call term_sendkeys(buf, "vi\<CR>") + call WaitForAssert({-> assert_match('foo', term_getline(buf, 1))}, 1000) + + call StopVimInTerminal(buf) +endfunc + " In Ex-mode, a backslash escapes a newline func Test_Ex_escape_enter() call feedkeys("gQlet l = \"a\\\<kEnter>b\"\<cr>vi\<cr>", 'xt') @@ -155,9 +179,7 @@ endfunc func Test_Ex_echo_backslash() throw 'Skipped: Nvim only supports Vim Ex mode' " This test works only when the language is English - if v:lang != "C" && v:lang !~ '^[Ee]n' - return - endif + CheckEnglish let bsl = '\\\\' let bsl2 = '\\\' call assert_fails('call feedkeys("Qecho " .. bsl .. "\nvisual\n", "xt")', diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 7d581d5efd..dac7a6989d 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -242,49 +242,58 @@ func Test_confirm_cmd() CheckNotGui CheckRunVimInTerminal - call writefile(['foo1'], 'foo') - call writefile(['bar1'], 'bar') + call writefile(['foo1'], 'Xfoo') + call writefile(['bar1'], 'Xbar') " Test for saving all the modified buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo2')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar2')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo2') + new Xbar + call setline(1, 'bar2') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "A") call StopVimInTerminal(buf) - call assert_equal(['foo2'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo2'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) " Test for discarding all the changes to modified buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo3')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar3')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo3') + new Xbar + call setline(1, 'bar3') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "D") call StopVimInTerminal(buf) - call assert_equal(['foo2'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo2'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) " Test for saving and discarding changes to some buffers - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new foo\n") - call term_sendkeys(buf, ":call setline(1, 'foo4')\n") - call term_sendkeys(buf, ":new bar\n") - call term_sendkeys(buf, ":call setline(1, 'bar4')\n") - call term_sendkeys(buf, ":wincmd b\n") + let lines =<< trim END + set nomore + new Xfoo + call setline(1, 'foo4') + new Xbar + call setline(1, 'bar4') + wincmd b + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm qall\n") call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) call term_sendkeys(buf, "N") @@ -292,11 +301,12 @@ func Test_confirm_cmd() call term_sendkeys(buf, "Y") call StopVimInTerminal(buf) - call assert_equal(['foo4'], readfile('foo')) - call assert_equal(['bar2'], readfile('bar')) + call assert_equal(['foo4'], readfile('Xfoo')) + call assert_equal(['bar2'], readfile('Xbar')) - call delete('foo') - call delete('bar') + call delete('Xscript') + call delete('Xfoo') + call delete('Xbar') endfunc func Test_confirm_cmd_cancel() @@ -304,10 +314,13 @@ func Test_confirm_cmd_cancel() CheckRunVimInTerminal " Test for closing a window with a modified buffer - let buf = RunVimInTerminal('', {'rows': 20}) - call term_sendkeys(buf, ":set nomore\n") - call term_sendkeys(buf, ":new\n") - call term_sendkeys(buf, ":call setline(1, 'abc')\n") + let lines =<< trim END + set nomore + new + call setline(1, 'abc') + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) call term_sendkeys(buf, ":confirm close\n") call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', \ term_getline(buf, 20))}, 1000) @@ -320,6 +333,43 @@ func Test_confirm_cmd_cancel() call WaitForAssert({-> assert_match('^ *0,0-1 All$', \ term_getline(buf, 20))}, 1000) call StopVimInTerminal(buf) + call delete('Xscript') +endfunc + +" The ":confirm" prompt was sometimes used with the terminal in cooked mode. +" This test verifies that a "\<CR>" character is NOT required to respond to a +" prompt from the ":conf q" and ":conf wq" commands. +func Test_confirm_q_wq() + CheckNotGui + CheckRunVimInTerminal + + call writefile(['foo'], 'Xfoo') + + let lines =<< trim END + set hidden nomore + call setline(1, 'abc') + edit Xfoo + END + call writefile(lines, 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) + call term_sendkeys(buf, ":confirm q\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'C') + call WaitForAssert({-> assert_notmatch('^\[Y\]es, (N)o, (C)ancel: C*$', + \ term_getline(buf, 20))}, 1000) + + call term_sendkeys(buf, ":edit Xfoo\n") + call term_sendkeys(buf, ":confirm wq\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'C') + call WaitForAssert({-> assert_notmatch('^\[Y\]es, (N)o, (C)ancel: C*$', + \ term_getline(buf, 20))}, 1000) + call StopVimInTerminal(buf) + + call delete('Xscript') + call delete('Xfoo') endfunc func Test_confirm_write_ro() @@ -646,5 +696,21 @@ func Test_using_zero_in_range() bwipe! endfunc +" Test :write after changing name with :file and loading it with :edit +func Test_write_after_rename() + call writefile(['text'], 'Xfile') + + enew + file Xfile + call assert_fails('write', 'E13: File exists (add ! to override)') + + " works OK after ":edit" + edit + write + + call delete('Xfile') + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim index d86fea4f45..383921bb82 100644 --- a/src/nvim/testdir/test_expand.vim +++ b/src/nvim/testdir/test_expand.vim @@ -84,6 +84,15 @@ func Test_expandcmd() let $FOO="blue\tsky" call setline(1, "$FOO") call assert_equal("grep pat blue\tsky", expandcmd('grep pat <cfile>')) + + " Test for expression expansion `= + let $FOO= "blue" + call assert_equal("blue sky", expandcmd("`=$FOO .. ' sky'`")) + + " Test for env variable with spaces + let $FOO= "foo bar baz" + call assert_equal("e foo bar baz", expandcmd("e $FOO")) + unlet $FOO close! endfunc diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 59b272d563..eedad15e9e 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -43,7 +43,9 @@ func Test_other_type() endfunc " Filetypes detected just from matching the file name. +" First one is checking that these files have no filetype. let s:filename_checks = { + \ 'none': ['bsd', 'some-bsd'], \ '8th': ['file.8th'], \ 'a2ps': ['/etc/a2ps.cfg', '/etc/a2ps/file.cfg', 'a2psrc', '.a2psrc', 'any/etc/a2ps.cfg', 'any/etc/a2ps/file.cfg'], \ 'a65': ['file.a65'], @@ -85,7 +87,7 @@ let s:filename_checks = { \ 'bindzone': ['named.root', '/bind/db.file', '/named/db.file', 'any/bind/db.file', 'any/named/db.file'], \ 'bitbake': ['file.bb', 'file.bbappend', 'file.bbclass', 'build/conf/local.conf', 'meta/conf/layer.conf', 'build/conf/bbappend.conf', 'meta-layer/conf/distro/foo.conf'], \ 'blank': ['file.bl'], - \ 'bsdl': ['file.bsd', 'file.bsdl', 'bsd', 'some-bsd'], + \ 'bsdl': ['file.bsd', 'file.bsdl'], \ 'bst': ['file.bst'], \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'], \ 'bzr': ['bzr_log.any', 'bzr_log.file'], @@ -532,6 +534,7 @@ let s:filename_checks = { \ 'svelte': ['file.svelte'], \ 'svg': ['file.svg'], \ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'], + \ 'swayconfig': ['/home/user/.sway/config', '/home/user/.config/sway/config', '/etc/sway/config', '/etc/xdg/sway/config'], \ 'swift': ['file.swift'], \ 'swiftgyb': ['file.swift.gyb'], \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf', 'any/etc/sysctl.conf', 'any/etc/sysctl.d/file.conf'], @@ -546,7 +549,7 @@ let s:filename_checks = { \ 'template': ['file.tmpl'], \ 'teraterm': ['file.ttl'], \ 'terminfo': ['file.ti'], - \ 'terraform': ['file.tfvars'], + \ 'terraform-vars': ['file.tfvars'], \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], @@ -647,7 +650,8 @@ func CheckItems(checks) if &filetype == '' && &readonly " File exists but not able to edit it (permission denied) else - call assert_equal(ft, &filetype, 'with file name: ' . names[i]) + let expected = ft == 'none' ? '' : ft + call assert_equal(expected, &filetype, 'with file name: ' . names[i]) endif bwipe! endfor @@ -1858,6 +1862,31 @@ func Test_inc_file() call assert_equal('bitbake', &filetype) bwipe! + call writefile(['S = "${WORKDIR}"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['DEPENDS:append = " somedep"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['MACHINE ??= "qemu"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['PROVIDES := "test"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['RDEPENDS_${PN} += "bar"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + " asm call writefile(['asmsyntax=bar'], 'Xfile.inc') split Xfile.inc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 9b8d740efb..c11e7b4fea 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -547,6 +547,7 @@ func Save_mode() return '' endfunc +" Test for the mode() function func Test_mode() new call append(0, ["Blue Ball Black", "Brown Band Bowl", ""]) @@ -717,6 +718,8 @@ func Test_mode() call assert_equal('c-c', g:current_modes) call feedkeys("gQecho \<C-R>=Save_mode()\<CR>\<CR>vi\<CR>", 'xt') call assert_equal('c-cv', g:current_modes) + " call feedkeys("Qcall Save_mode()\<CR>vi\<CR>", 'xt') + " call assert_equal('c-ce', g:current_modes) " How to test Ex mode? " Test mode in operatorfunc (it used to be Operator-pending). @@ -1262,6 +1265,23 @@ func Test_inputlist() call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>5q", 'tx') call assert_equal(0, c) + " Use backspace to delete characters in the prompt + call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<BS>3\<BS>2\<cr>", 'tx') + call assert_equal(2, c) + + " Use mouse to make a selection + " call test_setmouse(&lines - 3, 2) + call nvim_input_mouse('left', 'press', '', 0, &lines - 4, 1) " set mouse position + call getchar() " discard mouse event but keep mouse position + call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx') + call assert_equal(1, c) + " Mouse click outside of the list + " call test_setmouse(&lines - 6, 2) + call nvim_input_mouse('left', 'press', '', 0, &lines - 7, 1) " set mouse position + call getchar() " discard mouse event but keep mouse position + call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx') + call assert_equal(-2, c) + call assert_fails('call inputlist("")', 'E686:') endfunc diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index 1569177d66..feae44e5ee 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -138,6 +138,22 @@ func Test_gf_visual() call assert_equal('Xtest_gf_visual', bufname('%')) call assert_equal(3, getcurpos()[1]) + " do not include the NUL at the end + call writefile(['x'], 'X') + let save_enc = &enc + " for enc in ['latin1', 'utf-8'] + for enc in ['utf-8'] + exe "set enc=" .. enc + new + call setline(1, 'X') + set nomodified + exe "normal \<C-V>$gf" + call assert_equal('X', bufname()) + bwipe! + endfor + let &enc = save_enc + call delete('X') + " line number in visual area is used for file name if has('unix') bwipe! @@ -194,4 +210,27 @@ func Test_gf_includeexpr() delfunc IncFunc endfunc +" Check that expanding directories can handle more than 255 entries. +func Test_gf_subdirs_wildcard() + let cwd = getcwd() + let dir = 'Xtestgf_dir' + call mkdir(dir) + call chdir(dir) + for i in range(300) + call mkdir(i) + call writefile([], i .. '/' .. i, 'S') + endfor + set path=./** + + new | only + call setline(1, '99') + w! Xtest1 + normal gf + call assert_equal('99', fnamemodify(bufname(''), ":t")) + + call chdir(cwd) + call delete(dir, 'rf') + set path& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index feddf85346..947f7efc7c 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -1,4 +1,7 @@ +" Test for :global and :vglobal + source check.vim +source term_util.vim func Test_yank_put_clipboard() new @@ -82,4 +85,31 @@ func Test_wrong_delimiter() call assert_fails('g x^bxd', 'E146:') endfunc +" Test for interrupting :global using Ctrl-C +func Test_interrupt_global() + CheckRunVimInTerminal + let lines =<< trim END + cnoremap ; <Cmd>sleep 10<CR> + call setline(1, repeat(['foo'], 5)) + END + call writefile(lines, 'Xtest_interrupt_global') + let buf = RunVimInTerminal('-S Xtest_interrupt_global', {'rows': 6}) + + call term_sendkeys(buf, ":g/foo/norm :\<C-V>;\<CR>") + " Wait for :sleep to start + call term_wait(buf) + call term_sendkeys(buf, "\<C-C>") + call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 6))}, 1000) + + " Also test in Ex mode + call term_sendkeys(buf, "gQg/foo/norm :\<C-V>;\<CR>") + " Wait for :sleep to start + call term_wait(buf) + call term_sendkeys(buf, "\<C-C>") + call WaitForAssert({-> assert_match('Interrupted', term_getline(buf, 5))}, 1000) + + call StopVimInTerminal(buf) + call delete('Xtest_interrupt_global') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_feedkeys.vim b/src/nvim/testdir/test_input.vim index fb64711863..3b1e2eb2df 100644 --- a/src/nvim/testdir/test_feedkeys.vim +++ b/src/nvim/testdir/test_input.vim @@ -1,4 +1,4 @@ -" Test feedkeys() function. +" Tests for character input and feedkeys() function. func Test_feedkeys_x_with_empty_string() new @@ -34,4 +34,28 @@ func Test_feedkeys_escape_special() nunmap … endfunc +func Test_input_simplify_ctrl_at() + new + " feeding unsimplified CTRL-@ should still trigger i_CTRL-@ + call feedkeys("ifoo\<Esc>A\<*C-@>x", 'xt') + call assert_equal('foofo', getline(1)) + bw! +endfunc + +func Test_input_simplify_noremap() + call feedkeys("i\<*C-M>", 'nx') + call assert_equal('', getline(1)) + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + bw! +endfunc + +func Test_input_simplify_timedout() + inoremap <C-M>a b + call feedkeys("i\<*C-M>", 'xt') + call assert_equal('', getline(1)) + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + iunmap <C-M>a + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 93ab17955d..179218e48a 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -100,6 +100,74 @@ func Test_ins_complete() call delete('Xdir', 'rf') endfunc +func Test_omni_dash() + func Omni(findstart, base) + if a:findstart + return 5 + else + echom a:base + return ['-help', '-v'] + endif + endfunc + set omnifunc=Omni + new + exe "normal Gofind -\<C-x>\<C-o>" + call assert_equal("find -help", getline('$')) + + bwipe! + delfunc Omni + set omnifunc= +endfunc + +func Test_omni_throw() + let g:CallCount = 0 + func Omni(findstart, base) + let g:CallCount += 1 + if a:findstart + throw "he he he" + endif + endfunc + set omnifunc=Omni + new + try + exe "normal ifoo\<C-x>\<C-o>" + call assert_false(v:true, 'command should have failed') + catch + call assert_exception('he he he') + call assert_equal(1, g:CallCount) + endtry + + bwipe! + delfunc Omni + unlet g:CallCount + set omnifunc= +endfunc + +func Test_completefunc_args() + let s:args = [] + func! CompleteFunc(findstart, base) + let s:args += [[a:findstart, empty(a:base)]] + endfunc + new + + set completefunc=CompleteFunc + call feedkeys("i\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([1, 1], s:args[0]) + call assert_equal(0, s:args[1][0]) + set completefunc= + + let s:args = [] + set omnifunc=CompleteFunc + call feedkeys("i\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([1, 1], s:args[0]) + call assert_equal(0, s:args[1][0]) + set omnifunc= + + bwipe! + unlet s:args + delfunc CompleteFunc +endfunc + func s:CompleteDone_CompleteFuncNone( findstart, base ) throw 'skipped: Nvim does not support v:none' if a:findstart @@ -179,19 +247,6 @@ func Test_CompleteDoneDict() au! CompleteDone endfunc -func Test_CompleteDone_undo() - au CompleteDone * call append(0, "prepend1") - new - call setline(1, ["line1", "line2"]) - call feedkeys("Go\<C-X>\<C-N>\<CR>\<ESC>", "tx") - call assert_equal(["prepend1", "line1", "line2", "line1", ""], - \ getline(1, '$')) - undo - call assert_equal(["line1", "line2"], getline(1, '$')) - bwipe! - au! CompleteDone -endfunc - func s:CompleteDone_CompleteFuncDictNoUserData(findstart, base) if a:findstart return 0 @@ -268,72 +323,30 @@ func Test_CompleteDoneList() au! CompleteDone endfunc -func Test_omni_dash() - func Omni(findstart, base) - if a:findstart - return 5 - else - echom a:base - return ['-help', '-v'] - endif - endfunc - set omnifunc=Omni - new - exe "normal Gofind -\<C-x>\<C-o>" - call assert_equal("find -help", getline('$')) - - bwipe! - delfunc Omni - set omnifunc= -endfunc - -func Test_omni_throw() - let g:CallCount = 0 - func Omni(findstart, base) - let g:CallCount += 1 - if a:findstart - throw "he he he" - endif - endfunc - set omnifunc=Omni +func Test_CompleteDone_undo() + au CompleteDone * call append(0, "prepend1") new - try - exe "normal ifoo\<C-x>\<C-o>" - call assert_false(v:true, 'command should have failed') - catch - call assert_exception('he he he') - call assert_equal(1, g:CallCount) - endtry - + call setline(1, ["line1", "line2"]) + call feedkeys("Go\<C-X>\<C-N>\<CR>\<ESC>", "tx") + call assert_equal(["prepend1", "line1", "line2", "line1", ""], + \ getline(1, '$')) + undo + call assert_equal(["line1", "line2"], getline(1, '$')) bwipe! - delfunc Omni - unlet g:CallCount - set omnifunc= + au! CompleteDone endfunc -func Test_completefunc_args() - let s:args = [] - func! CompleteFunc(findstart, base) - let s:args += [[a:findstart, empty(a:base)]] - endfunc - new - - set completefunc=CompleteFunc - call feedkeys("i\<C-X>\<C-U>\<Esc>", 'x') - call assert_equal([1, 1], s:args[0]) - call assert_equal(0, s:args[1][0]) - set completefunc= - - let s:args = [] - set omnifunc=CompleteFunc - call feedkeys("i\<C-X>\<C-O>\<Esc>", 'x') - call assert_equal([1, 1], s:args[0]) - call assert_equal(0, s:args[1][0]) - set omnifunc= - - bwipe! - unlet s:args - delfunc CompleteFunc +func Test_CompleteDone_modify() + let value = { + \ 'word': '', + \ 'abbr': '', + \ 'menu': '', + \ 'info': '', + \ 'kind': '', + \ 'user_data': '', + \ } + let v:completed_item = value + call assert_equal(value, v:completed_item) endfunc func CompleteTest(findstart, query) @@ -505,19 +518,6 @@ func Test_ins_completeslash() set completeslash= endfunc -func Test_issue_7021() - CheckMSWindows - - let orig_shellslash = &shellslash - set noshellslash - - set completeslash=slash - call assert_false(expand('~') =~ '/') - - let &shellslash = orig_shellslash - set completeslash= -endfunc - func Test_pum_stopped_by_timer() CheckScreendump @@ -829,6 +829,19 @@ func Test_complete_stop() close! endfunc +func Test_issue_7021() + CheckMSWindows + + let orig_shellslash = &shellslash + set noshellslash + + set completeslash=slash + call assert_false(expand('~') =~ '/') + + let &shellslash = orig_shellslash + set completeslash= +endfunc + " Test to ensure 'Scanning...' messages are not recorded in messages history func Test_z1_complete_no_history() new @@ -838,7 +851,18 @@ func Test_z1_complete_no_history() exe "normal owh\<C-X>\<C-K>" exe "normal owh\<C-N>" call assert_equal(currmess, execute('messages')) - close! + bwipe! +endfunc + +" A mapping is not used for the key after CTRL-X. +func Test_no_mapping_for_ctrl_x_key() + new + inoremap <C-K> <Cmd>let was_mapped = 'yes'<CR> + setlocal dictionary=README.txt + call feedkeys("aexam\<C-X>\<C-K> ", 'xt') + call assert_equal('example ', getline(1)) + call assert_false(exists('was_mapped')) + bwipe! endfunc func FooBarComplete(findstart, base) diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim index f9429a8020..dad4c81a7b 100644 --- a/src/nvim/testdir/test_maparg.vim +++ b/src/nvim/testdir/test_maparg.vim @@ -1,12 +1,12 @@ -" Tests for maparg(). +" Tests for maparg(), mapcheck() and mapset(). " Also test utf8 map with a 0x80 byte. " Also test mapcheck() -function s:SID() +func s:SID() return str2nr(matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')) -endfun +endfunc -function Test_maparg() +func Test_maparg() new set cpo-=< set encoding=utf8 @@ -17,24 +17,28 @@ function Test_maparg() vnoremap <script> <buffer> <expr> <silent> bar isbar call assert_equal("is<F4>foo", maparg('foo<C-V>')) call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo<C-V>', - \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, - \ 'rhs': 'is<F4>foo', 'buffer': 0}, - \ maparg('foo<C-V>', '', 0, 1)) - call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', 'mode': 'v', + \ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16", + \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, + \ 'rhs': 'is<F4>foo', 'buffer': 0}, + \ maparg('foo<C-V>', '', 0, 1)) + call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', + \ 'lhsraw': 'bar', 'mode': 'v', \ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2, - \ 'rhs': 'isbar', 'buffer': 1}, + \ 'rhs': 'isbar', 'buffer': 1}, \ 'bar'->maparg('', 0, 1)) let lnum = expand('<sflnum>') map <buffer> <nowait> foo bar - call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', 'mode': ' ', + call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', + \ 'lhsraw': 'foo', 'mode': ' ', \ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar', - \ 'buffer': 1}, + \ 'buffer': 1}, \ maparg('foo', '', 0, 1)) let lnum = expand('<sflnum>') tmap baz foo - call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', 'mode': 't', + call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', + \ 'lhsraw': 'baz', 'mode': 't', \ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo', - \ 'buffer': 0}, + \ 'buffer': 0}, \ maparg('baz', 't', 0, 1)) map abc x<char-114>x @@ -81,6 +85,12 @@ function Test_maparg() call assert_equal(['{', 'w', 's'], [d.lhs, d.rhs, d.mode]) sunmap { + map <C-I> foo + unmap <Tab> + " This used to cause a segfault + call maparg('<C-I>', '', 0, 1) + unmap <C-I> + map abc <Nop> call assert_equal("<Nop>", maparg('abc')) unmap abc @@ -89,7 +99,8 @@ function Test_maparg() let d = maparg('esc', 'i', 1, 1) call assert_equal(['esc', "\<C-V>\<C-V>\<Esc>", '!'], [d.lhs, d.rhs, d.mode]) abclear -endfunction + unlet d +endfunc func Test_mapcheck() call assert_equal('', mapcheck('a')) @@ -130,7 +141,7 @@ func Test_mapcheck() unabbr ab endfunc -function Test_range_map() +func Test_range_map() new " Outside of the range, minimum inoremap <Char-0x1040> a @@ -145,6 +156,147 @@ function Test_range_map() inoremap <Char-0xf040> d execute "normal a\uf040\<Esc>" call assert_equal("abcd", getline(1)) -endfunction +endfunc + +func One_mapset_test(keys) + exe 'nnoremap ' .. a:keys .. ' original<CR>' + let orig = maparg(a:keys, 'n', 0, 1) + call assert_equal(a:keys, orig.lhs) + call assert_equal('original<CR>', orig.rhs) + call assert_equal('n', orig.mode) + + exe 'nunmap ' .. a:keys + let d = maparg(a:keys, 'n', 0, 1) + call assert_equal({}, d) + + call mapset('n', 0, orig) + let d = maparg(a:keys, 'n', 0, 1) + call assert_equal(a:keys, d.lhs) + call assert_equal('original<CR>', d.rhs) + call assert_equal('n', d.mode) + + exe 'nunmap ' .. a:keys +endfunc + +func Test_mapset() + call One_mapset_test('K') + call One_mapset_test('<F3>') + + " Check <> key conversion + new + inoremap K one<Left>x + call feedkeys("iK\<Esc>", 'xt') + call assert_equal('onxe', getline(1)) + + let orig = maparg('K', 'i', 0, 1) + call assert_equal('K', orig.lhs) + call assert_equal('one<Left>x', orig.rhs) + call assert_equal('i', orig.mode) + + iunmap K + let d = maparg('K', 'i', 0, 1) + call assert_equal({}, d) + + call mapset('i', 0, orig) + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('onxe', getline(1)) + + iunmap K + + " Test literal <CR> using a backslash + let cpo_save = &cpo + set cpo-=B + inoremap K one\<CR>two + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + let orig = maparg('K', 'i', 0, 1) + call assert_equal('K', orig.lhs) + call assert_equal('one\<CR>two', orig.rhs) + call assert_equal('i', orig.mode) + + iunmap K + let d = maparg('K', 'i', 0, 1) + call assert_equal({}, d) + + call mapset('i', 0, orig) + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + iunmap K + + " Test literal <CR> using CTRL-V + inoremap K one<CR>two + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + let orig = maparg('K', 'i', 0, 1) + call assert_equal('K', orig.lhs) + call assert_equal("one\x16<CR>two", orig.rhs) + call assert_equal('i', orig.mode) + + iunmap K + let d = maparg('K', 'i', 0, 1) + call assert_equal({}, d) + + call mapset('i', 0, orig) + call feedkeys("SK\<Esc>", 'xt') + call assert_equal('one<CR>two', getline(1)) + + iunmap K + let &cpo = cpo_save + bwipe! + + call assert_fails('call mapset([], v:false, {})', 'E730:') +endfunc + +func Check_ctrlb_map(d, check_alt) + call assert_equal('<C-B>', a:d.lhs) + if a:check_alt + call assert_equal("\x80\xfc\x04B", a:d.lhsraw) + call assert_equal("\x02", a:d.lhsrawalt) + else + call assert_equal("\x02", a:d.lhsraw) + endif +endfunc + +func Test_map_local() + nmap a global + nmap <buffer>a local + + let prev_map_list = split(execute('nmap a'), "\n") + call assert_match('n\s*a\s*@local', prev_map_list[0]) + call assert_match('n\s*a\s*global', prev_map_list[1]) + + let mapping = maparg('a', 'n', 0, 1) + call assert_equal(1, mapping.buffer) + let mapping.rhs = 'new_local' + call mapset('n', 0, mapping) + + " Check that the global mapping is left untouched. + let map_list = split(execute('nmap a'), "\n") + call assert_match('n\s*a\s*@new_local', map_list[0]) + call assert_match('n\s*a\s*global', map_list[1]) + + nunmap a +endfunc + +func Test_map_restore() + " Test restoring map with alternate keycode + nmap <C-B> back + let d = maparg('<C-B>', 'n', 0, 1) + call Check_ctrlb_map(d, 1) + let dsimp = maparg("\x02", 'n', 0, 1) + call Check_ctrlb_map(dsimp, 0) + nunmap <C-B> + call mapset('n', 0, d) + let d = maparg('<C-B>', 'n', 0, 1) + call Check_ctrlb_map(d, 1) + let dsimp = maparg("\x02", 'n', 0, 1) + call Check_ctrlb_map(dsimp, 0) + + nunmap <C-B> + +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 5670368936..a02d23b409 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -118,7 +118,9 @@ func Test_message_more() let buf = RunVimInTerminal('', {'rows': 6}) call term_sendkeys(buf, ":call setline(1, range(1, 100))\n") - call term_sendkeys(buf, ":%p#\n") + call term_sendkeys(buf, ":%pfoo\<C-H>\<C-H>\<C-H>#") + call WaitForAssert({-> assert_equal(':%p#', term_getline(buf, 6))}) + call term_sendkeys(buf, "\n") call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))}) call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))}) @@ -175,7 +177,8 @@ func Test_message_more() " Up all the way with 'g'. call term_sendkeys(buf, 'g') - call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))}) + call WaitForAssert({-> assert_equal(' 4 4', term_getline(buf, 5))}) + call WaitForAssert({-> assert_equal(':%p#', term_getline(buf, 1))}) call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))}) " All the way down. Pressing f should do nothing but pressing @@ -193,6 +196,13 @@ func Test_message_more() call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))}) call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))}) + " A command line that doesn't print text is appended to scrollback, + " even if it invokes a nested command line. + call term_sendkeys(buf, ":\<C-R>=':'\<CR>:\<CR>g<") + call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 4))}) + call WaitForAssert({-> assert_equal(':::', term_getline(buf, 5))}) + call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))}) + call term_sendkeys(buf, ":%p#\n") call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))}) call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))}) @@ -306,4 +316,60 @@ func Test_fileinfo_after_echo() call delete('b.txt') endfunc +func Test_cmdheight_zero() + set cmdheight=0 + set showcmd + redraw! + + echo 'test echo' + call assert_equal(116, screenchar(&lines, 1)) + redraw! + + echomsg 'test echomsg' + call assert_equal(116, screenchar(&lines, 1)) + redraw! + + call feedkeys(":ls\<CR>", "xt") + call assert_equal(':ls', Screenline(&lines - 1)) + redraw! + + let char = getchar(0) + call assert_match(char, 0) + + " Check change/restore cmdheight when macro + call feedkeys("qa", "xt") + call assert_equal(1, &cmdheight) + call feedkeys("q", "xt") + call assert_equal(0, &cmdheight) + + call setline(1, 'somestring') + call feedkeys("y", "n") + %s/somestring/otherstring/gc + call assert_equal('otherstring', getline(1)) + + call feedkeys("g\<C-g>", "xt") + call assert_match( + \ 'Col 1 of 11; Line 1 of 1; Word 1 of 1', + \ Screenline(&lines)) + + " Check split behavior + for i in range(1, 10) + split + endfor + only + call assert_equal(0, &cmdheight) + + " Check that pressing ":" should not scroll a window + " Check for what patch 9.0.0115 fixes + botright 10new + call setline(1, range(12)) + 7 + call feedkeys(":\"\<C-R>=line('w0')\<CR>\<CR>", "xt") + call assert_equal('"1', @:) + bwipe! + + set cmdheight& + set showcmd& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 9fbd1f774a..f18ddb274c 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -580,6 +580,7 @@ endfunc func Test_normal_z_error() call assert_beeps('normal! z2p') call assert_beeps('normal! zq') + call assert_beeps('normal! cz1') endfunc func Test_normal15_z_scroll_vert() @@ -619,7 +620,7 @@ func Test_normal15_z_scroll_vert() call assert_equal(10, winheight(0)) exe "norm! z12\<cr>" call assert_equal(12, winheight(0)) - exe "norm! z10\<cr>" + exe "norm! z15\<Del>0\<cr>" call assert_equal(10, winheight(0)) " Test for z. @@ -2849,35 +2850,10 @@ func Test_gr_command() enew! endfunc -" When splitting a window the changelist position is wrong. -" Test the changelist position after splitting a window. -" Test for the bug fixed by 7.4.386 -func Test_changelist() - let save_ul = &ul - enew! - call append('$', ['1', '2']) - exe "normal i\<C-G>u" - exe "normal Gkylpa\<C-G>u" - set ul=100 - exe "normal Gylpa\<C-G>u" - set ul=100 - normal gg - vsplit - normal g; - call assert_equal([3, 2], [line('.'), col('.')]) - normal g; - call assert_equal([2, 2], [line('.'), col('.')]) - call assert_fails('normal g;', 'E662:') - new - call assert_fails('normal g;', 'E664:') - %bwipe! - let &ul = save_ul -endfunc - func Test_nv_hat_count() %bwipeout! let l:nr = bufnr('%') + 1 - call assert_fails(':execute "normal! ' . l:nr . '\<C-^>"', 'E92') + call assert_fails(':execute "normal! ' . l:nr . '\<C-^>"', 'E92:') edit Xfoo let l:foo_nr = bufnr('Xfoo') diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 1f003041e6..c5b1266689 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -1,6 +1,7 @@ " Test for options source check.vim +source view_util.vim func Test_whichwrap() set whichwrap=b,s @@ -48,7 +49,9 @@ func Test_pastetoggle() let &pastetoggle = str call assert_equal(str, &pastetoggle) call assert_equal("\n pastetoggle=" .. strtrans(str), execute('set pastetoggle?')) + unlet str + set pastetoggle& endfunc func Test_wildchar() @@ -782,7 +785,6 @@ endfunc func Test_rightleftcmd() CheckFeature rightleft set rightleft - set rightleftcmd let g:l = [] func AddPos() @@ -791,6 +793,13 @@ func Test_rightleftcmd() endfunc cmap <expr> <F2> AddPos() + set rightleftcmd= + call feedkeys("/\<F2>abc\<Right>\<F2>\<Left>\<Left>\<F2>" .. + \ "\<Right>\<F2>\<Esc>", 'xt') + call assert_equal([2, 5, 3, 4], g:l) + + let g:l = [] + set rightleftcmd=search call feedkeys("/\<F2>abc\<Left>\<F2>\<Right>\<Right>\<F2>" .. \ "\<Left>\<F2>\<Esc>", 'xt') call assert_equal([&co - 1, &co - 4, &co - 2, &co - 3], g:l) @@ -801,6 +810,14 @@ func Test_rightleftcmd() set rightleft& endfunc +" Test for the "debug" option +func Test_debug_option() + set debug=beep + exe "normal \<C-c>" + call assert_equal('Beep!', Screenline(&lines)) + set debug& +endfunc + " Test for setting option values using v:false and v:true func Test_opt_boolean() set number& diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index a5e4be49f4..0486ed83ad 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -955,6 +955,25 @@ func Test_menu_only_exists_in_terminal() endtry endfunc +" This used to crash before patch 8.1.1424 +func Test_popup_delete_when_shown() + CheckFeature menu + CheckNotGui + + func Func() + popup Foo + return "\<Ignore>" + endfunc + + nmenu Foo.Bar : + nnoremap <expr> <F2> Func() + call feedkeys("\<F2>\<F2>\<Esc>", 'xt') + + delfunc Func + nunmenu Foo.Bar + nunmap <F2> +endfunc + func Test_popup_complete_info_01() new inoremap <buffer><F5> <C-R>=complete_info().mode<CR> diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index ddd4229f17..f6d573d76b 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -172,6 +172,14 @@ func XlistTests(cchar) \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning', \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l) + " Very long line should be displayed. + let text = 'Line' .. repeat('1234567890', 130) + let lines = ['Xtestfile9:2:9:' .. text] + Xgetexpr lines + + let l = split(execute('Xlist', ''), "\n") + call assert_equal([' 1 Xtestfile9:2 col 9: ' .. text] , l) + " For help entries in the quickfix list, only the filename without directory " should be displayed Xhelpgrep setqflist() diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 82d250e8b3..d08a980787 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -1027,4 +1027,15 @@ func Test_using_invalid_visual_position() bwipe! endfunc +func Test_recursive_substitute_expr() + new + func Repl() + s + endfunc + silent! s/\%')/~\=Repl() + + bwipe! + delfunc Repl +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim index b7e3da37cb..7c588d736a 100644 --- a/src/nvim/testdir/test_spell_utf8.vim +++ b/src/nvim/testdir/test_spell_utf8.vim @@ -820,5 +820,13 @@ func Test_check_empty_line() bwipe! endfunc +func Test_spell_suggest_too_long() + " this was creating a word longer than MAXWLEN + new + call setline(1, 'a' .. repeat("\u0333", 150)) + norm! z= + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index d830f5216d..39fafbf7b4 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -12,6 +12,9 @@ func Test_startup_script() source $VIMRUNTIME/defaults.vim call assert_equal(0, &compatible) + " Restore some options, so that the following tests doesn't break + set nomore + set noshowmode endfunc " Verify the order in which plugins are loaded: @@ -814,6 +817,25 @@ func Test_v_argv() call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:]) endfunc +" Test for the "-r" recovery mode option +func Test_r_arg() + throw 'Skipped: Nvim has different directories' + " Can't catch the output of gvim. + CheckNotGui + CheckUnix + CheckEnglish + let cmd = GetVimCommand() + " There can be swap files anywhere, only check for the headers. + let expected =<< trim END + Swap files found:.* + In current directory:.* + In directory \~/tmp:.* + In directory /var/tmp:.* + In directory /tmp:.* + END + call assert_match(join(expected, ""), system(cmd .. " -r")->substitute("[\r\n]\\+", '', '')) +endfunc + " Test for the '-t' option to jump to a tag func Test_t_arg() let before =<< trim [CODE] @@ -824,6 +846,7 @@ func Test_t_arg() call writefile([s], "Xtestout") qall [CODE] + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", \ "first\tXfile1\t/^ \\zsfirst$/", \ "second\tXfile1\t/^ \\zssecond$/", @@ -890,6 +913,138 @@ func Test_x_arg() call delete('Xtest_x_arg') endfunc +" Test for entering the insert mode on startup +func Test_start_insertmode() + throw "Skipped: Nvim does not support setting 'insertmode'" + let before =<< trim [CODE] + set insertmode + [CODE] + let after =<< trim [CODE] + call writefile(['insertmode=' .. &insertmode], 'Xtestout') + qall + [CODE] + if RunVim(before, after, '') + call assert_equal(['insertmode=1'], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for enabling the binary mode on startup +func Test_b_arg() + let after =<< trim [CODE] + call writefile(['binary=' .. &binary], 'Xtestout') + qall + [CODE] + if RunVim([], after, '-b') + call assert_equal(['binary=1'], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for enabling the lisp mode on startup +func Test_l_arg() + let after =<< trim [CODE] + let s = 'lisp=' .. &lisp .. ', showmatch=' .. &showmatch + call writefile([s], 'Xtestout') + qall + [CODE] + if RunVim([], after, '-l') + call assert_equal(['lisp=1, showmatch=1'], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for specifying a non-existing vimrc file using "-u" +func Test_missing_vimrc() + if !CanRunVimInTerminal() + throw 'Skipped: cannot run vim in terminal' + endif + let after =<< trim [CODE] + call assert_match('^E282:', v:errmsg) + call writefile(v:errors, 'Xtestout') + [CODE] + call writefile(after, 'Xafter') + + let cmd = GetVimCommandCleanTerm() . ' -u Xvimrc_missing -S Xafter' + let buf = term_start(cmd, {'term_rows' : 10}) + call WaitForAssert({-> assert_equal("running", term_getstatus(buf))}) + call term_wait(buf) + call term_sendkeys(buf, "\n:") + call term_wait(buf) + call WaitForAssert({-> assert_match(':', term_getline(buf, 10))}) + call StopVimInTerminal(buf) + call assert_equal([], readfile('Xtestout')) + call delete('Xafter') + call delete('Xtestout') +endfunc + +" Test for using the $VIMINIT environment variable +func Test_VIMINIT() + let after =<< trim [CODE] + call assert_equal(1, exists('viminit_found')) + call assert_equal('yes', viminit_found) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + call writefile(after, 'Xafter') + " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"' + let cmd = GetVimProg() . ' -S Xafter --cmd "set enc=utf8"' + call setenv('VIMINIT', 'let viminit_found="yes"') + exe "silent !" . cmd + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + call delete('Xafter') +endfunc + +" Test for using the $EXINIT environment variable +func Test_EXINIT() + let after =<< trim [CODE] + call assert_equal(1, exists('exinit_found')) + call assert_equal('yes', exinit_found) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + call writefile(after, 'Xafter') + " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"' + let cmd = GetVimProg() . ' -S Xafter --cmd "set enc=utf8"' + call setenv('EXINIT', 'let exinit_found="yes"') + exe "silent !" . cmd + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + call delete('Xafter') +endfunc + +" Test for using the 'exrc' option +func Test_exrc() + let after =<< trim [CODE] + call assert_equal(1, &exrc) + call assert_equal(1, &secure) + call assert_equal(37, exrc_found) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + call mkdir('Xdir') + call writefile(['let exrc_found=37'], 'Xdir/.exrc') + call writefile(after, 'Xdir/Xafter') + " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "cd Xdir" --cmd "set enc=utf8 exrc secure"' + let cmd = GetVimProg() . ' -S Xafter --cmd "cd Xdir" --cmd "set enc=utf8 exrc secure"' + exe "silent !" . cmd + call assert_equal([], readfile('Xdir/Xtestout')) + call delete('Xdir', 'rf') +endfunc + +" Test for starting Vim with a non-terminal as input/output +func Test_io_not_a_terminal() + throw 'Skipped: Nvim does not support --ttyfail' + " Can't catch the output of gvim. + CheckNotGui + CheckUnix + CheckEnglish + let l = systemlist(GetVimProg() .. ' --ttyfail') + call assert_equal(['Vim: Warning: Output is not to a terminal', + \ 'Vim: Warning: Input is not from a terminal'], l) +endfunc + " Test for --not-a-term avoiding escape codes. func Test_not_a_term() CheckUnix @@ -948,6 +1103,93 @@ func Test_w_arg() call delete('Xscriptin') endfunc +" Test for the "-s scriptin" argument +func Test_s_arg() + " Can't catch the output of gvim. + CheckNotGui + CheckEnglish + " Test for failing to open the script input file. + let m = system(GetVimCommand() .. " -s abcxyz") + " call assert_equal("Cannot open for reading: \"abcxyz\"\n", m) + call assert_equal("Cannot open for reading: \"abcxyz\": no such file or directory\n", m) + + call writefile([], 'Xinput') + let m = system(GetVimCommand() .. " -s Xinput -s Xinput") + call assert_equal("Attempt to open script file again: \"-s Xinput\"\n", m) + call delete('Xinput') +endfunc + +" Test for the "-n" (no swap file) argument +func Test_n_arg() + let after =<< trim [CODE] + call assert_equal(0, &updatecount) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + if RunVim([], after, '-n') + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for the "-h" (help) argument +func Test_h_arg() + throw 'Skipped: Nvim has different output for "-h" argument' + " Can't catch the output of gvim. + CheckNotGui + let l = systemlist(GetVimProg() .. ' -h') + call assert_match('^VIM - Vi IMproved', l[0]) + let l = systemlist(GetVimProg() .. ' -?') + call assert_match('^VIM - Vi IMproved', l[0]) +endfunc + +" Test for the "-F" (farsi) argument +func Test_F_arg() + throw 'Skipped: Nvim does not recognize "-F" argument' + " Can't catch the output of gvim. + CheckNotGui + let l = systemlist(GetVimProg() .. ' -F') + call assert_match('^E27:', l[0]) +endfunc + +" Test for the "-E" (improved Ex mode) argument +func Test_E_arg() + let after =<< trim [CODE] + call assert_equal('cv', mode(1)) + call writefile(v:errors, 'Xtestout') + qall + [CODE] + if RunVim([], after, '-E') + call assert_equal([], readfile('Xtestout')) + call delete('Xtestout') + endif +endfunc + +" Test for the "-D" (debugger) argument +func Test_D_arg() + CheckRunVimInTerminal + + let cmd = GetVimCommandCleanTerm() .. ' -D' + let buf = term_start(cmd, {'term_rows' : 10}) + call WaitForAssert({-> assert_equal("running", term_getstatus(buf))}) + + call WaitForAssert({-> assert_equal('Entering Debug mode. Type "cont" to continue.', + \ term_getline(buf, 7))}) + call WaitForAssert({-> assert_equal('>', term_getline(buf, 10))}) + + call StopVimInTerminal(buf) +endfunc + +" Test for too many edit argument errors +func Test_too_many_edit_args() + throw 'Skipped: N/A' + " Can't catch the output of gvim. + CheckNotGui + CheckEnglish + let l = systemlist(GetVimProg() .. ' - -') + call assert_match('^Too many edit arguments: "-"', l[1]) +endfunc + " Test starting vim with various names: vim, ex, view, evim, etc. func Test_progname() CheckUnix diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index ec35fac964..6bde052442 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -11,6 +11,10 @@ func SetUp() set laststatus=2 endfunc +func TearDown() + set laststatus& +endfunc + func s:get_statusline() return ScreenLines(&lines - 1, &columns)[0] endfunc @@ -39,7 +43,6 @@ endfunc func Test_caught_error_in_statusline() let s:func_in_statusline_called = 0 - set laststatus=2 let statusline = '%{StatuslineWithCaughtError()}' let &statusline = statusline redrawstatus @@ -50,7 +53,6 @@ endfunc func Test_statusline_will_be_disabled_with_error() let s:func_in_statusline_called = 0 - set laststatus=2 let statusline = '%{StatuslineWithError()}' try let &statusline = statusline @@ -77,7 +79,6 @@ func Test_statusline() call assert_match('^ ((2) of 2)\s*$', s:get_statusline()) only - set laststatus=2 set splitbelow call setline(1, range(1, 10000)) @@ -436,7 +437,6 @@ func Test_statusline() %bw! call delete('Xstatusline') set statusline& - set laststatus& set splitbelow& endfunc @@ -524,7 +524,6 @@ endfunc " with a custom 'statusline' func Test_statusline_mbyte_fillchar() only - set laststatus=2 set fillchars=vert:\|,fold:-,stl:━,stlnc:═ set statusline=a%=b call assert_match('^a\+━\+b$', s:get_statusline()) @@ -532,7 +531,7 @@ func Test_statusline_mbyte_fillchar() call assert_match('^a\+━\+b━a\+═\+b$', s:get_statusline()) wincmd w call assert_match('^a\+═\+b═a\+━\+b$', s:get_statusline()) - set statusline& fillchars& laststatus& + set statusline& fillchars& %bw! endfunc diff --git a/src/nvim/testdir/test_syn_attr.vim b/src/nvim/testdir/test_syn_attr.vim index fa0b08fde5..88f9d0d84d 100644 --- a/src/nvim/testdir/test_syn_attr.vim +++ b/src/nvim/testdir/test_syn_attr.vim @@ -1,19 +1,39 @@ " Test syntax highlighting functions. func Test_missing_attr() - hi Mine cterm=italic + throw 'Skipped: use test/functional/legacy/syn_attr_spec.lua' + + hi Mine term=bold cterm=italic call assert_equal('Mine', synIDattr(hlID("Mine"), "name")) + call assert_equal('', synIDattr("Mine"->hlID(), "bg", 'term')) + call assert_equal('', synIDattr("Mine"->hlID(), "fg", 'term')) + call assert_equal('', synIDattr("Mine"->hlID(), "sp", 'term')) + call assert_equal('1', synIDattr(hlID("Mine"), "bold", 'term')) call assert_equal('1', synIDattr(hlID("Mine"), "italic", 'cterm')) - hi Mine cterm=inverse + hi Mine term=reverse cterm=inverse + call assert_equal('1', synIDattr(hlID("Mine"), "reverse", 'term')) call assert_equal('1', synIDattr(hlID("Mine"), "inverse", 'cterm')) - hi Mine cterm=standout gui=undercurl + + hi Mine term=underline cterm=standout gui=undercurl + call assert_equal('1', synIDattr(hlID("Mine"), "underline", 'term')) call assert_equal('1', synIDattr(hlID("Mine"), "standout", 'cterm')) call assert_equal('1', synIDattr("Mine"->hlID(), "undercurl", 'gui')) - hi Mine gui=strikethrough + + hi Mine term=underdouble cterm=underdotted gui=underdashed + call assert_equal('1', synIDattr(hlID("Mine"), "underdouble", 'term')) + call assert_equal('1', synIDattr(hlID("Mine"), "underdotted", 'cterm')) + call assert_equal('1', synIDattr("Mine"->hlID(), "underdashed", 'gui')) + + hi Mine term=nocombine gui=strikethrough call assert_equal('1', synIDattr(hlID("Mine"), "strikethrough", 'gui')) - hi Mine cterm=NONE gui=NONE + call assert_equal('1', synIDattr(hlID("Mine"), "nocombine", 'term')) + call assert_equal('', synIDattr(hlID("Mine"), "nocombine", 'gui')) + hi Mine term=NONE cterm=NONE gui=NONE + call assert_equal('', synIDattr(hlID("Mine"), "bold", 'term')) call assert_equal('', synIDattr(hlID("Mine"), "italic", 'cterm')) + call assert_equal('', synIDattr(hlID("Mine"), "reverse", 'term')) call assert_equal('', synIDattr(hlID("Mine"), "inverse", 'cterm')) + call assert_equal('', synIDattr(hlID("Mine"), "underline", 'term')) call assert_equal('', synIDattr(hlID("Mine"), "standout", 'cterm')) call assert_equal('', synIDattr(hlID("Mine"), "undercurl", 'gui')) call assert_equal('', synIDattr(hlID("Mine"), "strikethrough", 'gui')) diff --git a/src/nvim/testdir/test_termcodes.vim b/src/nvim/testdir/test_termcodes.vim index c0712aa892..eda485c512 100644 --- a/src/nvim/testdir/test_termcodes.vim +++ b/src/nvim/testdir/test_termcodes.vim @@ -33,28 +33,5 @@ func Test_special_term_keycodes() bw! endfunc -func Test_simplify_ctrl_at() - " feeding unsimplified CTRL-@ should still trigger i_CTRL-@ - call feedkeys("ifoo\<Esc>A\<*C-@>x", 'xt') - call assert_equal('foofo', getline(1)) - bw! -endfunc - -func Test_simplify_noremap() - call feedkeys("i\<*C-M>", 'nx') - call assert_equal('', getline(1)) - call assert_equal([0, 2, 1, 0, 1], getcurpos()) - bw! -endfunc - -func Test_simplify_timedout() - inoremap <C-M>a b - call feedkeys("i\<*C-M>", 'xt') - call assert_equal('', getline(1)) - call assert_equal([0, 2, 1, 0, 1], getcurpos()) - iunmap <C-M>a - bw! -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 970f5ae0d0..0fc56083aa 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -1260,6 +1260,41 @@ func Test_comment_nested() %bw! endfunc +" Test for a space character in 'comments' setting +func Test_comment_space() + new + setlocal comments=b:\ > fo+=ro + exe "normal i> B\nD\<C-C>ggOA\<C-C>joC" + exe "normal Go > F\nH\<C-C>kOE\<C-C>joG" + let expected =<< trim END + A + > B + C + D + > E + > F + > G + > H + END + call assert_equal(expected, getline(1, '$')) + %bw! +endfunc + +" Test for the 'O' flag in 'comments' +func Test_comment_O() + new + setlocal comments=Ob:* fo+=ro + exe "normal i* B\nD\<C-C>kOA\<C-C>joC" + let expected =<< trim END + A + * B + * C + * D + END + call assert_equal(expected, getline(1, '$')) + %bw! +endfunc + " Test for 'a' and 'w' flags in 'formatoptions' func Test_fo_a_w() new @@ -1299,6 +1334,25 @@ func Test_fo_a_w() %bw! endfunc +" Test for 'j' flag in 'formatoptions' +func Test_fo_j() + new + setlocal fo+=j comments=:// + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 comment2', getline(1)) + setlocal fo-=j + call setline(1, ['i++; // comment1', ' // comment2']) + normal J + call assert_equal('i++; // comment1 // comment2', getline(1)) + " Test with nested comments + setlocal fo+=j comments=n:>,n:) + call setline(1, ['i++; > ) > ) comment1', ' > ) comment2']) + normal J + call assert_equal('i++; > ) > ) comment1 comment2', getline(1)) + %bw! +endfunc + " Test for formatting lines using gq in visual mode func Test_visual_gq_format() new @@ -1480,4 +1534,16 @@ func Test_autoformat_comments() close! endfunc +" This was leaving the cursor after the end of a line. Complicated way to +" have the problem show up with valgrind. +func Test_correct_cursor_position() + " set encoding=iso8859 + new + norm a0000 + sil! norm gggg0i0gw0gg + + bwipe! + set encoding=utf8 +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 56a5ec96af..6adf503f14 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -337,7 +337,7 @@ endfunc " Test that the garbage collector isn't triggered if a timer callback invokes " vgetc(). -func Test_timer_nocatch_garbage_collect() +func Test_nocatch_timer_garbage_collect() " skipped: Nvim does not support test_garbagecollect_soon(), test_override() return " 'uptimetime. must be bigger than the timer timeout diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 598402fafe..646594e482 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -2014,11 +2014,11 @@ endfunc " Test for verbose messages with :try :catch, and :finally {{{1 func Test_try_catch_verbose() " This test works only when the language is English - if v:lang != "C" && v:lang !~ '^[Ee]n' - return - endif + CheckEnglish set verbose=14 + + " Test for verbose messages displayed when an exception is caught redir => msg try echo i diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 1323288676..de4629451b 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1661,16 +1661,25 @@ func Test_compound_assignment_operators() call assert_equal(6, &scrolljump) let &scrolljump %= 5 call assert_equal(1, &scrolljump) - call assert_fails('let &scrolljump .= "j"', 'E734') + call assert_fails('let &scrolljump .= "j"', 'E734:') set scrolljump&vim + let &foldlevelstart = 2 + let &foldlevelstart -= 1 + call assert_equal(1, &foldlevelstart) + let &foldlevelstart -= 1 + call assert_equal(0, &foldlevelstart) + let &foldlevelstart = 2 + let &foldlevelstart -= 2 + call assert_equal(0, &foldlevelstart) + " Test for register let @/ = 1 - call assert_fails('let @/ += 1', 'E734') - call assert_fails('let @/ -= 1', 'E734') - call assert_fails('let @/ *= 1', 'E734') - call assert_fails('let @/ /= 1', 'E734') - call assert_fails('let @/ %= 1', 'E734') + call assert_fails('let @/ += 1', 'E734:') + call assert_fails('let @/ -= 1', 'E734:') + call assert_fails('let @/ *= 1', 'E734:') + call assert_fails('let @/ /= 1', 'E734:') + call assert_fails('let @/ %= 1', 'E734:') let @/ .= 's' call assert_equal('1s', @/) let @/ = '' diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 7decac2c36..83a3216534 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -1074,7 +1074,6 @@ func Test_split_cmds_with_no_room() endfunc func Test_window_resize() - throw 'Skipped: Nvim supports cmdheight=0' " Vertical :resize (absolute, relative, min and max size). vsplit vert resize 8 @@ -1360,7 +1359,6 @@ func Test_win_move_separator() endfunc func Test_win_move_statusline() - redraw " This test fails in Nvim without a redraw to clear messages. edit a leftabove split b let h = winheight(0) @@ -1391,11 +1389,9 @@ func Test_win_move_statusline() call assert_equal(h0, winheight(0)) call assert_equal(1, &cmdheight) endfor - " Nvim supports cmdheight=0 + " supports cmdheight=0 set cmdheight=0 call assert_true(win_move_statusline(0, 1)) - "call assert_equal(h0, winheight(0)) - "call assert_equal(1, &cmdheight) call assert_equal(h0 + 1, winheight(0)) call assert_equal(0, &cmdheight) set cmdheight& diff --git a/src/nvim/testing.h b/src/nvim/testing.h index 1522ebc7b7..69596d725c 100644 --- a/src/nvim/testing.h +++ b/src/nvim/testing.h @@ -1,7 +1,6 @@ #ifndef NVIM_TESTING_H #define NVIM_TESTING_H -#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/types.h b/src/nvim/types.h index 73cd2204d6..00b9e6fc09 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -22,6 +22,8 @@ typedef int handle_T; // absent callback etc. typedef int LuaRef; +typedef void (*FunPtr)(void); + typedef handle_T NS; typedef struct expand expand_T; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index a49e9df9ee..4fcfee1192 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -348,7 +348,8 @@ void ui_attach_impl(UI *ui, uint64_t chanid) if (ui_count == MAX_UI_COUNT) { abort(); } - if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { + if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug] + && !ui_client_channel_id) { ui_comp_attach(ui); } @@ -502,6 +503,9 @@ handle_T ui_cursor_grid(void) void ui_flush(void) { + if (!ui_active()) { + return; + } cmdline_ui_flush(); win_ui_flush(); msg_ext_ui_flush(); @@ -608,6 +612,12 @@ bool ui_has(UIExtension ext) return ui_ext[ext]; } +/// Returns true if the UI has messages area. +bool ui_has_messages(void) +{ + return p_ch > 0 || ui_has(kUIMessages); +} + Array ui_array(void) { Array all_uis = ARRAY_DICT_INIT; diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index be01538f67..a586fec3bf 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -22,11 +22,6 @@ # include "ui_events_client.generated.h" #endif -// Temporary buffer for converting a single grid_line event -static size_t buf_size = 0; -static schar_T *buf_char = NULL; -static sattr_T *buf_attr = NULL; - void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; @@ -46,37 +41,23 @@ void ui_client_init(uint64_t chan) ui_client_channel_id = chan; } -/// Handler for "redraw" events sent by the NVIM server -/// -/// This function will be called by handle_request (in msgpack_rpc/channel.c) -/// The individual ui_events sent by the server are individually handled -/// by their respective handlers defined in ui_events_client.generated.h -/// -/// @note The "flush" event is called only once and only after handling all -/// the other events -/// @param channel_id: The id of the rpc channel -/// @param uidata: The dense array containing the ui_events sent by the server -/// @param[out] err Error details, if any -Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error) { - for (size_t i = 0; i < args.size; i++) { - Array call = args.items[i].data.array; - String name = call.items[0].data.string; - - int hash = ui_client_handler_hash(name.data, name.size); - if (hash < 0) { - ELOG("No ui client handler for %s", name.size ? name.data : "<empty>"); - continue; - } - UIClientHandler handler = event_handlers[hash]; - - // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); - DLOG("Invoke ui client handler for %s", name.data); - for (size_t j = 1; j < call.size; j++) { - handler.fn(call.items[j].data.array); - } + int hash = ui_client_handler_hash(name, name_len); + if (hash < 0) { + return (UIClientHandler){ NULL, NULL }; } + return event_handlers[hash]; +} +/// Placeholder for _sync_ requests with 'redraw' method name +/// +/// async 'redraw' events, which are expected when nvim acts as an ui client. +/// get handled in msgpack_rpc/unpacker.c and directly dispatched to handlers +/// of specific ui events, like ui_client_event_grid_resize and so on. +Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +{ + api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request"); return NIL; } @@ -120,88 +101,30 @@ void ui_client_event_grid_resize(Array args) Integer height = args.items[2].data.integer; ui_call_grid_resize(grid, width, height); - if (buf_size < (size_t)width) { - xfree(buf_char); - xfree(buf_attr); - buf_size = (size_t)width; - buf_char = xmalloc(buf_size * sizeof(schar_T)); - buf_attr = xmalloc(buf_size * sizeof(sattr_T)); + if (grid_line_buf_size < (size_t)width) { + xfree(grid_line_buf_char); + xfree(grid_line_buf_attr); + grid_line_buf_size = (size_t)width; + grid_line_buf_char = xmalloc(grid_line_buf_size * sizeof(schar_T)); + grid_line_buf_attr = xmalloc(grid_line_buf_size * sizeof(sattr_T)); } } void ui_client_event_grid_line(Array args) + FUNC_ATTR_NORETURN { - if (args.size < 4 - || args.items[0].type != kObjectTypeInteger - || args.items[1].type != kObjectTypeInteger - || args.items[2].type != kObjectTypeInteger - || args.items[3].type != kObjectTypeArray) { - goto error; - } + abort(); // unreachable +} - Integer grid = args.items[0].data.integer; - Integer row = args.items[1].data.integer; - Integer startcol = args.items[2].data.integer; - Array cells = args.items[3].data.array; +void ui_client_event_raw_line(GridLineEvent *g) +{ + int grid = g->args[0], row = g->args[1], startcol = g->args[2]; + Integer endcol = startcol + g->coloff; + Integer clearcol = endcol + g->clear_width; // TODO(hlpr98): Accommodate other LineFlags when included in grid_line LineFlags lineflags = 0; - size_t j = 0; - int cur_attr = 0; - int clear_attr = 0; - int clear_width = 0; - for (size_t i = 0; i < cells.size; i++) { - if (cells.items[i].type != kObjectTypeArray) { - goto error; - } - Array cell = cells.items[i].data.array; - - if (cell.size < 1 || cell.items[0].type != kObjectTypeString) { - goto error; - } - String sstring = cell.items[0].data.string; - - char *schar = sstring.data; - int repeat = 1; - if (cell.size >= 2) { - if (cell.items[1].type != kObjectTypeInteger - || cell.items[1].data.integer < 0) { - goto error; - } - cur_attr = (int)cell.items[1].data.integer; - } - - if (cell.size >= 3) { - if (cell.items[2].type != kObjectTypeInteger - || cell.items[2].data.integer < 0) { - goto error; - } - repeat = (int)cell.items[2].data.integer; - } - - if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) { - clear_width = repeat; - break; - } - - for (int r = 0; r < repeat; r++) { - if (j >= buf_size) { - goto error; // _YIKES_ - } - STRLCPY(buf_char[j], schar, sizeof(schar_T)); - buf_attr[j++] = cur_attr; - } - } - - Integer endcol = startcol + (int)j; - Integer clearcol = endcol + clear_width; - clear_attr = cur_attr; - - ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, - (const schar_T *)buf_char, (const sattr_T *)buf_attr); - return; - -error: - ELOG("Error handling ui event 'grid_line'"); + ui_call_raw_line(grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags, + (const schar_T *)grid_line_buf_char, grid_line_buf_attr); } diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 41d9fa6227..311dafaa0b 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -2,12 +2,18 @@ #define NVIM_UI_CLIENT_H #include "nvim/api/private/defs.h" +#include "nvim/grid_defs.h" typedef struct { const char *name; void (*fn)(Array args); } UIClientHandler; +// Temporary buffer for converting a single grid_line event +EXTERN size_t grid_line_buf_size INIT(= 0); +EXTERN schar_T *grid_line_buf_char INIT(= NULL); +EXTERN sattr_T *grid_line_buf_attr INIT(= NULL); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_client.h.generated.h" diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 8324db37c6..45c083b034 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2765,7 +2765,7 @@ void ex_undolist(exarg_T *eap) if (GA_EMPTY(&ga)) { msg(_("Nothing to undo")); } else { - sort_strings((char_u **)ga.ga_data, ga.ga_len); + sort_strings(ga.ga_data, ga.ga_len); msg_start(); msg_puts_attr(_("number changes when saved"), diff --git a/src/nvim/window.c b/src/nvim/window.c index 2bffe2055f..abb277bd23 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -13,6 +13,7 @@ #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" @@ -171,7 +172,7 @@ void do_window(int nchar, long Prenum, int xchar) CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode - if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : Prenum) == NULL) { + if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : (int)Prenum) == NULL) { if (Prenum == 0) { emsg(_(e_noalt)); } else { @@ -181,7 +182,7 @@ void do_window(int nchar, long Prenum, int xchar) } if (!curbuf_locked() && win_split(0, 0) == OK) { - (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : Prenum, + (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : (int)Prenum, (linenr_T)0, GETF_ALT, false); } break; @@ -451,9 +452,9 @@ newwindow: case '}': CHECK_CMDWIN; if (Prenum) { - g_do_tagpreview = Prenum; + g_do_tagpreview = (int)Prenum; } else { - g_do_tagpreview = p_pvh; + g_do_tagpreview = (int)p_pvh; } FALLTHROUGH; case ']': @@ -461,7 +462,7 @@ newwindow: CHECK_CMDWIN; // Keep visual mode, can select words to use as a tag. if (Prenum) { - postponed_split = Prenum; + postponed_split = (int)Prenum; } else { postponed_split = -1; } @@ -550,16 +551,16 @@ wingotofile: case '}': xchar = Ctrl_RSB; if (Prenum) { - g_do_tagpreview = Prenum; + g_do_tagpreview = (int)Prenum; } else { - g_do_tagpreview = p_pvh; + g_do_tagpreview = (int)p_pvh; } FALLTHROUGH; case ']': case Ctrl_RSB: // Keep visual mode, can select words to use as a tag. if (Prenum) { - postponed_split = Prenum; + postponed_split = (int)Prenum; } else { postponed_split = -1; } @@ -725,31 +726,31 @@ void win_set_minimal_style(win_T *wp) wp->w_p_fcs = ((*old == NUL) ? (char_u *)xstrdup("eob: ") : concat_str(old, (char_u *)",eob: ")); - xfree(old); + free_string_option(old); } if (wp->w_hl_ids[HLF_EOB] != -1) { char_u *old = wp->w_p_winhl; wp->w_p_winhl = ((*old == NUL) ? (char_u *)xstrdup("EndOfBuffer:") : concat_str(old, (char_u *)",EndOfBuffer:")); - xfree(old); + free_string_option(old); } // signcolumn: use 'auto' if (wp->w_p_scl[0] != 'a' || STRLEN(wp->w_p_scl) >= 8) { - xfree(wp->w_p_scl); + free_string_option(wp->w_p_scl); wp->w_p_scl = (char_u *)xstrdup("auto"); } // foldcolumn: use '0' if (wp->w_p_fdc[0] != '0') { - xfree(wp->w_p_fdc); + free_string_option(wp->w_p_fdc); wp->w_p_fdc = (char_u *)xstrdup("0"); } // colorcolumn: cleared if (wp->w_p_cc != NULL && *wp->w_p_cc != NUL) { - xfree(wp->w_p_cc); + free_string_option(wp->w_p_cc); wp->w_p_cc = (char_u *)xstrdup(""); } } @@ -799,8 +800,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig) // compute initial position if (wp->w_float_config.relative == kFloatRelativeWindow) { - int row = wp->w_float_config.row; - int col = wp->w_float_config.col; + int row = (int)wp->w_float_config.row; + int col = (int)wp->w_float_config.col; Error dummy = ERROR_INIT; win_T *parent = find_window_by_handle(wp->w_float_config.window, &dummy); if (parent) { @@ -824,8 +825,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig) wp->w_winrow = row; wp->w_wincol = col; } else { - wp->w_winrow = fconfig.row; - wp->w_wincol = fconfig.col; + wp->w_winrow = (int)fconfig.row; + wp->w_wincol = (int)fconfig.col; } // changing border style while keeping border only requires redrawing border @@ -1064,10 +1065,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) * width. */ // Current window requires at least 1 space. - wmw1 = (p_wmw == 0 ? 1 : p_wmw); + wmw1 = (p_wmw == 0 ? 1 : (int)p_wmw); needed = wmw1 + 1; if (flags & WSP_ROOM) { - needed += p_wiw - wmw1; + needed += (int)p_wiw - wmw1; } if (flags & (WSP_BOT | WSP_TOP)) { minwidth = frame_minwidth(topframe, NOWIN); @@ -1141,10 +1142,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Check if we are able to split the current window and compute its height. // Current window requires at least 1 space plus space for the window bar. - wmh1 = MAX(p_wmh, 1) + oldwin->w_winbar_height; + wmh1 = MAX((int)p_wmh, 1) + oldwin->w_winbar_height; needed = wmh1 + STATUS_HEIGHT; if (flags & WSP_ROOM) { - needed += p_wh - wmh1 + oldwin->w_winbar_height; + needed += (int)p_wh - wmh1 + oldwin->w_winbar_height; } if (p_ch < 1) { needed += 1; // Adjust for cmdheight=0. @@ -1304,7 +1305,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Need to create a new frame in the tree to make a branch. frp = xcalloc(1, sizeof(frame_T)); *frp = *curfrp; - curfrp->fr_layout = layout; + curfrp->fr_layout = (char)layout; frp->fr_parent = curfrp; frp->fr_next = NULL; frp->fr_prev = NULL; @@ -1489,20 +1490,17 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Don't change the window height/width to 'winheight' / 'winwidth' if a // size was given. if (flags & WSP_VERT) { - i = p_wiw; + i = (int)p_wiw; if (size != 0) { p_wiw = size; } } else { - i = p_wh; + i = (int)p_wh; if (size != 0) { p_wh = size; } } - // Keep same changelist position in new window. - wp->w_changelistidx = oldwin->w_changelistidx; - // make the new window the current window win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS); @@ -1573,6 +1571,10 @@ static void win_init(win_T *newp, win_T *oldp, int flags) } newp->w_tagstackidx = oldp->w_tagstackidx; newp->w_tagstacklen = oldp->w_tagstacklen; + + // Keep same changelist position in new window. + newp->w_changelistidx = oldp->w_changelistidx; + copyFoldingState(oldp, newp); win_init_some(newp, oldp); @@ -1687,14 +1689,14 @@ int make_windows(int count, bool vertical) if (vertical) { // Each window needs at least 'winminwidth' lines and a separator column. - maxcount = (curwin->w_width + curwin->w_vsep_width - - (p_wiw - p_wmw)) / (p_wmw + 1); + maxcount = (int)(curwin->w_width + curwin->w_vsep_width + - (p_wiw - p_wmw)) / ((int)p_wmw + 1); } else { // Each window needs at least 'winminheight' lines. // If statusline isn't global, each window also needs a statusline. // If 'winbar' is set, each window also needs a winbar. - maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height - - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT + global_winbar_height()); + maxcount = (int)(curwin->w_height + curwin->w_hsep_height + curwin->w_status_height + - (p_wh - p_wmh)) / ((int)p_wmh + STATUS_HEIGHT + global_winbar_height()); } if (maxcount < 2) { @@ -2037,10 +2039,10 @@ void win_move_after(win_T *win1, win_T *win2) static int get_maximum_wincount(frame_T *fr, int height) { if (fr->fr_layout != FR_COL) { - return (height / (p_wmh + STATUS_HEIGHT + frame2win(fr)->w_winbar_height)); + return (height / ((int)p_wmh + STATUS_HEIGHT + frame2win(fr)->w_winbar_height)); } else if (global_winbar_height()) { // If winbar is globally enabled, no need to check each window for it. - return (height / (p_wmh + STATUS_HEIGHT + 1)); + return (height / ((int)p_wmh + STATUS_HEIGHT + 1)); } frame_T *frp; @@ -2053,13 +2055,13 @@ static int get_maximum_wincount(frame_T *fr, int height) if (height < (p_wmh + STATUS_HEIGHT + wp->w_winbar_height)) { break; } - height -= p_wmh + STATUS_HEIGHT + wp->w_winbar_height; + height -= (int)p_wmh + STATUS_HEIGHT + wp->w_winbar_height; total_wincount += 1; } // If we still have enough room for more windows, just use the default winbar height (which is 0) // in order to get the amount of windows that'd fit in the remaining space - total_wincount += height / (p_wmh + STATUS_HEIGHT); + total_wincount += height / ((int)p_wmh + STATUS_HEIGHT); return total_wincount; } @@ -2132,7 +2134,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { extra_sep = 0; } - totwincount = (n + extra_sep) / (p_wmw + 1); + totwincount = (n + extra_sep) / ((int)p_wmw + 1); has_next_curwin = frame_has_win(topfr, next_curwin); /* @@ -2143,29 +2145,27 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int m = frame_minwidth(topfr, next_curwin); room = width - m; if (room < 0) { - next_curwin_size = p_wiw + room; + next_curwin_size = (int)p_wiw + room; room = 0; } else { next_curwin_size = -1; FOR_ALL_FRAMES(fr, topfr->fr_child) { - // If 'winfixwidth' set keep the window width if - // possible. - // Watch out for this window being the next_curwin. if (!frame_fixed_width(fr)) { continue; } + // If 'winfixwidth' set keep the window width if possible. + // Watch out for this window being the next_curwin. n = frame_minwidth(fr, NOWIN); new_size = fr->fr_width; if (frame_has_win(fr, next_curwin)) { - room += p_wiw - p_wmw; + room += (int)p_wiw - (int)p_wmw; next_curwin_size = 0; if (new_size < p_wiw) { - new_size = p_wiw; + new_size = (int)p_wiw; } } else { // These windows don't use up room. - totwincount -= (n + (fr->fr_next == NULL - ? extra_sep : 0)) / (p_wmw + 1); + totwincount -= (n + (fr->fr_next == NULL ? extra_sep : 0)) / ((int)p_wmw + 1); } room -= new_size - n; if (room < 0) { @@ -2182,12 +2182,12 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int / (totwincount - 1) > p_wiw) { // Can make all windows wider than 'winwidth', spread // the room equally. - next_curwin_size = (room + p_wiw - + (totwincount - 1) * p_wmw - + (totwincount - 1)) / totwincount; - room -= next_curwin_size - p_wiw; + next_curwin_size = (int)(room + p_wiw + + (totwincount - 1) * p_wmw + + (totwincount - 1)) / totwincount; + room -= next_curwin_size - (int)p_wiw; } else { - next_curwin_size = p_wiw; + next_curwin_size = (int)p_wiw; } } } @@ -2210,8 +2210,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { // Compute the maximum number of windows horiz. in "fr". n = frame_minwidth(fr, NOWIN); - wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) - / (p_wmw + 1); + wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) / ((int)p_wmw + 1); m = frame_minwidth(fr, next_curwin); if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); @@ -2227,7 +2226,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int new_size = (wincount * room + (totwincount / 2)) / totwincount; } if (hnc) { // add next_curwin size - next_curwin_size -= p_wiw - (m - n); + next_curwin_size -= (int)p_wiw - (m - n); new_size += next_curwin_size; room -= new_size - next_curwin_size; } else { @@ -2276,24 +2275,23 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int if (room < 0) { // The room is less than 'winheight', use all space for the // current window. - next_curwin_size = p_wh + room; + next_curwin_size = (int)p_wh + room; room = 0; } else { next_curwin_size = -1; FOR_ALL_FRAMES(fr, topfr->fr_child) { - // If 'winfixheight' set keep the window height if - // possible. - // Watch out for this window being the next_curwin. if (!frame_fixed_height(fr)) { continue; } + // If 'winfixheight' set keep the window height if possible. + // Watch out for this window being the next_curwin. n = frame_minheight(fr, NOWIN); new_size = fr->fr_height; if (frame_has_win(fr, next_curwin)) { - room += p_wh - p_wmh; + room += (int)p_wh - (int)p_wmh; next_curwin_size = 0; if (new_size < p_wh) { - new_size = p_wh; + new_size = (int)p_wh; } } else { // These windows don't use up room. @@ -2314,12 +2312,12 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int / (totwincount - 1) > p_wh) { // can make all windows higher than 'winheight', // spread the room equally. - next_curwin_size = (room + p_wh - + (totwincount - 1) * p_wmh - + (totwincount - 1)) / totwincount; - room -= next_curwin_size - p_wh; + next_curwin_size = (int)(room + p_wh + + (totwincount - 1) * p_wmh + + (totwincount - 1)) / totwincount; + room -= next_curwin_size - (int)p_wh; } else { - next_curwin_size = p_wh; + next_curwin_size = (int)p_wh; } } } @@ -2358,7 +2356,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int new_size = (wincount * room + (totwincount / 2)) / totwincount; } if (hnc) { // add next_curwin size - next_curwin_size -= p_wh - (m - n); + next_curwin_size -= (int)p_wh - (m - n); new_size += next_curwin_size; room -= new_size - next_curwin_size; } else { @@ -3778,9 +3776,9 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) + topfrp->fr_win->w_status_height; if (topfrp->fr_win == next_curwin) { - m = p_wh + extra_height; + m = (int)p_wh + extra_height; } else { - m = p_wmh + extra_height; + m = (int)p_wmh + extra_height; if (topfrp->fr_win == curwin && next_curwin == NULL) { // Current window is minimal one line high. if (p_wmh == 0) { @@ -3821,10 +3819,10 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) if (topfrp->fr_win != NULL) { if (topfrp->fr_win == next_curwin) { - m = p_wiw + topfrp->fr_win->w_vsep_width; + m = (int)p_wiw + topfrp->fr_win->w_vsep_width; } else { // window: minimal width of the window plus separator column - m = p_wmw + topfrp->fr_win->w_vsep_width; + m = (int)p_wmw + topfrp->fr_win->w_vsep_width; // Current window is minimal one column wide if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) { ++m; @@ -3903,9 +3901,7 @@ void close_others(int message, int forceit) continue; } } - win_close(wp, - !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer), - false); + win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer), false); } if (message && !ONE_WINDOW) { @@ -3981,7 +3977,7 @@ static int win_alloc_firstwin(win_T *oldwin) new_frame(curwin); topframe = curwin->w_frame; topframe->fr_width = Columns; - topframe->fr_height = Rows - p_ch - global_stl_height(); + topframe->fr_height = Rows - (int)p_ch - global_stl_height(); return OK; } @@ -4003,11 +3999,11 @@ static void new_frame(win_T *wp) */ void win_init_size(void) { - firstwin->w_height = ROWS_AVAIL; + firstwin->w_height = (int)ROWS_AVAIL; firstwin->w_height_inner = firstwin->w_height - firstwin->w_winbar_height; firstwin->w_height_outer = firstwin->w_height; firstwin->w_winrow_off = firstwin->w_winbar_height; - topframe->fr_height = ROWS_AVAIL; + topframe->fr_height = (int)ROWS_AVAIL; firstwin->w_width = Columns; firstwin->w_width_inner = firstwin->w_width; firstwin->w_width_outer = firstwin->w_width; @@ -4116,7 +4112,6 @@ int win_new_tabpage(int after, char_u *filename) newtp->tp_topframe = topframe; last_status(false); - set_winbar(); redraw_all_later(NOT_VALID); @@ -4167,7 +4162,7 @@ int make_tabpages(int maxcount) // Limit to 'tabpagemax' tabs. if (count > p_tpm) { - count = p_tpm; + count = (int)p_tpm; } /* @@ -4303,7 +4298,7 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds) tp->tp_prevwin = prevwin; tp->tp_firstwin = firstwin; tp->tp_lastwin = lastwin; - tp->tp_old_Rows = Rows; + tp->tp_old_Rows_avail = ROWS_AVAIL; tp->tp_old_Columns = Columns; firstwin = NULL; lastwin = NULL; @@ -4343,10 +4338,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a const int row = win_comp_pos(); // recompute w_winrow for all windows diff_need_scrollbind = true; - // The tabpage line may have appeared or disappeared, may need to resize - // the frames for that. When the Vim window was resized need to update - // frame sizes too. Use the stored value of p_ch, so that it can be - // different for each tab page. + // Use the stored value of p_ch, so that it can be different for each tab page. if (p_ch != curtab->tp_ch_used) { clear_cmdline = true; } @@ -4359,7 +4351,9 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a clear_cmdline = true; } - if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { + // The tabpage line may have appeared or disappeared, may need to resize the frames for that. + // When the Vim window was resized or ROWS_AVAIL changed need to update frame sizes too. + if (curtab->tp_old_Rows_avail != ROWS_AVAIL || (old_off != firstwin->w_winrow)) { win_new_screen_rows(); } if (curtab->tp_old_Columns != Columns && starting == 0) { @@ -5118,10 +5112,10 @@ static void win_free(win_T *wp, tabpage_T *tp) xfree(wp->w_localdir); xfree(wp->w_prevdir); - stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); xfree(wp->w_status_click_defs); - stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); xfree(wp->w_winbar_click_defs); // Remove the window from the b_wininfo lists, it may happen that the @@ -5503,7 +5497,7 @@ void win_setheight_win(int height, win_T *win) { // Always keep current window at least one line high, even when 'winminheight' is zero. // Keep window at least two lines high if 'winbar' is enabled. - height = MAX(height, (win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height); + height = MAX(height, (int)(win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height); if (win->w_floating) { win->w_float_config.height = height; @@ -5566,7 +5560,7 @@ static void frame_setheight(frame_T *curfrp, int height) // If height is greater than the available space, try to create space for // the frame by reducing 'cmdheight' if possible, while making sure // `cmdheight` doesn't go below 1. - height = MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height); + height = (int)MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height); } if (height > 0) { frame_new_height(curfrp, height, false, false); @@ -5608,7 +5602,7 @@ static void frame_setheight(frame_T *curfrp, int height) room_cmdline = 0; } else { win_T *wp = lastwin_nofloating(); - room_cmdline = Rows - p_ch - global_stl_height() + room_cmdline = Rows - (int)p_ch - global_stl_height() - (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height); if (room_cmdline < 0) { room_cmdline = 0; @@ -5718,7 +5712,7 @@ void win_setwidth_win(int width, win_T *wp) // 'winminwidth' is zero. if (wp == curwin) { if (width < p_wmw) { - width = p_wmw; + width = (int)p_wmw; } if (width == 0) { width = 1; @@ -5882,8 +5876,8 @@ void win_setminheight(void) // loop until there is a 'winminheight' that is possible while (p_wmh > 0) { - const int room = Rows - p_ch; - const int needed = min_rows() - 1; // 1 was added for the cmdline + const int room = Rows - (int)p_ch; + const int needed = min_rows(); if (room >= needed) { break; } @@ -5973,7 +5967,7 @@ void win_drag_status_line(win_T *dragwin, int offset) // Only dragging the last status line can reduce p_ch. room = Rows - cmdline_row; if (curfr->fr_next != NULL) { - room -= p_ch + global_stl_height(); + room -= (int)p_ch + global_stl_height(); } if (room < 0) { room = 0; @@ -6151,8 +6145,7 @@ void set_fraction(win_T *wp) // When cursor is in the first line the percentage is computed as if // it's halfway that line. Thus with two lines it is 25%, with three // lines 17%, etc. Similarly for the last line: 75%, 83%, etc. - wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) - / (long)wp->w_height_inner; + wp->w_fraction = (int)(wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) / wp->w_height_inner; } } @@ -6199,7 +6192,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) if (lnum < 1) { // can happen when starting up lnum = 1; } - wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; + wp->w_wrow = (int)((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; sline = wp->w_wrow - line_size; @@ -6314,7 +6307,8 @@ void win_set_inner_size(win_T *wp) // There is no point in adjusting the scroll position when exiting. Some // values might be invalid. - if (!exiting) { + // Skip scroll_to_fraction() when 'cmdheight' was set to one from zero. + if (!exiting && !made_cmdheight_nonzero) { scroll_to_fraction(wp, prev_height); } redraw_later(wp, NOT_VALID); // SOME_VALID?? @@ -6384,7 +6378,7 @@ void command_height(void) { int h; frame_T *frp; - int old_p_ch = curtab->tp_ch_used; + int old_p_ch = (int)curtab->tp_ch_used; // Use the value of p_ch that we remembered. This is needed for when the // GUI starts up, we can't be sure in what order things happen. And when @@ -6404,7 +6398,7 @@ void command_height(void) } if (starting != NO_SCREEN) { - cmdline_row = Rows - p_ch; + cmdline_row = Rows - (int)p_ch; if (p_ch > old_p_ch) { // p_ch got bigger while (p_ch > old_p_ch) { @@ -6412,12 +6406,12 @@ void command_height(void) emsg(_(e_noroom)); p_ch = old_p_ch; curtab->tp_ch_used = p_ch; - cmdline_row = Rows - p_ch; + cmdline_row = Rows - (int)p_ch; break; } h = frp->fr_height - frame_minheight(frp, NULL); if (h > p_ch - old_p_ch) { - h = p_ch - old_p_ch; + h = (int)p_ch - old_p_ch; } old_p_ch += h; frame_add_height(frp, -h); @@ -6481,9 +6475,9 @@ char_u *grab_file_name(long count, linenr_T *file_lnum) } // Only recognize ":123" here if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) { - char_u *p = ptr + len + 1; + char *p = (char *)ptr + len + 1; - *file_lnum = getdigits_long(&p, false, 0); + *file_lnum = (linenr_T)getdigits_long(&p, false, 0); } return find_file_name_in_path(ptr, len, options, count, (char_u *)curbuf->b_ffname); } @@ -6607,7 +6601,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u } p = skipwhite(p); if (isdigit(*p)) { - *file_lnum = getdigits_long((char_u **)&p, false, 0); + *file_lnum = (linenr_T)getdigits_long(&p, false, 0); } } } @@ -6637,7 +6631,7 @@ static void win_remove_status_line(win_T *wp, bool add_hsep) } comp_col(); - stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); xfree(wp->w_status_click_defs); wp->w_status_click_defs_size = 0; wp->w_status_click_defs = NULL; @@ -6745,34 +6739,50 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) } } -// Add or remove window bars from windows depending on the value of 'winbar'. -void set_winbar(void) -{ - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - // Require the local value to be set in order to show winbar on a floating window. - int winbar_height = wp->w_floating ? ((*wp->w_p_wbr != NUL) ? 1 : 0) - : ((*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0); - - if (wp->w_winbar_height != winbar_height) { - if (winbar_height == 1 && wp->w_height_inner <= 1) { - if (wp->w_floating) { - emsg(_(e_noroom)); - continue; - } else if (!resize_frame_for_winbar(wp->w_frame)) { - return; - } +/// Add or remove window bar from window "wp". +/// +/// @param make_room Whether to resize frames to make room for winbar. +/// +/// @return Success status. +int set_winbar_win(win_T *wp, bool make_room) +{ + // Require the local value to be set in order to show winbar on a floating window. + int winbar_height = wp->w_floating ? ((*wp->w_p_wbr != NUL) ? 1 : 0) + : ((*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0); + + if (wp->w_winbar_height != winbar_height) { + if (winbar_height == 1 && wp->w_height_inner <= 1) { + if (wp->w_floating) { + emsg(_(e_noroom)); + return NOTDONE; + } else if (!make_room || !resize_frame_for_winbar(wp->w_frame)) { + return FAIL; } - wp->w_winbar_height = winbar_height; - win_set_inner_size(wp); - wp->w_redr_status = wp->w_redr_status || winbar_height; + } + wp->w_winbar_height = winbar_height; + win_set_inner_size(wp); + wp->w_redr_status = wp->w_redr_status || winbar_height; - if (winbar_height == 0) { - // When removing winbar, deallocate the w_winbar_click_defs array - stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); - xfree(wp->w_winbar_click_defs); - wp->w_winbar_click_defs_size = 0; - wp->w_winbar_click_defs = NULL; - } + if (winbar_height == 0) { + // When removing winbar, deallocate the w_winbar_click_defs array + stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); + xfree(wp->w_winbar_click_defs); + wp->w_winbar_click_defs_size = 0; + wp->w_winbar_click_defs = NULL; + } + } + + return OK; +} + +/// Add or remove window bars from all windows in tab depending on the value of 'winbar'. +/// +/// @param make_room Whether to resize frames to make room for winbar. +void set_winbar(bool make_room) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (set_winbar_win(wp, make_room) == FAIL) { + break; } } } @@ -6821,7 +6831,9 @@ int min_rows(void) } } total += tabline_height() + global_stl_height(); - total += 1; // count the room for the command line + if (p_ch > 0) { + total += 1; // count the room for the command line + } return total; } @@ -6846,43 +6858,67 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return count <= 1; } -/// Correct the cursor line number in other windows. Used after changing the -/// current buffer, and before applying autocommands. -/// -/// @param do_curwin when true, also check current window. -void check_lnums(bool do_curwin) +/// Implementation of check_lnums() and check_lnums_nested(). +static void check_lnums_both(bool do_curwin, bool nested) { FOR_ALL_TAB_WINDOWS(tp, wp) { if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) { - // save the original cursor position and topline - wp->w_save_cursor.w_cursor_save = wp->w_cursor; - wp->w_save_cursor.w_topline_save = wp->w_topline; + if (!nested) { + // save the original cursor position and topline + wp->w_save_cursor.w_cursor_save = wp->w_cursor; + wp->w_save_cursor.w_topline_save = wp->w_topline; + } - if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count) { + bool need_adjust = wp->w_cursor.lnum > curbuf->b_ml.ml_line_count; + if (need_adjust) { wp->w_cursor.lnum = curbuf->b_ml.ml_line_count; } - if (wp->w_topline > curbuf->b_ml.ml_line_count) { - wp->w_topline = curbuf->b_ml.ml_line_count; + if (need_adjust || !nested) { + // save the (corrected) cursor position + wp->w_save_cursor.w_cursor_corr = wp->w_cursor; } - // save the corrected cursor position and topline - wp->w_save_cursor.w_cursor_corr = wp->w_cursor; - wp->w_save_cursor.w_topline_corr = wp->w_topline; + need_adjust = wp->w_topline > curbuf->b_ml.ml_line_count; + if (need_adjust) { + wp->w_topline = curbuf->b_ml.ml_line_count; + } + if (need_adjust || !nested) { + // save the (corrected) topline + wp->w_save_cursor.w_topline_corr = wp->w_topline; + } } } } +/// Correct the cursor line number in other windows. Used after changing the +/// current buffer, and before applying autocommands. +/// +/// @param do_curwin when true, also check current window. +void check_lnums(bool do_curwin) +{ + check_lnums_both(do_curwin, false); +} + +/// Like check_lnums() but for when check_lnums() was already called. +void check_lnums_nested(bool do_curwin) +{ + check_lnums_both(do_curwin, true); +} + /// Reset cursor and topline to its stored values from check_lnums(). /// check_lnums() must have been called first! void reset_lnums(void) { FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == curbuf) { - // Restore the value if the autocommand didn't change it. - if (equalpos(wp->w_save_cursor.w_cursor_corr, wp->w_cursor)) { + // Restore the value if the autocommand didn't change it and it was + // set. + if (equalpos(wp->w_save_cursor.w_cursor_corr, wp->w_cursor) + && wp->w_save_cursor.w_cursor_save.lnum != 0) { wp->w_cursor = wp->w_save_cursor.w_cursor_save; } - if (wp->w_save_cursor.w_topline_corr == wp->w_topline) { + if (wp->w_save_cursor.w_topline_corr == wp->w_topline + && wp->w_save_cursor.w_topline_save != 0) { wp->w_topline = wp->w_save_cursor.w_topline_save; } } @@ -7201,14 +7237,14 @@ int win_getid(typval_T *argvars) if (argvars[0].v_type == VAR_UNKNOWN) { return curwin->handle; } - int winnr = tv_get_number(&argvars[0]); + int winnr = (int)tv_get_number(&argvars[0]); win_T *wp; if (winnr > 0) { if (argvars[1].v_type == VAR_UNKNOWN) { wp = firstwin; } else { tabpage_T *tp = NULL; - int tabnr = tv_get_number(&argvars[1]); + int tabnr = (int)tv_get_number(&argvars[1]); FOR_ALL_TABS(tp2) { if (--tabnr == 0) { tp = tp2; @@ -7235,7 +7271,7 @@ int win_getid(typval_T *argvars) int win_gotoid(typval_T *argvars) { - int id = tv_get_number(&argvars[0]); + int id = (int)tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -7302,7 +7338,7 @@ win_T *win_id2wp_tp(int id, tabpage_T **tpp) int win_id2win(typval_T *argvars) { int nr = 1; - int id = tv_get_number(&argvars[0]); + int id = (int)tv_get_number(&argvars[0]); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->handle == id) { @@ -7315,7 +7351,7 @@ int win_id2win(typval_T *argvars) void win_findbuf(typval_T *argvars, list_T *list) { - int bufnr = tv_get_number(&argvars[0]); + int bufnr = (int)tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 933103046c..c4197f0b3e 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -34,6 +34,7 @@ describe('API: highlight',function() underdotted = true, underdashed = true, strikethrough = true, + nocombine = true, } before_each(function() @@ -55,7 +56,7 @@ describe('API: highlight',function() eq('Invalid highlight id: 30000', string.match(emsg, 'Invalid.*')) -- Test all highlight properties. - command('hi NewHighlight gui=underline,bold,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough') + command('hi NewHighlight gui=underline,bold,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough,nocombine') eq(expected_rgb2, nvim("get_hl_by_id", hl_id, true)) -- Test nil argument. @@ -136,10 +137,10 @@ describe('API: highlight',function() -- Test cterm & Normal values. #18024 (tail) & #18980 -- Ensure Normal, and groups that match Normal return their fg & bg cterm values meths.set_hl(0, 'Normal', {ctermfg = 17, ctermbg = 213}) - meths.set_hl(0, 'NotNormal', {ctermfg = 17, ctermbg = 213}) + meths.set_hl(0, 'NotNormal', {ctermfg = 17, ctermbg = 213, nocombine = true}) -- Note colors are "cterm" values, not rgb-as-ints eq({foreground = 17, background = 213}, nvim("get_hl_by_name", 'Normal', false)) - eq({foreground = 17, background = 213}, nvim("get_hl_by_name", 'NotNormal', false)) + eq({foreground = 17, background = 213, nocombine = true}, nvim("get_hl_by_name", 'NotNormal', false)) end) it('nvim_get_hl_id_by_name', function() @@ -214,6 +215,7 @@ describe("API: set highlight", function() reverse = true, undercurl = true, strikethrough = true, + nocombine = true, } } local highlight3_result_gui = { @@ -236,6 +238,7 @@ describe("API: set highlight", function() reverse = true, undercurl = true, strikethrough = true, + nocombine = true, } local function get_ns() @@ -290,7 +293,7 @@ describe("API: set highlight", function() exec_capture('highlight Test_hl')) meths.set_hl(0, 'Test_hl2', highlight3_config) - eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough ctermfg=8 ctermbg=15 gui=bold,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa', + eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough,nocombine ctermfg=8 ctermbg=15 gui=bold,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa', exec_capture('highlight Test_hl2')) -- Colors are stored with the name they are defined, but diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 6bc6651e04..eb2a467a8b 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -25,6 +25,7 @@ describe('nvim_get_keymap', function() local foo_bar_string = 'nnoremap foo bar' local foo_bar_map_table = { lhs='foo', + lhsraw='foo', script=0, silent=0, rhs='bar', @@ -56,6 +57,7 @@ describe('nvim_get_keymap', function() command('nnoremap foo_longer bar_longer') local foolong_bar_map_table = shallowcopy(foo_bar_map_table) foolong_bar_map_table['lhs'] = 'foo_longer' + foolong_bar_map_table['lhsraw'] = 'foo_longer' foolong_bar_map_table['rhs'] = 'bar_longer' eq({foolong_bar_map_table, foo_bar_map_table}, @@ -87,6 +89,7 @@ describe('nvim_get_keymap', function() command('nnoremap foo_longer bar_longer') local foolong_bar_map_table = shallowcopy(foo_bar_map_table) foolong_bar_map_table['lhs'] = 'foo_longer' + foolong_bar_map_table['lhsraw'] = 'foo_longer' foolong_bar_map_table['rhs'] = 'bar_longer' local buffer_table = shallowcopy(foo_bar_map_table) @@ -283,6 +286,16 @@ describe('nvim_get_keymap', function() command('onoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\') command('onoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\') + -- wrapper around get_keymap() that drops "lhsraw" and "lhsrawalt" which are hard to check + local function get_keymap_noraw(...) + local ret = meths.get_keymap(...) + for _, item in ipairs(ret) do + item.lhsraw = nil + item.lhsrawalt = nil + end + return ret + end + for _, cmd in ipairs({ 'set cpo-=B', 'set cpo+=B', @@ -290,22 +303,23 @@ describe('nvim_get_keymap', function() command(cmd) eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'n'), cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'n')}, - meths.get_keymap('n')) + get_keymap_noraw('n')) eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'x'), cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'x')}, - meths.get_keymap('x')) + get_keymap_noraw('x')) eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 's'), cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 's')}, - meths.get_keymap('s')) + get_keymap_noraw('s')) eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 'o'), cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 'o')}, - meths.get_keymap('o')) + get_keymap_noraw('o')) end end) it('always uses space for space and bar for bar', function() local space_table = { lhs='| |', + lhsraw='| |', rhs='| |', mode='n', script=0, @@ -340,6 +354,7 @@ describe('nvim_get_keymap', function() mapargs[1].callback = nil eq({ lhs='asdf', + lhsraw='asdf', script=0, silent=0, expr=0, @@ -356,6 +371,7 @@ describe('nvim_get_keymap', function() meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"}) eq({ lhs='lhs', + lhsraw='lhs', rhs='rhs', script=0, silent=0, @@ -413,7 +429,11 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- Gets a maparg() dict from Nvim, if one exists. local function get_mapargs(mode, lhs) - return funcs.maparg(lhs, normalize_mapmode(mode), false, true) + local mapargs = funcs.maparg(lhs, normalize_mapmode(mode), false, true) + -- drop "lhsraw" and "lhsrawalt" which are hard to check + mapargs.lhsraw = nil + mapargs.lhsrawalt = nil + return mapargs end it('error on empty LHS', function() @@ -503,6 +523,11 @@ describe('nvim_set_keymap, nvim_del_keymap', function() pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true})) end) + it('error when "replace_keycodes" is used without "expr"', function() + eq('"replace_keycodes" requires "expr"', + pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {replace_keycodes = true})) + end) + local optnames = {'nowait', 'silent', 'script', 'expr', 'unique'} for _, opt in ipairs(optnames) do -- note: need '%' to escape hyphens, which have special meaning in lua @@ -817,17 +842,39 @@ describe('nvim_set_keymap, nvim_del_keymap', function() local mapargs = funcs.maparg('asdf', 'n', false, true) assert(type(mapargs.callback) == 'number', 'callback is not luaref number') mapargs.callback = nil + mapargs.lhsraw = nil + mapargs.lhsrawalt = nil eq(generate_mapargs('n', 'asdf', nil, {sid=sid_lua}), mapargs) end) - it('can make lua expr mappings', function() + it('can make lua expr mappings replacing keycodes', function() exec_lua [[ - vim.api.nvim_set_keymap ('n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true }) + vim.api.nvim_set_keymap ('n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true }) ]] feed('aa') - eq(99, exec_lua[[return SomeValue]]) + eq({'π<M-π>foo<'}, meths.buf_get_lines(0, 0, -1, false)) + end) + + it('can make lua expr mappings without replacing keycodes', function() + exec_lua [[ + vim.api.nvim_set_keymap ('i', 'aa', '', {callback = function() return '<space>' end, expr = true }) + ]] + + feed('iaa<esc>') + + eq({'<space>'}, meths.buf_get_lines(0, 0, -1, false)) + end) + + it('lua expr mapping returning nil is equivalent to returnig an empty string', function() + exec_lua [[ + vim.api.nvim_set_keymap ('i', 'aa', '', {callback = function() return nil end, expr = true }) + ]] + + feed('iaa<esc>') + + eq({''}, meths.buf_get_lines(0, 0, -1, false)) end) it('does not reset pum in lua mapping', function() @@ -1018,16 +1065,27 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() eq(1, exec_lua[[return GlobalCount]]) end) - it('can make lua expr mappings', function() + it('can make lua expr mappings replacing keycodes', function() exec_lua [[ - vim.api.nvim_buf_set_keymap (0, 'n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true }) + vim.api.nvim_buf_set_keymap (0, 'n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true }) ]] feed('aa') - eq(99, exec_lua[[return SomeValue ]]) + eq({'π<M-π>foo<'}, meths.buf_get_lines(0, 0, -1, false)) + end) + + it('can make lua expr mappings without replacing keycodes', function() + exec_lua [[ + vim.api.nvim_buf_set_keymap (0, 'i', 'aa', '', {callback = function() return '<space>' end, expr = true }) + ]] + + feed('iaa<esc>') + + eq({'<space>'}, meths.buf_get_lines(0, 0, -1, false)) end) + it('can overwrite lua mappings', function() eq(0, exec_lua [[ GlobalCount = 0 diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3724dbf820..17de6730fb 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3733,6 +3733,12 @@ describe('API', function() eq("", meths.cmd({ cmd = "Foo", bang = false }, { output = true })) end) it('works with modifiers', function() + -- with :silent output is still captured + eq('1', + meths.cmd({ cmd = 'echomsg', args = { '1' }, mods = { silent = true } }, + { output = true })) + -- with :silent message isn't added to message history + eq('', meths.cmd({ cmd = 'messages' }, { output = true })) meths.create_user_command("Foo", 'set verbose', {}) eq(" verbose=1", meths.cmd({ cmd = "Foo", mods = { verbose = 1 } }, { output = true })) eq(0, meths.get_option_value("verbose", {})) diff --git a/test/functional/editor/mark_spec.lua b/test/functional/editor/mark_spec.lua index 1eb76aa628..2440867c6e 100644 --- a/test/functional/editor/mark_spec.lua +++ b/test/functional/editor/mark_spec.lua @@ -157,6 +157,13 @@ describe('named marks', function() os.remove(file1) end) + it("errors when using a mark in another buffer in command range", function() + feed('ifoo<Esc>mA') + command('enew') + feed('ibar<Esc>') + eq('Vim(print):E20: Mark not set', pcall_err(command, [['Aprint]])) + end) + it("leave a context mark when moving with '", function() command("edit " .. file1) feed("llmamA") diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua index 23964ca10f..825b20138a 100644 --- a/test/functional/editor/meta_key_spec.lua +++ b/test/functional/editor/meta_key_spec.lua @@ -91,7 +91,7 @@ describe('meta-keys #8226 #13042', function() command('tnoremap <A-j> alt-j') feed('i<M-l> xxx <A-j>') eq('meta-l xxx alt-j', exec_lua([[return _G.input_data]])) - -- Unmapped ALT-chord is sent to terminal as-is. #16220 + -- Unmapped ALT-chord is sent to terminal as-is. #16202 #16220 exec_lua([[_G.input_data = '']]) command('tunmap <M-l>') feed('<M-l>') diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua index cc9fce8f67..5050edff5c 100644 --- a/test/functional/editor/put_spec.lua +++ b/test/functional/editor/put_spec.lua @@ -879,9 +879,13 @@ describe('put command', function() ine of words 2]], curbuf_contents()) end) - local function bell_test(actions, should_ring) - local screen = Screen.new() + local screen + setup(function() + screen = Screen.new() screen:attach() + end) + + local function bell_test(actions, should_ring) if should_ring then -- check bell is not set by nvim before the action screen:sleep(50) @@ -899,7 +903,6 @@ describe('put command', function() end end end, unchanged=(not should_ring)} - screen:detach() end it('should not ring the bell with gp at end of line', function() diff --git a/test/functional/editor/tabpage_spec.lua b/test/functional/editor/tabpage_spec.lua index 3b2c1db350..7dd0b9f154 100644 --- a/test/functional/editor/tabpage_spec.lua +++ b/test/functional/editor/tabpage_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local clear = helpers.clear local command = helpers.command @@ -53,6 +54,46 @@ describe('tabpage', function() neq(999, eval('g:win_closed')) end) + it('switching tabpage after setting laststatus=3 #19591', function() + local screen = Screen.new(40, 8) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, + [1] = {bold = true, reverse = true}, -- StatusLine + [2] = {reverse = true}, -- StatusLineNC, TabLineFill + [3] = {bold = true}, -- TabLineSel + [4] = {background = Screen.colors.LightGrey, underline = true}, -- TabLine + [5] = {bold = true, foreground = Screen.colors.Magenta}, + }) + screen:attach() + + command('tabnew') + command('tabprev') + command('set laststatus=3') + command('tabnext') + feed('<C-G>') + screen:expect([[ + {4: [No Name] }{3: [No Name] }{2: }{4:X}| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[No Name] }| + "[No Name]" --No lines in buffer-- | + ]]) + command('vnew') + screen:expect([[ + {4: [No Name] }{3: }{5:2}{3: [No Name] }{2: }{4:X}| + ^ │ | + {0:~ }│{0:~ }| + {0:~ }│{0:~ }| + {0:~ }│{0:~ }| + {0:~ }│{0:~ }| + {1:[No Name] }| + "[No Name]" --No lines in buffer-- | + ]]) + end) + it(":tabmove handles modifiers and addr", function() command('tabnew | tabnew | tabnew') eq(4, funcs.nvim_tabpage_get_number(0)) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 1553de4432..ee8da2932d 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -14,7 +14,6 @@ local pesc = helpers.pesc local rmdir = helpers.rmdir local sleep = helpers.sleep local meths = helpers.meths -local expect_exit = helpers.expect_exit local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec' @@ -42,18 +41,85 @@ describe(':mksession', function() command('split') command('terminal') command('split') - command('mksession '..session_file) + command('mksession ' .. session_file) + command('%bwipeout!') -- Create a new test instance of Nvim. - expect_exit(command, 'qall!') clear() -- Restore session. - command('source '..session_file) + command('source ' .. session_file) eq(funcs.winbufnr(1), funcs.winbufnr(2)) neq(funcs.winbufnr(1), funcs.winbufnr(3)) end) + -- common testing procedure for testing "sessionoptions-=terminal" + local function test_terminal_session_disabled(expected_buf_count) + command('set sessionoptions-=terminal') + + command('mksession ' .. session_file) + + -- Create a new test instance of Nvim. + clear() + + -- Restore session. + command('source ' .. session_file) + + eq(expected_buf_count, #meths.list_bufs()) + end + + it( + 'do not restore :terminal if not set in sessionoptions, terminal in current window #13078', + function() + local tmpfile_base = file_prefix .. '-tmpfile' + command('edit ' .. tmpfile_base) + command('terminal') + + local buf_count = #meths.list_bufs() + eq(2, buf_count) + + eq('terminal', meths.buf_get_option(0, 'buftype')) + + test_terminal_session_disabled(2) + + -- no terminal should be set. As a side effect we end up with a blank buffer + eq('', meths.buf_get_option(meths.list_bufs()[1], 'buftype')) + eq('', meths.buf_get_option(meths.list_bufs()[2], 'buftype')) + end + ) + + it('do not restore :terminal if not set in sessionoptions, terminal hidden #13078', function() + command('terminal') + local terminal_bufnr = meths.get_current_buf() + + local tmpfile_base = file_prefix .. '-tmpfile' + -- make terminal hidden by opening a new file + command('edit ' .. tmpfile_base .. '1') + + local buf_count = #meths.list_bufs() + eq(2, buf_count) + + eq(1, funcs.getbufinfo(terminal_bufnr)[1].hidden) + + test_terminal_session_disabled(1) + + -- no terminal should exist here + neq('', meths.buf_get_name(meths.list_bufs()[1])) + end) + + it('do not restore :terminal if not set in sessionoptions, only buffer #13078', function() + command('terminal') + eq('terminal', meths.buf_get_option(0, 'buftype')) + + local buf_count = #meths.list_bufs() + eq(1, buf_count) + + test_terminal_session_disabled(1) + + -- no terminal should be set + eq('', meths.buf_get_option(0, 'buftype')) + end) + it('restores tab-local working directories', function() local tmpfile_base = file_prefix .. '-tmpfile' local cwd_dir = funcs.getcwd() @@ -103,28 +169,27 @@ describe(':mksession', function() it('restores CWD for :terminal buffers #11288', function() local cwd_dir = funcs.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '') - cwd_dir = cwd_dir:gsub([[\]], '/') -- :mksession always uses unix slashes. - local session_path = cwd_dir..'/'..session_file + cwd_dir = cwd_dir:gsub([[\]], '/') -- :mksession always uses unix slashes. + local session_path = cwd_dir .. '/' .. session_file - command('cd '..tab_dir) + command('cd ' .. tab_dir) command('terminal') - command('cd '..cwd_dir) - command('mksession '..session_path) - command('bdelete!') + command('cd ' .. cwd_dir) + command('mksession ' .. session_path) + command('%bwipeout!') if iswin() then - sleep(100) -- Make sure all child processes have exited. + sleep(100) -- Make sure all child processes have exited. end - expect_exit(command, 'qall!') -- Create a new test instance of Nvim. clear() - command('silent source '..session_path) + command('silent source ' .. session_path) - local expected_cwd = cwd_dir..'/'..tab_dir - matches('^term://'..pesc(expected_cwd)..'//%d+:', funcs.expand('%')) - command('bdelete!') + local expected_cwd = cwd_dir .. '/' .. tab_dir + matches('^term://' .. pesc(expected_cwd) .. '//%d+:', funcs.expand('%')) + command('%bwipeout!') if iswin() then - sleep(100) -- Make sure all child processes have exited. + sleep(100) -- Make sure all child processes have exited. end end) @@ -136,10 +201,10 @@ describe(':mksession', function() local screen local cwd_dir = funcs.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '') - local session_path = cwd_dir..'/'..session_file + local session_path = cwd_dir .. '/' .. session_file screen = Screen.new(50, 6) - screen:attach({rgb=false}) + screen:attach({ rgb = false }) local expected_screen = [[ ^/ | | @@ -155,15 +220,15 @@ describe(':mksession', function() -- Verify that the terminal's working directory is "/". screen:expect(expected_screen) - command('cd '..cwd_dir) - command('mksession '..session_path) - expect_exit(command, 'qall!') + command('cd ' .. cwd_dir) + command('mksession ' .. session_path) + command('%bwipeout!') -- Create a new test instance of Nvim. clear() screen = Screen.new(50, 6) - screen:attach({rgb=false}) - command('silent source '..session_path) + screen:attach({ rgb = false }) + command('silent source ' .. session_path) -- Verify that the terminal's working directory is "/". screen:expect(expected_screen) @@ -181,7 +246,7 @@ describe(':mksession', function() height = 3, row = 0, col = 1, - style = 'minimal' + style = 'minimal', } meths.open_win(buf, false, config) local cmdheight = meths.get_option('cmdheight') diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index 0dc0c8c2db..aa47198f7a 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -67,10 +67,12 @@ local function expect_notification(method, params, ...) local message = read_message() assert_eq(method, message.method, ..., "expect_notification", "method") - assert_eq(params, message.params, - ..., "expect_notification", method, "params") - assert_eq({jsonrpc = "2.0"; method=method, params=params}, message, - ..., "expect_notification", "message") + if params then + assert_eq(params, message.params, + ..., "expect_notification", method, "params") + assert_eq({jsonrpc = "2.0"; method=method, params=params}, message, + ..., "expect_notification", "message") + end end local function expect_request(method, handler, ...) @@ -257,6 +259,26 @@ function tests.basic_check_capabilities() } end +function tests.text_document_save_did_open() + skeleton { + on_init = function() + return { + capabilities = { + textDocumentSync = { + save = true + } + } + } + end; + body = function() + notify('start') + expect_notification('textDocument/didOpen') + expect_notification('textDocument/didSave') + notify('shutdown') + end; + } +end + function tests.text_document_sync_save_bool() skeleton { on_init = function() diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 0c616e73fb..8c5a60657a 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -271,10 +271,22 @@ function module.command(cmd) end --- use for commands which expect nvim to quit -function module.expect_exit(...) - eq("EOF was received from Nvim. Likely the Nvim process crashed.", - module.pcall_err(...)) +-- Use for commands which expect nvim to quit. +-- The first argument can also be a timeout. +function module.expect_exit(fn_or_timeout, ...) + local eof_err_msg = 'EOF was received from Nvim. Likely the Nvim process crashed.' + if type(fn_or_timeout) == 'function' then + eq(eof_err_msg, module.pcall_err(fn_or_timeout, ...)) + else + eq(eof_err_msg, module.pcall_err(function(timeout, fn, ...) + fn(...) + while session:next_message(timeout) do + end + if session.eof_err then + error(session.eof_err[2]) + end + end, fn_or_timeout, ...)) + end end -- Evaluates a VimL expression. diff --git a/test/functional/legacy/075_maparg_spec.lua b/test/functional/legacy/075_maparg_spec.lua deleted file mode 100644 index ad6c190104..0000000000 --- a/test/functional/legacy/075_maparg_spec.lua +++ /dev/null @@ -1,59 +0,0 @@ --- Tests for maparg(). --- Also test utf8 map with a 0x80 byte. - -local helpers = require('test.functional.helpers')(after_each) -local clear, feed = helpers.clear, helpers.feed -local command, expect = helpers.command, helpers.expect -local poke_eventloop = helpers.poke_eventloop - -describe('maparg()', function() - setup(clear) - - it('is working', function() - command('set cpo-=<') - - -- Test maparg() with a string result - command('map foo<C-V> is<F4>foo') - command('vnoremap <script> <buffer> <expr> <silent> bar isbar') - command([[call append('$', maparg('foo<C-V>'))]]) - command([[call append('$', string(maparg('foo<C-V>', '', 0, 1)))]]) - command([[call append('$', string(maparg('bar', '', 0, 1)))]]) - command('map <buffer> <nowait> foo bar') - command([[call append('$', string(maparg('foo', '', 0, 1)))]]) - command('map abc x<char-114>x') - command([[call append('$', maparg('abc'))]]) - command('map abc y<S-char-114>y') - command([[call append('$', maparg('abc'))]]) - feed('Go<esc>:<cr>') - poke_eventloop() - - -- Outside of the range, minimum - command('inoremap <Char-0x1040> a') - command([[execute "normal a\u1040\<Esc>"]]) - - -- Inside of the range, minimum - command('inoremap <Char-0x103f> b') - command([[execute "normal a\u103f\<Esc>"]]) - - -- Inside of the range, maximum - command('inoremap <Char-0xf03f> c') - command([[execute "normal a\uf03f\<Esc>"]]) - - -- Outside of the range, maximum - command('inoremap <Char-0xf040> d') - command([[execute "normal a\uf040\<Esc>"]]) - - -- Remove empty line - command('1d') - - -- Assert buffer contents. - expect([[ - is<F4>foo - {'lnum': 0, 'script': 0, 'silent': 0, 'noremap': 0, 'lhs': 'foo<C-V>', 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': 0, 'rhs': 'is<F4>foo', 'buffer': 0} - {'lnum': 0, 'script': 1, 'silent': 1, 'noremap': 1, 'lhs': 'bar', 'mode': 'v', 'nowait': 0, 'expr': 1, 'sid': 0, 'rhs': 'isbar', 'buffer': 1} - {'lnum': 0, 'script': 0, 'silent': 0, 'noremap': 0, 'lhs': 'foo', 'mode': ' ', 'nowait': 1, 'expr': 0, 'sid': 0, 'rhs': 'bar', 'buffer': 1} - xrx - yRy - abcd]]) - end) -end) diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 8379e426e0..f90da16d7b 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -4,6 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, command, eq = helpers.clear, helpers.command, helpers.eq local eval, exc_exec, neq = helpers.eval, helpers.exc_exec, helpers.neq +local expect_exit = helpers.expect_exit local feed = helpers.feed local pcall_err = helpers.pcall_err @@ -275,6 +276,6 @@ describe('argument list commands', function() 2 more files to edit. Quit anyway? | [Y]es, (N)o: ^ | ]]) - feed('Y') + expect_exit(100, feed, 'Y') end) end) diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua index d8d849271b..cf02636890 100644 --- a/test/functional/legacy/cmdline_spec.lua +++ b/test/functional/legacy/cmdline_spec.lua @@ -3,11 +3,12 @@ local Screen = require('test.functional.ui.screen') local clear = helpers.clear local feed = helpers.feed local feed_command = helpers.feed_command -local source = helpers.source +local exec = helpers.exec describe('cmdline', function() before_each(clear) + -- oldtest: Test_cmdlineclear_tabenter() it('is cleared when switching tabs', function() local screen = Screen.new(30, 10) screen:attach() @@ -91,10 +92,11 @@ describe('cmdline', function() ]]) end) + -- oldtest: Test_verbose_option() it('prints every executed Ex command if verbose >= 16', function() local screen = Screen.new(60, 12) screen:attach() - source([[ + exec([[ command DoSomething echo 'hello' |set ts=4 |let v = '123' |echo v call feedkeys("\r", 't') " for the hit-enter prompt set verbose=20 @@ -115,4 +117,27 @@ describe('cmdline', function() Press ENTER or type command to continue^ | ]]) end) + + -- oldtest: Test_cmdline_redraw_tabline() + it('tabline is redrawn on entering cmdline', function() + local screen = Screen.new(30, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {reverse = true}, -- TabLineFill + }) + screen:attach() + exec([[ + set showtabline=2 + autocmd CmdlineEnter * set tabline=foo + ]]) + feed(':') + screen:expect([[ + {1:foo }| + | + {0:~ }| + {0:~ }| + {0:~ }| + :^ | + ]]) + end) end) diff --git a/test/functional/legacy/ex_mode_spec.lua b/test/functional/legacy/ex_mode_spec.lua index 98f113bbd0..a8f54c6939 100644 --- a/test/functional/legacy/ex_mode_spec.lua +++ b/test/functional/legacy/ex_mode_spec.lua @@ -6,6 +6,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local meths = helpers.meths +local sleep = helpers.sleep before_each(clear) @@ -122,4 +123,54 @@ describe('Ex mode', function() | ]]) end) + + it('pressing Ctrl-C in :append inside a loop in Ex mode does not hang', function() + local screen = Screen.new(60, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, reverse = true}, -- MsgSeparator + [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + }) + screen:attach() + feed('gQ') + feed('for i in range(1)<CR>') + feed('append<CR>') + screen:expect([[ + {0: }| + Entering Ex mode. Type "visual" to go to Normal mode. | + :for i in range(1) | + | + : append | + ^ | + ]]) + feed('<C-C>') + sleep(10) -- Wait for input to be flushed + feed('foo<CR>') + screen:expect([[ + Entering Ex mode. Type "visual" to go to Normal mode. | + :for i in range(1) | + | + : append | + foo | + ^ | + ]]) + feed('.<CR>') + screen:expect([[ + :for i in range(1) | + | + : append | + foo | + . | + : ^ | + ]]) + feed('endfor<CR>') + feed('vi<CR>') + screen:expect([[ + ^foo | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) end) diff --git a/test/functional/legacy/excmd_spec.lua b/test/functional/legacy/excmd_spec.lua index 6b3b265579..65957d85de 100644 --- a/test/functional/legacy/excmd_spec.lua +++ b/test/functional/legacy/excmd_spec.lua @@ -2,10 +2,13 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear = helpers.clear local command = helpers.command +local exec = helpers.exec local exec_lua = helpers.exec_lua +local expect_exit = helpers.expect_exit local feed = helpers.feed +local funcs = helpers.funcs +local iswin = helpers.iswin local meths = helpers.meths -local poke_eventloop = helpers.poke_eventloop local read_file = helpers.read_file local source = helpers.source local eq = helpers.eq @@ -37,152 +40,484 @@ describe('Ex command', function() end) end) -it(':confirm command dialog', function() +describe(':confirm command dialog', function() local screen local function start_new() clear() - screen = Screen.new(60, 20) + screen = Screen.new(75, 20) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, reverse = true}, -- StatusLine, MsgSeparator + [2] = {reverse = true}, -- StatusLineNC + [3] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + }) screen:attach() end - write_file('foo', 'foo1\n') - write_file('bar', 'bar1\n') - - -- Test for saving all the modified buffers - start_new() - command("set nomore") - command("new foo") - command("call setline(1, 'foo2')") - command("new bar") - command("call setline(1, 'bar2')") - command("wincmd b") - feed(':confirm qall\n') - screen:expect([[ - bar2 | - ~ | - ~ | - ~ | - ~ | - ~ | - bar [+] | - foo2 | - ~ | - ~ | - ~ | - ~ | - foo [+] | - | - ~ | - ~ | - | - :confirm qall | - Save changes to "bar"? | - [Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ^ | - ]]) - feed('A') - poke_eventloop() - - eq('foo2\n', read_file('foo')) - eq('bar2\n', read_file('bar')) - - -- Test for discarding all the changes to modified buffers - start_new() - command("set nomore") - command("new foo") - command("call setline(1, 'foo3')") - command("new bar") - command("call setline(1, 'bar3')") - command("wincmd b") - feed(':confirm qall\n') - screen:expect([[ - bar3 | - ~ | - ~ | - ~ | - ~ | - ~ | - bar [+] | - foo3 | - ~ | - ~ | - ~ | - ~ | - foo [+] | - | - ~ | - ~ | - | - :confirm qall | - Save changes to "bar"? | - [Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ^ | - ]]) - feed('D') - poke_eventloop() - - eq('foo2\n', read_file('foo')) - eq('bar2\n', read_file('bar')) - - -- Test for saving and discarding changes to some buffers - start_new() - command("set nomore") - command("new foo") - command("call setline(1, 'foo4')") - command("new bar") - command("call setline(1, 'bar4')") - command("wincmd b") - feed(':confirm qall\n') - screen:expect([[ - bar4 | - ~ | - ~ | - ~ | - ~ | - ~ | - bar [+] | - foo4 | - ~ | - ~ | - ~ | - ~ | - foo [+] | - | - ~ | - ~ | - | - :confirm qall | - Save changes to "bar"? | - [Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ^ | - ]]) - feed('N') - screen:expect([[ - bar4 | - ~ | - ~ | - ~ | - ~ | - ~ | - bar [+] | - foo4 | - ~ | - ~ | - ~ | - ~ | - foo [+] | - | - | - :confirm qall | - Save changes to "bar"? | - | - Save changes to "foo"? | - [Y]es, (N)o, (C)ancel: ^ | - ]]) - feed('Y') - poke_eventloop() - - eq('foo4\n', read_file('foo')) - eq('bar2\n', read_file('bar')) - - os.remove('foo') - os.remove('bar') + -- Test for the :confirm command dialog + -- oldtest: Test_confirm_cmd() + it('works', function() + write_file('Xfoo', 'foo1\n') + write_file('Xbar', 'bar1\n') + + -- Test for saving all the modified buffers + start_new() + exec([[ + set nomore + new Xfoo + call setline(1, 'foo2') + new Xbar + call setline(1, 'bar2') + wincmd b + ]]) + feed(':confirm qall\n') + screen:expect([[ + bar2 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xbar [+] }| + foo2 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xfoo [+] }| + | + {0:~ }| + {0:~ }| + {1: }| + :confirm qall | + {3:Save changes to "Xbar"?} | + {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | + ]]) + expect_exit(100, feed, 'A') + + eq('foo2\n', read_file('Xfoo')) + eq('bar2\n', read_file('Xbar')) + + -- Test for discarding all the changes to modified buffers + start_new() + exec([[ + set nomore + new Xfoo + call setline(1, 'foo3') + new Xbar + call setline(1, 'bar3') + wincmd b + ]]) + feed(':confirm qall\n') + screen:expect([[ + bar3 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xbar [+] }| + foo3 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xfoo [+] }| + | + {0:~ }| + {0:~ }| + {1: }| + :confirm qall | + {3:Save changes to "Xbar"?} | + {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | + ]]) + expect_exit(100, feed, 'D') + + eq('foo2\n', read_file('Xfoo')) + eq('bar2\n', read_file('Xbar')) + + -- Test for saving and discarding changes to some buffers + start_new() + exec([[ + set nomore + new Xfoo + call setline(1, 'foo4') + new Xbar + call setline(1, 'bar4') + wincmd b + ]]) + feed(':confirm qall\n') + screen:expect([[ + bar4 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xbar [+] }| + foo4 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xfoo [+] }| + | + {0:~ }| + {0:~ }| + {1: }| + :confirm qall | + {3:Save changes to "Xbar"?} | + {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | + ]]) + feed('N') + screen:expect([[ + bar4 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xbar [+] }| + foo4 | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:Xfoo [+] }| + | + {1: }| + :confirm qall | + {3:Save changes to "Xbar"?} | + | + {3:Save changes to "Xfoo"?} | + {3:[Y]es, (N)o, (C)ancel: }^ | + ]]) + expect_exit(100, feed, 'Y') + + eq('foo4\n', read_file('Xfoo')) + eq('bar2\n', read_file('Xbar')) + + os.remove('Xfoo') + os.remove('Xbar') + end) + + -- oldtest: Test_confirm_cmd_cancel() + it('can be cancelled', function() + -- Test for closing a window with a modified buffer + start_new() + screen:try_resize(75, 10) + exec([[ + set nohidden nomore + new + call setline(1, 'abc') + ]]) + feed(':confirm close\n') + screen:expect([[ + abc | + {0:~ }| + {0:~ }| + {0:~ }| + {1:[No Name] [+] }| + | + {1: }| + :confirm close | + {3:Save changes to "Untitled"?} | + {3:[Y]es, (N)o, (C)ancel: }^ | + ]]) + feed('C') + screen:expect([[ + ^abc | + {0:~ }| + {0:~ }| + {0:~ }| + {1:[No Name] [+] }| + | + {0:~ }| + {0:~ }| + {2:[No Name] }| + | + ]]) + feed(':confirm close\n') + screen:expect([[ + abc | + {0:~ }| + {0:~ }| + {0:~ }| + {1:[No Name] [+] }| + | + {1: }| + :confirm close | + {3:Save changes to "Untitled"?} | + {3:[Y]es, (N)o, (C)ancel: }^ | + ]]) + feed('N') + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + + -- oldtest: Test_confirm_q_wq() + it('works with :q and :wq', function() + write_file('Xfoo', 'foo') + start_new() + screen:try_resize(75, 8) + exec([[ + set hidden nomore + call setline(1, 'abc') + edit Xfoo + set nofixendofline + ]]) + feed(':confirm q\n') + screen:expect([[ + foo | + {0:~ }| + {0:~ }| + {0:~ }| + {1: }| + :confirm q | + {3:Save changes to "Untitled"?} | + {3:[Y]es, (N)o, (C)ancel: }^ | + ]]) + feed('C') + screen:expect([[ + ^abc | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + + command('edit Xfoo') + feed(':confirm wq\n') + screen:expect([[ + foo | + {0:~ }| + {0:~ }| + {0:~ }| + {1: }| + "Xfoo" [noeol] 1L, 3B written | + {3:Save changes to "Untitled"?} | + {3:[Y]es, (N)o, (C)ancel: }^ | + ]]) + feed('C') + screen:expect([[ + ^abc | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + "Xfoo" [noeol] 1L, 3B written | + ]]) + + os.remove('Xfoo') + end) + + -- oldtest: Test_confirm_write_ro() + it('works when writing a read-only file', function() + write_file('Xconfirm_write_ro', 'foo\n') + start_new() + screen:try_resize(75, 8) + exec([[ + set ruler + set nobackup ff=unix cmdheight=2 + edit Xconfirm_write_ro + norm Abar + ]]) + + -- Try to write with 'ro' option. + feed(':set ro | confirm w\n') + screen:expect([[ + foobar | + {0:~ }| + {0:~ }| + {1: }| + :set ro | confirm w | + {3:'readonly' option is set for "Xconfirm_write_ro".} | + {3:Do you wish to write anyway?} | + {3:(Y)es, [N]o: }^ | + ]]) + feed('N') + screen:expect([[ + fooba^r | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + 1,6 All | + ]]) + eq('foo\n', read_file('Xconfirm_write_ro')) + + feed(':confirm w\n') + screen:expect([[ + foobar | + {0:~ }| + {0:~ }| + {1: }| + :confirm w | + {3:'readonly' option is set for "Xconfirm_write_ro".} | + {3:Do you wish to write anyway?} | + {3:(Y)es, [N]o: }^ | + ]]) + feed('Y') + if iswin() then + screen:expect([[ + foobar | + {0:~ }| + {1: }| + :confirm w | + {3:'readonly' option is set for "Xconfirm_write_ro".} | + {3:Do you wish to write anyway?} | + "Xconfirm_write_ro" [unix] 1L, 7B written | + {3:Press ENTER or type command to continue}^ | + ]]) + else + screen:expect([[ + foobar | + {0:~ }| + {1: }| + :confirm w | + {3:'readonly' option is set for "Xconfirm_write_ro".} | + {3:Do you wish to write anyway?} | + "Xconfirm_write_ro" 1L, 7B written | + {3:Press ENTER or type command to continue}^ | + ]]) + end + eq('foobar\n', read_file('Xconfirm_write_ro')) + feed('<CR>') -- suppress hit-enter prompt + + -- Try to write with read-only file permissions. + funcs.setfperm('Xconfirm_write_ro', 'r--r--r--') + feed(':set noro | silent undo | confirm w\n') + screen:expect([[ + foobar | + {0:~ }| + {1: }| + :set noro | silent undo | confirm w | + {3:File permissions of "Xconfirm_write_ro" are read-only.} | + {3:It may still be possible to write it.} | + {3:Do you wish to try?} | + {3:(Y)es, [N]o: }^ | + ]]) + feed('Y') + if iswin() then + screen:expect([[ + foobar | + {1: }| + :set noro | silent undo | confirm w | + {3:File permissions of "Xconfirm_write_ro" are read-only.} | + {3:It may still be possible to write it.} | + {3:Do you wish to try?} | + "Xconfirm_write_ro" [unix] 1L, 4B written | + {3:Press ENTER or type command to continue}^ | + ]]) + else + screen:expect([[ + foobar | + {1: }| + :set noro | silent undo | confirm w | + {3:File permissions of "Xconfirm_write_ro" are read-only.} | + {3:It may still be possible to write it.} | + {3:Do you wish to try?} | + "Xconfirm_write_ro" 1L, 4B written | + {3:Press ENTER or type command to continue}^ | + ]]) + end + eq('foo\n', read_file('Xconfirm_write_ro')) + feed('<CR>') -- suppress hit-enter prompt + + os.remove('Xconfirm_write_ro') + end) + + -- oldtest: Test_confirm_write_partial_file() + it('works when writing a partial file', function() + write_file('Xwrite_partial', 'a\nb\nc\nd\n') + start_new() + screen:try_resize(75, 8) + exec([[ + set ruler + set nobackup ff=unix cmdheight=2 + edit Xwrite_partial + ]]) + + feed(':confirm 2,3w\n') + screen:expect([[ + a | + b | + c | + d | + {1: }| + :confirm 2,3w | + {3:Write partial file?} | + {3:(Y)es, [N]o: }^ | + ]]) + feed('N') + screen:expect([[ + ^a | + b | + c | + d | + {0:~ }| + {0:~ }| + | + 1,1 All | + ]]) + eq('a\nb\nc\nd\n', read_file('Xwrite_partial')) + os.remove('Xwrite_partial') + + feed(':confirm 2,3w\n') + screen:expect([[ + a | + b | + c | + d | + {1: }| + :confirm 2,3w | + {3:Write partial file?} | + {3:(Y)es, [N]o: }^ | + ]]) + feed('Y') + if iswin() then + screen:expect([[ + a | + b | + c | + {1: }| + :confirm 2,3w | + {3:Write partial file?} | + "Xwrite_partial" [New][unix] 2L, 4B written | + {3:Press ENTER or type command to continue}^ | + ]]) + else + screen:expect([[ + a | + b | + c | + {1: }| + :confirm 2,3w | + {3:Write partial file?} | + "Xwrite_partial" [New] 2L, 4B written | + {3:Press ENTER or type command to continue}^ | + ]]) + end + eq('b\nc\n', read_file('Xwrite_partial')) + + os.remove('Xwrite_partial') + end) end) diff --git a/test/functional/legacy/global_spec.lua b/test/functional/legacy/global_spec.lua new file mode 100644 index 0000000000..9f4528530c --- /dev/null +++ b/test/functional/legacy/global_spec.lua @@ -0,0 +1,51 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local exec = helpers.exec +local feed = helpers.feed +local sleep = helpers.sleep + +before_each(clear) + +describe(':global', function() + -- oldtest: Test_interrupt_global() + it('can be interrupted using Ctrl-C in cmdline mode vim-patch:9.0.0082', function() + local screen = Screen.new(75, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, reverse = true}, -- MsgSeparator + [1] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg + }) + screen:attach() + + exec([[ + set nohlsearch noincsearch + cnoremap ; <Cmd>sleep 10<CR> + call setline(1, repeat(['foo'], 5)) + ]]) + + feed(':g/foo/norm :<C-V>;<CR>') + sleep(10) -- Wait for :sleep to start + feed('<C-C>') + screen:expect([[ + ^foo | + foo | + foo | + foo | + foo | + {1:Interrupted} | + ]]) + + -- Also test in Ex mode + feed('gQg/foo/norm :<C-V>;<CR>') + sleep(10) -- Wait for :sleep to start + feed('<C-C>') + screen:expect([[ + {0: }| + Entering Ex mode. Type "visual" to go to Normal mode. | + :g/foo/norm :; | + | + {1:Interrupted} | + :^ | + ]]) + end) +end) diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua index b296ac909d..51c2406933 100644 --- a/test/functional/legacy/messages_spec.lua +++ b/test/functional/legacy/messages_spec.lua @@ -27,7 +27,16 @@ describe('messages', function() it('works', function() command('call setline(1, range(1, 100))') - feed(':%p#\n') + feed(':%pfoo<C-H><C-H><C-H>#') + screen:expect([[ + 1 | + 2 | + 3 | + 4 | + 5 | + :%p#^ | + ]]) + feed('\n') screen:expect([[ {2: 1 }1 | {2: 2 }2 | @@ -199,11 +208,11 @@ describe('messages', function() -- Up all the way with 'g'. feed('g') screen:expect([[ + :%p# | {2: 1 }1 | {2: 2 }2 | {2: 3 }3 | {2: 4 }4 | - {2: 5 }5 | {1:-- More --}^ | ]]) @@ -241,6 +250,18 @@ describe('messages', function() {1:Press ENTER or type command to continue}^ | ]]) + -- A command line that doesn't print text is appended to scrollback, + -- even if it invokes a nested command line. + feed([[:<C-R>=':'<CR>:<CR>g<lt>]]) + screen:expect([[ + {2: 97 }97 | + {2: 98 }98 | + {2: 99 }99 | + {2:100 }100 | + ::: | + {1:Press ENTER or type command to continue}^ | + ]]) + feed(':%p#\n') screen:expect([[ {2: 1 }1 | diff --git a/test/functional/legacy/syn_attr_spec.lua b/test/functional/legacy/syn_attr_spec.lua new file mode 100644 index 0000000000..06e8427e27 --- /dev/null +++ b/test/functional/legacy/syn_attr_spec.lua @@ -0,0 +1,60 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval + +before_each(clear) + +-- oldtest: Test_missing_attr() +it('synIDattr() works', function() + local bool_attrs = { + 'bold', + 'italic', + 'reverse', + 'standout', + 'underline', + 'undercurl', + 'underdouble', + 'underdotted', + 'underdashed', + 'strikethrough', + 'nocombine', + } + + command('hi Mine cterm=NONE gui=NONE') + eq('Mine', eval([[synIDattr(hlID("Mine"), "name")]])) + for _, mode in ipairs({'cterm', 'gui'}) do + eq('', eval(([[synIDattr("Mine"->hlID(), "bg", '%s')]]):format(mode))) + eq('', eval(([[synIDattr("Mine"->hlID(), "fg", '%s')]]):format(mode))) + eq('', eval(([[synIDattr("Mine"->hlID(), "sp", '%s')]]):format(mode))) + for _, attr in ipairs(bool_attrs) do + eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode))) + eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode))) + eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode))) + end + eq('', eval(([[synIDattr(hlID("Mine"), "inverse", '%s')]]):format(mode))) + end + + for i, attr1 in ipairs(bool_attrs) do + local attr2 = bool_attrs[i - 1] or bool_attrs[#bool_attrs] + + command(('hi Mine cterm=%s gui=%s'):format(attr1, attr2)) + eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr1))) + eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr2))) + eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr1))) + eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr2))) + + command(('hi Mine cterm=%s gui=%s'):format(attr2, attr1)) + eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr1))) + eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr2))) + eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr1))) + eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr2))) + end + + command('hi Mine cterm=reverse gui=inverse') + eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'cterm')]])) + eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'cterm')]])) + eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'gui')]])) + eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'gui')]])) +end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 883e0e373b..2b249b7a69 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2148,6 +2148,13 @@ describe('lua stdlib', function() ]] eq('2', funcs.luaeval "BUF") eq(2, funcs.luaeval "#vim.api.nvim_list_bufs()") + + -- vim.cmd can be indexed with a command name + exec_lua [[ + vim.cmd.let 'g:var = 2' + ]] + + eq(2, funcs.luaeval "vim.g.var") end) it('vim.regex', function() @@ -2493,6 +2500,41 @@ describe('lua stdlib', function() eq(false, pcall_result) end) + + describe('returns -2 when interrupted', function() + before_each(function() + local channel = meths.get_api_info()[1] + meths.set_var('channel', channel) + end) + + it('without callback', function() + exec_lua([[ + function _G.Wait() + vim.rpcnotify(vim.g.channel, 'ready') + local _, interrupted = vim.wait(4000) + vim.rpcnotify(vim.g.channel, 'wait', interrupted) + end + ]]) + feed(':lua _G.Wait()<CR>') + eq({'notification', 'ready', {}}, next_msg(500)) + feed('<C-C>') + eq({'notification', 'wait', {-2}}, next_msg(500)) + end) + + it('with callback', function() + exec_lua([[ + function _G.Wait() + vim.rpcnotify(vim.g.channel, 'ready') + local _, interrupted = vim.wait(4000, function() end) + vim.rpcnotify(vim.g.channel, 'wait', interrupted) + end + ]]) + feed(':lua _G.Wait()<CR>') + eq({'notification', 'ready', {}}, next_msg(500)) + feed('<C-C>') + eq({'notification', 'wait', {-2}}, next_msg(500)) + end) + end) end) it('vim.notify_once', function() @@ -2750,12 +2792,12 @@ describe('vim.keymap', function() it('can make an expr mapping', function() exec_lua [[ - vim.keymap.set('n', 'aa', function() return ':lua SomeValue = 99<cr>' end, {expr = true}) + vim.keymap.set('n', 'aa', function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, {expr = true}) ]] feed('aa') - eq(99, exec_lua[[return SomeValue]]) + eq({'π<M-π>foo<'}, meths.buf_get_lines(0, 0, -1, false)) end) it('can overwrite a mapping', function() diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index c166982052..cd7415de90 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -535,6 +535,46 @@ describe('LSP', function() } end) + it('saveas sends didOpen if filename changed', function() + local expected_handlers = { + { NIL, {}, { method = 'shutdown', client_id = 1 } }, + { NIL, {}, { method = 'start', client_id = 1 } }, + } + local client + test_rpc_server({ + test_name = 'text_document_save_did_open', + on_init = function(c) + client = c + end, + on_exit = function(code, signal) + eq(0, code, 'exit code') + eq(0, signal, 'exit signal') + end, + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') + if ctx.method == 'start' then + local tmpfile_old = helpers.tmpname() + local tmpfile_new = helpers.tmpname() + os.remove(tmpfile_new) + exec_lua( + [=[ + local oldname, newname = ... + BUFFER = vim.api.nvim_get_current_buf() + vim.api.nvim_buf_set_name(BUFFER, oldname) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, true, {"help me"}) + lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID) + vim.api.nvim_buf_call(BUFFER, function() vim.cmd('saveas ' .. newname) end) + ]=], + tmpfile_old, + tmpfile_new + ) + else + client.stop() + end + end, + }) + end) + it('BufWritePost sends didSave including text if server capability is set', function() local expected_handlers = { {NIL, {}, {method="shutdown", client_id=1}}; diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 1cef771f0d..23430a620b 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -5,12 +5,15 @@ local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local poke_eventloop = helpers.poke_eventloop local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source local eq, neq = helpers.eq, helpers.neq +local meths = helpers.meths +local retry = helpers.retry local write_file = helpers.write_file local command = helpers.command local exc_exec = helpers.exc_exec local matches = helpers.matches local exec_lua = helpers.exec_lua local sleep = helpers.sleep +local funcs = helpers.funcs describe(':terminal buffer', function() local screen @@ -298,6 +301,44 @@ describe(':terminal buffer', function() feed_command('put a') -- register a is empty helpers.assert_alive() end) + + it([[can use temporary normal mode <c-\><c-o>]], function() + eq('t', funcs.mode(1)) + feed [[<c-\><c-o>]] + screen:expect{grid=[[ + tty ready | + {2:^ } | + | + | + | + | + {3:-- (terminal) --} | + ]]} + eq('ntT', funcs.mode(1)) + + feed [[:let g:x = 17]] + screen:expect{grid=[[ + tty ready | + {2: } | + | + | + | + | + :let g:x = 17^ | + ]]} + + feed [[<cr>]] + screen:expect{grid=[[ + tty ready | + {1: } | + | + | + | + | + {3:-- TERMINAL --} | + ]]} + eq('t', funcs.mode(1)) + end) end) describe('No heap-buffer-overflow when using', function() @@ -364,3 +405,11 @@ describe('on_lines does not emit out-of-bounds line indexes when', function() eq('', exec_lua([[return _G.cb_error]])) end) end) + +it('terminal truncates number of composing characters to 5', function() + clear() + local chan = meths.open_term(0, {}) + local composing = ('a̳'):sub(2) + meths.chan_send(chan, 'a' .. composing:rep(8)) + retry(nil, nil, function() eq('a' .. composing:rep(5), meths.get_current_line()) end) +end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index dd88379344..eee759d2be 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -144,10 +144,9 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]} - -- TODO(bfredl): messes up the output (just like vim does). feed_data('g') screen:expect{grid=[[ - ) | + :call ManyErr() | {8:Error detected while processing function ManyErr:} | {11:line 2:} | {10:-- More --}{1: } | @@ -156,7 +155,7 @@ describe('TUI', function() screen:try_resize(50,10) screen:expect{grid=[[ - ) | + :call ManyErr() | {8:Error detected while processing function ManyErr:} | {11:line 2:} | {8:FAIL 0} | @@ -436,8 +435,11 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]]) - feed_data(':tab split\r:tabnew\r') - feed_data(':highlight Tabline ctermbg=NONE ctermfg=NONE cterm=underline\r') + child_session:request('nvim_command', [[ + tab split + tabnew + highlight Tabline ctermbg=NONE ctermfg=NONE cterm=underline + ]]) local attrs = screen:get_default_attr_ids() attrs[11] = {underline = true} screen:expect([[ diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index eb0a14da31..db13647cc6 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -96,119 +96,6 @@ local function test_cmdline(linegrid) ]]} end) - describe("redraws statusline on entering", function() - before_each(function() - command('set laststatus=2') - command('set statusline=%{mode()}') - end) - - it('from normal mode', function() - screen:expect{grid=[[ - ^ | - {1:~ }| - {1:~ }| - {3:n }| - | - ]]} - - feed(':') - screen:expect{grid=[[ - ^ | - {1:~ }| - {1:~ }| - {3:c }| - | - ]], cmdline={{ - firstc = ":", - content = {{""}}, - pos = 0, - }}} - end) - - it('from normal mode when : is mapped', function() - command('nnoremap ; :') - - screen:expect{grid=[[ - ^ | - {1:~ }| - {1:~ }| - {3:n }| - | - ]]} - - feed(';') - screen:expect{grid=[[ - ^ | - {1:~ }| - {1:~ }| - {3:c }| - | - ]], cmdline={{ - firstc = ":", - content = {{""}}, - pos = 0, - }}} - end) - - it('but not with scrolled messages', function() - screen:try_resize(35,10) - feed(':echoerr doesnotexist<cr>') - screen:expect{grid=[[ - | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {3: }| - {4:E121: Undefined variable: doesnotex}| - {4:ist} | - {5:Press ENTER or type command to cont}| - {5:inue}^ | - ]]} - feed(':echoerr doesnotexist<cr>') - screen:expect{grid=[[ - | - {1:~ }| - {3: }| - {4:E121: Undefined variable: doesnotex}| - {4:ist} | - {5:Press ENTER or type command to cont}| - {4:E121: Undefined variable: doesnotex}| - {4:ist} | - {5:Press ENTER or type command to cont}| - {5:inue}^ | - ]]} - - feed(':echoerr doesnotexist<cr>') - screen:expect{grid=[[ - {4:E121: Undefined variable: doesnotex}| - {4:ist} | - {5:Press ENTER or type command to cont}| - {4:E121: Undefined variable: doesnotex}| - {4:ist} | - {5:Press ENTER or type command to cont}| - {4:E121: Undefined variable: doesnotex}| - {4:ist} | - {5:Press ENTER or type command to cont}| - {5:inue}^ | - ]]} - - feed('<cr>') - screen:expect{grid=[[ - ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {3:n }| - | - ]]} - end) - end) - it("works with input()", function() feed(':call input("input", "default")<cr>') screen:expect{grid=[[ @@ -883,6 +770,154 @@ describe('cmdline redraw', function() end) end) +describe('statusline is redrawn on entering cmdline', function() + local screen + + before_each(function() + clear() + screen = new_screen() + command('set laststatus=2') + end) + + it('from normal mode', function() + command('set statusline=%{mode()}') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {3:n }| + | + ]]} + + feed(':') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {3:c }| + :^ | + ]]} + end) + + it('from normal mode when : is mapped', function() + command('set statusline=%{mode()}') + command('nnoremap ; :') + + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {3:n }| + | + ]]} + + feed(';') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {3:c }| + :^ | + ]]} + end) + + it('but not with scrolled messages', function() + command('set statusline=%{mode()}') + screen:try_resize(35,10) + feed(':echoerr doesnotexist<cr>') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3: }| + {4:E121: Undefined variable: doesnotex}| + {4:ist} | + {5:Press ENTER or type command to cont}| + {5:inue}^ | + ]]} + feed(':echoerr doesnotexist<cr>') + screen:expect{grid=[[ + | + {1:~ }| + {3: }| + {4:E121: Undefined variable: doesnotex}| + {4:ist} | + {5:Press ENTER or type command to cont}| + {4:E121: Undefined variable: doesnotex}| + {4:ist} | + {5:Press ENTER or type command to cont}| + {5:inue}^ | + ]]} + + feed(':echoerr doesnotexist<cr>') + screen:expect{grid=[[ + {4:E121: Undefined variable: doesnotex}| + {4:ist} | + {5:Press ENTER or type command to cont}| + {4:E121: Undefined variable: doesnotex}| + {4:ist} | + {5:Press ENTER or type command to cont}| + {4:E121: Undefined variable: doesnotex}| + {4:ist} | + {5:Press ENTER or type command to cont}| + {5:inue}^ | + ]]} + + feed('<cr>') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:n }| + | + ]]} + end) + + describe('if custom statusline is set by', function() + before_each(function() + command('set statusline=') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {3:[No Name] }| + | + ]]} + end) + + it('CmdlineEnter autocommand', function() + command('autocmd CmdlineEnter * set statusline=command') + feed(':') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {3:command }| + :^ | + ]]} + end) + + it('ModeChanged autocommand', function() + command('autocmd ModeChanged *:c set statusline=command') + feed(':') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {3:command }| + :^ | + ]]} + end) + end) +end) + describe("cmdline height", function() it("does not crash resized screen #14263", function() clear() @@ -1068,7 +1103,7 @@ describe('cmdheight=0', function() ~ | ~ | ~ | - ~ | + recording @q | ]], showmode={}} feed('q') screen:expect{grid=[[ diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index a1423c98a8..03cd4bfd06 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -215,7 +215,7 @@ describe('ui/cursor', function() m.hl_id = 60 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 65 end + if m.id_lm then m.id_lm = 61 end end -- Assert the new expectation. diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index 5d056cd6c3..36dc5addcd 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -6,6 +6,7 @@ local clear = helpers.clear local command = helpers.command local insert = helpers.insert local write_file = helpers.write_file +local dedent = helpers.dedent local exec = helpers.exec describe('Diff mode screen', function() @@ -985,6 +986,93 @@ int main(int argc, char **argv) ]]) end) end) + + -- oldtest: Test_diff_scroll() + -- This was scrolling for 'cursorbind' but 'scrollbind' is more important + it('scrolling works correctly vim-patch:8.2.5155', function() + screen:try_resize(40, 12) + write_file(fname, dedent([[ + line 1 + line 2 + line 3 + line 4 + + // Common block + // one + // containing + // four lines + + // Common block + // two + // containing + // four lines]]), false) + write_file(fname_2, dedent([[ + line 1 + line 2 + line 3 + line 4 + + Lorem + ipsum + dolor + sit + amet, + consectetur + adipiscing + elit. + Etiam + luctus + lectus + sodales, + dictum + + // Common block + // one + // containing + // four lines + + Vestibulum + tincidunt + aliquet + nulla. + + // Common block + // two + // containing + // four lines]]), false) + reread() + + feed('<C-W><C-W>jjjj') + screen:expect([[ + {1: }line 1 │{1: }line 1 | + {1: }line 2 │{1: }line 2 | + {1: }line 3 │{1: }line 3 | + {1: }line 4 │{1: }line 4 | + {1: } │{1: }^ | + {1: }{2:-----------------}│{1: }{4:Lorem }| + {1: }{2:-----------------}│{1: }{4:ipsum }| + {1: }{2:-----------------}│{1: }{4:dolor }| + {1: }{2:-----------------}│{1: }{4:sit }| + {1: }{2:-----------------}│{1: }{4:amet, }| + {3:<nal-diff-screen-1 }{7:<al-diff-screen-1.2 }| + :e | + ]]) + feed('j') + screen:expect([[ + {1: }line 1 │{1: }line 1 | + {1: }line 2 │{1: }line 2 | + {1: }line 3 │{1: }line 3 | + {1: }line 4 │{1: }line 4 | + {1: } │{1: } | + {1: }{2:-----------------}│{1: }{4:^Lorem }| + {1: }{2:-----------------}│{1: }{4:ipsum }| + {1: }{2:-----------------}│{1: }{4:dolor }| + {1: }{2:-----------------}│{1: }{4:sit }| + {1: }{2:-----------------}│{1: }{4:amet, }| + {3:<nal-diff-screen-1 }{7:<al-diff-screen-1.2 }| + :e | + ]]) + end) end) it('win_update redraws lines properly', function() @@ -1227,6 +1315,7 @@ it('Align the filler lines when changing text in diff mode', function() ]]} end) +-- oldtest: Test_diff_binary() it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', function() clear() local screen = Screen.new(40, 20) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 50247ed214..5967b630f6 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -18,6 +18,7 @@ local run = helpers.run local pcall_err = helpers.pcall_err local tbl_contains = global_helpers.tbl_contains local curbuf, curwin, curtab = helpers.curbuf, helpers.curwin, helpers.curtab +local NIL = helpers.NIL describe('float window', function() before_each(function() @@ -420,6 +421,15 @@ describe('float window', function() eq(winids, eval('winids')) end) + it("no segfault when setting minimal style after clearing local 'fillchars' #19510", function() + local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1} + local float_win = meths.open_win(0, true, float_opts) + meths.win_set_option(float_win, 'fillchars', NIL) + float_opts.style = 'minimal' + meths.win_set_config(float_win, float_opts) + assert_alive() + end) + describe('with only one tabpage,', function() local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1} local old_buf, old_win diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 394f2f5f49..c79fc2989c 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -1818,6 +1818,83 @@ describe("folded lines", function() ]]) end end) + + it('fold text is shown when text has been scrolled to the right #19123', function() + insert(content1) + command('set number nowrap') + command('3,4fold') + feed('gg') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {8: 1 }^This is a | + {8: 2 }valid English | + {8: 3 }{5:+-- 2 lines: sentence composed by·······}| + {8: 5 }in his cave. | + {8: 6 } | + {1:~ }| + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {8: 1 }^This is a | + {8: 2 }valid English | + {8: 3 }{5:+-- 2 lines: sentence composed by·······}| + {8: 5 }in his cave. | + {8: 6 } | + {1:~ }| + {1:~ }| + | + ]]) + end + + feed('zl') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {8: 1 }^his is a | + {8: 2 }alid English | + {8: 3 }{5:+-- 2 lines: sentence composed by·······}| + {8: 5 }n his cave. | + {8: 6 } | + {1:~ }| + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {8: 1 }^his is a | + {8: 2 }alid English | + {8: 3 }{5:+-- 2 lines: sentence composed by·······}| + {8: 5 }n his cave. | + {8: 6 } | + {1:~ }| + {1:~ }| + | + ]]) + end + end) end describe("with ext_multigrid", function() diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 00f126a1f2..e7eaedba2d 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1854,7 +1854,7 @@ aliquip ex ea commodo consequat.]]) feed('k') screen:expect{grid=[[ - {7:0}{8: }{7:)}{8: }| + {7:0}{8: }| {9:1}{10: }| {9:2}{10: }| {9:3}{10: }| @@ -1943,6 +1943,7 @@ aliquip ex ea commodo consequat.]]) -- text is not reflown; existing lines get cut screen:try_resize(30, 12) screen:expect{grid=[[ + :lua error(_G.x) | {2:E5108: Error executing lua [st}| {2:":lua"]:1: Lorem ipsum dolor s}| {2:et, consectetur} | @@ -1953,7 +1954,6 @@ aliquip ex ea commodo consequat.]]) | | | - | {4:-- More --}^ | ]]} @@ -1961,6 +1961,22 @@ aliquip ex ea commodo consequat.]]) -- wrapped at the new screen size. feed('<cr>') screen:expect{grid=[[ + {2:E5108: Error executing lua [st}| + {2:":lua"]:1: Lorem ipsum dolor s}| + {2:et, consectetur} | + {2:adipisicing elit, sed do eiusm}| + {2:mpore} | + {2:incididunt ut labore et dolore}| + {2:a aliqua.} | + {2:Ut enim ad minim veniam, quis }| + {2:nostrud xercitation} | + {2:ullamco laboris nisi ut} | + {2:aliquip ex ea commodo consequa}| + {4:-- More --}^ | + ]]} + + feed('<cr>') + screen:expect{grid=[[ {2:":lua"]:1: Lorem ipsum dolor s}| {2:et, consectetur} | {2:adipisicing elit, sed do eiusm}| diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 69b0d1ecec..e389b7ab89 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1572,22 +1572,22 @@ describe('ui/mouse/input', function() meths.input_mouse('left', 'release', '', 0, 0, 0) end) - it('scroll keys are not translated into multiclicks #6211 #6989', function() + it('scroll keys are not translated into multiclicks and can be mapped #6211 #6989', function() meths.set_var('mouse_up', 0) meths.set_var('mouse_up2', 0) - meths.set_var('mouse_up3', 0) - meths.set_var('mouse_up4', 0) command('nnoremap <ScrollWheelUp> <Cmd>let g:mouse_up += 1<CR>') command('nnoremap <2-ScrollWheelUp> <Cmd>let g:mouse_up2 += 1<CR>') - command('nnoremap <3-ScrollWheelUp> <Cmd>let g:mouse_up3 += 1<CR>') - command('nnoremap <4-ScrollWheelUp> <Cmd>let g:mouse_up4 += 1<CR>') - meths.input_mouse('wheel', 'up', '', 0, 0, 0) - meths.input_mouse('wheel', 'up', '', 0, 0, 0) + feed('<ScrollWheelUp><0,0>') + feed('<ScrollWheelUp><0,0>') meths.input_mouse('wheel', 'up', '', 0, 0, 0) meths.input_mouse('wheel', 'up', '', 0, 0, 0) eq(4, meths.get_var('mouse_up')) eq(0, meths.get_var('mouse_up2')) - eq(0, meths.get_var('mouse_up3')) - eq(0, meths.get_var('mouse_up4')) + end) + + it('feeding <MouseMove> does not use uninitialized memory #19480', function() + feed('<MouseMove>') + helpers.poke_eventloop() + helpers.assert_alive() end) end) diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index c2b0bcdb64..8d7c404637 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -51,7 +51,7 @@ describe('UI receives option updates', function() end) it('on attach #11372', function() - clear() + clear{args_rm={'--headless'}} local evs = {} screen = Screen.new(20,5) -- Override mouse_on/mouse_off handlers. @@ -88,6 +88,13 @@ describe('UI receives option updates', function() eq(expected, screen.options) end) + command("set pumblend=50") + expected.pumblend = 50 + screen:expect(function() + eq(expected, screen.options) + end) + + -- check handling of out-of-bounds value command("set pumblend=-1") expected.pumblend = 0 screen:expect(function() diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index 1e1066d48a..69a2d2f4ed 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command local feed = helpers.feed @@ -20,11 +21,9 @@ describe('statusline clicks', function() command('set laststatus=2 mousemodel=extend') exec([=[ function! MyClickFunc(minwid, clicks, button, mods) - let mods = trim(a:mods) - if mods ==# '' - let g:testvar = printf("%d %d %s", a:minwid, a:clicks, a:button) - else - let g:testvar = printf("%d %d %s %s", a:minwid, a:clicks, a:button, mods) + let g:testvar = printf("%d %d %s", a:minwid, a:clicks, a:button) + if a:mods !=# ' ' + let g:testvar ..= '(' .. a:mods .. ')' endif endfunction ]=]) @@ -34,8 +33,20 @@ describe('statusline clicks', function() meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T') meths.input_mouse('left', 'press', '', 0, 6, 17) eq('0 1 l', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 2 l', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 3 l', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 4 l', eval("g:testvar")) meths.input_mouse('right', 'press', '', 0, 6, 17) eq('0 1 r', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 17) + eq('0 2 r', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 17) + eq('0 3 r', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 17) + eq('0 4 r', eval("g:testvar")) end) it('works for winbar', function() @@ -98,12 +109,60 @@ describe('statusline clicks', function() eq('0 2 r', eval("g:testvar")) end) - it("click works with modifiers #18994", function() + it("works with modifiers #18994", function() meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T') - meths.input_mouse('right', 'press', 's', 0, 6, 17) - eq('0 1 r s', eval("g:testvar")) - meths.input_mouse('left', 'press', 's', 0, 6, 17) - eq('0 1 l s', eval("g:testvar")) + -- Note: alternate between left and right mouse buttons to avoid triggering multiclicks + meths.input_mouse('left', 'press', 'S', 0, 6, 17) + eq('0 1 l(s )', eval("g:testvar")) + meths.input_mouse('right', 'press', 'S', 0, 6, 17) + eq('0 1 r(s )', eval("g:testvar")) + meths.input_mouse('left', 'press', 'A', 0, 6, 17) + eq('0 1 l( a )', eval("g:testvar")) + meths.input_mouse('right', 'press', 'A', 0, 6, 17) + eq('0 1 r( a )', eval("g:testvar")) + meths.input_mouse('left', 'press', 'AS', 0, 6, 17) + eq('0 1 l(s a )', eval("g:testvar")) + meths.input_mouse('right', 'press', 'AS', 0, 6, 17) + eq('0 1 r(s a )', eval("g:testvar")) + meths.input_mouse('left', 'press', 'T', 0, 6, 17) + eq('0 1 l( m)', eval("g:testvar")) + meths.input_mouse('right', 'press', 'T', 0, 6, 17) + eq('0 1 r( m)', eval("g:testvar")) + meths.input_mouse('left', 'press', 'TS', 0, 6, 17) + eq('0 1 l(s m)', eval("g:testvar")) + meths.input_mouse('right', 'press', 'TS', 0, 6, 17) + eq('0 1 r(s m)', eval("g:testvar")) + meths.input_mouse('left', 'press', 'C', 0, 6, 17) + eq('0 1 l( c )', eval("g:testvar")) + -- <C-RightMouse> is for tag jump + end) + + it("works for global statusline with vertical splits #19186", function() + command('set laststatus=3') + meths.set_option('statusline', '%0@MyClickFunc@Clicky stuff%T %= %0@MyClickFunc@Clicky stuff%T') + command('vsplit') + screen:expect([[ + ^ │ | + ~ │~ | + ~ │~ | + ~ │~ | + ~ │~ | + ~ │~ | + Clicky stuff Clicky stuff| + | + ]]) + + -- clickable area on the right + meths.input_mouse('left', 'press', '', 0, 6, 35) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 35) + eq('0 1 r', eval("g:testvar")) + + -- clickable area on the left + meths.input_mouse('left', 'press', '', 0, 6, 5) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 5) + eq('0 1 r', eval("g:testvar")) end) end) @@ -340,3 +399,11 @@ describe('global statusline', function() eq(1, meths.get_option('cmdheight')) end) end) + +it('statusline does not crash if it has Arabic characters #19447', function() + clear() + meths.set_option('statusline', 'غً') + meths.set_option('laststatus', 2) + command('redraw!') + assert_alive() +end) diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua index 92a6ab2e84..60fa10da87 100644 --- a/test/functional/ui/winbar_spec.lua +++ b/test/functional/ui/winbar_spec.lua @@ -578,3 +578,48 @@ describe('winbar', function() eq('Vim(set):E36: Not enough room', pcall_err(command, 'set winbar=test')) end) end) + +it('local winbar works with tabs', function() + clear() + local screen = Screen.new(60, 13) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true}, + [2] = {reverse = true}, + [3] = {bold = true, foreground = Screen.colors.Blue}, + [4] = {underline = true, background = Screen.colors.LightGray} + }) + meths.set_option_value('winbar', 'foo', { scope = 'local', win = 0 }) + command('tabnew') + screen:expect([[ + {4: [No Name] }{1: [No Name] }{2: }{4:X}| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]) + command('tabnext') + screen:expect{grid=[[ + {1: [No Name] }{4: [No Name] }{2: }{4:X}| + {1:foo }| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]} +end) diff --git a/test/functional/vimscript/buf_functions_spec.lua b/test/functional/vimscript/buf_functions_spec.lua index e957e5f5af..b521620320 100644 --- a/test/functional/vimscript/buf_functions_spec.lua +++ b/test/functional/vimscript/buf_functions_spec.lua @@ -303,4 +303,8 @@ describe('setbufvar() function', function() pcall_err(funcs.setbufvar, 1, 'changedtick', true)) eq(2, funcs.getbufvar(1, 'changedtick')) end) + it('throws error when setting a string option to a boolean value vim-patch:9.0.0090', function() + eq('Vim:E928: String required', + pcall_err(funcs.setbufvar, '', '&errorformat', true)) + end) end) diff --git a/test/functional/vimscript/let_spec.lua b/test/functional/vimscript/let_spec.lua index ca1b5e8907..86905199a8 100644 --- a/test/functional/vimscript/let_spec.lua +++ b/test/functional/vimscript/let_spec.lua @@ -7,6 +7,7 @@ local eval = helpers.eval local meths = helpers.meths local exec = helpers.exec local exec_capture = helpers.exec_capture +local expect_exit = helpers.expect_exit local source = helpers.source local testprg = helpers.testprg @@ -28,7 +29,7 @@ describe(':let', function() it(":unlet self-referencing node in a List graph #6070", function() -- :unlet-ing a self-referencing List must not allow GC on indirectly -- referenced in-scope Lists. Before #6070 this caused use-after-free. - source([=[ + expect_exit(100, source, [=[ let [l1, l2] = [[], []] echo 'l1:' . id(l1) echo 'l2:' . id(l2) diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua index 275c72d212..aa64006de0 100644 --- a/test/functional/vimscript/map_functions_spec.lua +++ b/test/functional/vimscript/map_functions_spec.lua @@ -3,7 +3,10 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval +local expect = helpers.expect +local feed = helpers.feed local funcs = helpers.funcs +local meths = helpers.meths local nvim = helpers.nvim local source = helpers.source local command = helpers.command @@ -13,6 +16,7 @@ describe('maparg()', function() local foo_bar_map_table = { lhs='foo', + lhsraw='foo', script=0, silent=0, rhs='bar', @@ -141,6 +145,7 @@ describe('maparg()', function() local function acmap(lhs, rhs) return { lhs = ac(lhs), + lhsraw = ac(lhs), rhs = ac(rhs), buffer = 0, @@ -161,3 +166,32 @@ describe('maparg()', function() eq(acmap('e`', 'f`'), funcs.maparg(ac('e`'), 'n', 0, 1)) end) end) + +describe('mapset()', function() + before_each(clear) + + it('can restore mapping description from the dict returned by maparg()', function() + meths.set_keymap('n', 'lhs', 'rhs', {desc = 'map description'}) + eq('\nn lhs rhs\n map description', + helpers.exec_capture("nmap lhs")) + local mapargs = funcs.maparg('lhs', 'n', false, true) + meths.del_keymap('n', 'lhs') + eq('\nNo mapping found', helpers.exec_capture("nmap lhs")) + funcs.mapset('n', false, mapargs) + eq('\nn lhs rhs\n map description', + helpers.exec_capture("nmap lhs")) + end) + + it('can restore "replace_keycodes" from the dict returned by maparg()', function() + meths.set_keymap('i', 'foo', [['<l' .. 't>']], {expr = true, replace_keycodes = true}) + feed('Afoo') + expect('<') + local mapargs = funcs.maparg('foo', 'i', false, true) + meths.set_keymap('i', 'foo', [['<l' .. 't>']], {expr = true}) + feed('foo') + expect('<<lt>') + funcs.mapset('i', false, mapargs) + feed('foo') + expect('<<lt><') + end) +end) diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index c915556c57..a778e2f435 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -630,7 +630,7 @@ end) describe('shell :!', function() before_each(clear) - it(':{range}! with powershell filter/redirect #16271', function() + it(':{range}! with powershell filter/redirect #16271 #19250', function() local screen = Screen.new(500, 8) screen:attach() local found = helpers.set_shell_powershell(true) @@ -639,18 +639,25 @@ describe('shell :!', function() 1 4 2]]) - feed(':4verbose %!sort<cr>') - screen:expect{ - any=[[Executing command: .?Start%-Process sort %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]] - } + if iswin() then + feed(':4verbose %!sort /R<cr>') + screen:expect{ + any=[[Executing command: .?Start%-Process sort %-ArgumentList "/R" %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]] + } + else + feed(':4verbose %!sort -r<cr>') + screen:expect{ + any=[[Executing command: .?Start%-Process sort %-ArgumentList "%-r" %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]] + } + end feed('<CR>') if found then -- Not using fake powershell, so we can test the result. expect([[ - 1 - 2 + 4 3 - 4]]) + 2 + 1]]) end end) end) |