aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.github/scripts/build_universal_macos.sh12
-rw-r--r--.github/workflows/release.yml8
-rw-r--r--.github/workflows/test.yml2
-rw-r--r--CMakeLists.txt7
-rw-r--r--cmake.deps/CMakeLists.txt3
-rw-r--r--cmake.deps/cmake/BuildLuarocks.cmake58
-rw-r--r--runtime/autoload/health/provider.vim755
-rw-r--r--runtime/autoload/tar.vim76
-rw-r--r--runtime/bugreport.vim87
-rw-r--r--runtime/doc/api.txt4
-rw-r--r--runtime/doc/builtin.txt1
-rw-r--r--runtime/doc/deprecated.txt2
-rw-r--r--runtime/doc/eval.txt48
-rw-r--r--runtime/doc/index.txt1
-rw-r--r--runtime/doc/lua.txt64
-rw-r--r--runtime/doc/news.txt21
-rw-r--r--runtime/doc/options.txt21
-rw-r--r--runtime/doc/pi_health.txt2
-rw-r--r--runtime/doc/pi_tar.txt17
-rw-r--r--runtime/doc/usr_05.txt27
-rw-r--r--runtime/doc/usr_09.txt38
-rw-r--r--runtime/doc/vim_diff.txt5
-rw-r--r--runtime/ftplugin/cs.lua1
-rw-r--r--runtime/lua/editorconfig.lua2
-rw-r--r--runtime/lua/man.lua1
-rw-r--r--runtime/lua/nvim/health.lua2
-rw-r--r--runtime/lua/provider/health.lua916
-rw-r--r--runtime/lua/vim/_editor.lua22
-rw-r--r--runtime/lua/vim/diagnostic.lua2
-rw-r--r--runtime/lua/vim/highlight.lua19
-rw-r--r--runtime/lua/vim/loader.lua22
-rw-r--r--runtime/lua/vim/lsp.lua5
-rw-r--r--runtime/lua/vim/lsp/_snippet.lua4
-rw-r--r--runtime/lua/vim/lsp/buf.lua1
-rw-r--r--runtime/lua/vim/lsp/codelens.lua2
-rw-r--r--runtime/lua/vim/lsp/handlers.lua1
-rw-r--r--runtime/lua/vim/lsp/log.lua1
-rw-r--r--runtime/lua/vim/lsp/protocol.lua1
-rw-r--r--runtime/lua/vim/lsp/rpc.lua1
-rw-r--r--runtime/lua/vim/lsp/util.lua3
-rw-r--r--runtime/lua/vim/shared.lua93
-rw-r--r--runtime/lua/vim/treesitter/query.lua2
-rw-r--r--runtime/lua/vim/uri.lua1
-rw-r--r--runtime/mswin.vim5
-rw-r--r--runtime/pack/dist/opt/vimball/autoload/vimball.vim775
-rw-r--r--runtime/pack/dist/opt/vimball/doc/vimball.txt273
-rw-r--r--runtime/pack/dist/opt/vimball/plugin/vimballPlugin.vim43
-rw-r--r--runtime/plugin/health.vim1
-rw-r--r--runtime/plugin/tarPlugin.vim1
-rw-r--r--scripts/gen_help_html.lua8
-rw-r--r--scripts/lintcommit.lua30
-rwxr-xr-xscripts/vim-patch.sh2
-rw-r--r--src/nvim/api/vimscript.c6
-rw-r--r--src/nvim/api/win_config.c12
-rw-r--r--src/nvim/autocmd.c6
-rw-r--r--src/nvim/autocmd.h1
-rw-r--r--src/nvim/cmdexpand.c18
-rw-r--r--src/nvim/debugger.c2
-rw-r--r--src/nvim/digraph.c2
-rw-r--r--src/nvim/drawline.c27
-rw-r--r--src/nvim/eval.c573
-rw-r--r--src/nvim/eval.h21
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/eval/typval.c31
-rw-r--r--src/nvim/eval/userfunc.c72
-rw-r--r--src/nvim/eval/userfunc.h1
-rw-r--r--src/nvim/eval/vars.c102
-rw-r--r--src/nvim/ex_cmds.lua8
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c81
-rw-r--r--src/nvim/ex_eval.c30
-rw-r--r--src/nvim/ex_getln.c3
-rw-r--r--src/nvim/fold.c3
-rw-r--r--src/nvim/highlight_group.c4
-rw-r--r--src/nvim/mapping.c2
-rw-r--r--src/nvim/mark.c2
-rw-r--r--src/nvim/mark.h2
-rw-r--r--src/nvim/normal.c18
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c2
-rw-r--r--src/nvim/os/env.c2
-rw-r--r--src/nvim/path.c4
-rw-r--r--src/nvim/po/fi.po2
-rw-r--r--src/nvim/quickfix.c12
-rw-r--r--src/nvim/regexp.c2
-rw-r--r--src/nvim/shada.c2
-rw-r--r--src/nvim/statusline.c4
-rw-r--r--src/nvim/tag.c9
-rw-r--r--src/nvim/usercmd.c1
-rw-r--r--src/nvim/vim.h1
-rw-r--r--test/functional/api/highlight_spec.lua21
-rw-r--r--test/functional/autocmd/autocmd_oldtest_spec.lua7
-rw-r--r--test/functional/core/startup_spec.lua30
-rw-r--r--test/functional/editor/mark_spec.lua44
-rw-r--r--test/functional/legacy/061_undo_tree_spec.lua17
-rw-r--r--test/functional/legacy/prompt_buffer_spec.lua6
-rw-r--r--test/functional/lua/loader_spec.lua36
-rw-r--r--test/functional/lua/vim_spec.lua36
-rw-r--r--test/functional/plugin/lsp_spec.lua31
-rw-r--r--test/functional/shada/merging_spec.lua2
-rw-r--r--test/functional/ui/float_spec.lua19
-rw-r--r--test/functional/ui/highlight_spec.lua21
-rw-r--r--test/functional/ui/messages_spec.lua9
-rw-r--r--test/functional/ui/statuscolumn_spec.lua36
-rw-r--r--test/functional/ui/winbar_spec.lua35
-rw-r--r--test/functional/vimscript/eval_spec.lua35
-rw-r--r--test/functional/vimscript/system_spec.lua4
-rw-r--r--test/helpers.lua18
-rw-r--r--test/old/testdir/test_alot.vim1
-rw-r--r--test/old/testdir/test_arglist.vim35
-rw-r--r--test/old/testdir/test_behave.vim29
-rw-r--r--test/old/testdir/test_cmdline.vim17
-rw-r--r--test/old/testdir/test_expr.vim32
-rw-r--r--test/old/testdir/test_help_tagjump.vim4
-rw-r--r--test/old/testdir/test_let.vim4
-rw-r--r--test/old/testdir/test_prompt_buffer.vim13
-rw-r--r--test/old/testdir/test_usercommands.vim8
-rw-r--r--test/old/testdir/test_vimscript.vim30
-rw-r--r--test/old/testdir/test_visual.vim6
-rw-r--r--test/unit/eval/helpers.lua3
-rw-r--r--test/unit/formatc.lua2
-rw-r--r--test/unit/helpers.lua314
-rw-r--r--test/unit/preprocess.lua176
-rw-r--r--test/unit/set.lua44
124 files changed, 2701 insertions, 2998 deletions
diff --git a/.github/scripts/build_universal_macos.sh b/.github/scripts/build_universal_macos.sh
index 392eec62ac..4dfe0d0cf8 100755
--- a/.github/scripts/build_universal_macos.sh
+++ b/.github/scripts/build_universal_macos.sh
@@ -23,9 +23,17 @@ echo "Build release"
cd "$GITHUB_WORKSPACE"
MACOSX_DEPLOYMENT_TARGET="$(sw_vers -productVersion | cut -f1 -d.)"
export MACOSX_DEPLOYMENT_TARGET
-cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} -D CMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} -D CMAKE_OSX_ARCHITECTURES=arm64\;x86_64
+cmake -S cmake.deps -B .deps -G Ninja \
+ -D CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} \
+ -D CMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \
+ -D CMAKE_OSX_ARCHITECTURES=arm64\;x86_64 \
+ -D CMAKE_FIND_FRAMEWORK=NEVER
cmake --build .deps
-cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} -D CMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} -D CMAKE_OSX_ARCHITECTURES=arm64\;x86_64
+cmake -B build -G Ninja \
+ -D CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} \
+ -D CMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \
+ -D CMAKE_OSX_ARCHITECTURES=arm64\;x86_64 \
+ -D CMAKE_FIND_FRAMEWORK=NEVER
cmake --build build
cmake --install build --prefix build/release/nvim-macos
cd build
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 219ad0288f..c6d0c39402 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -225,13 +225,15 @@ jobs:
identifier: Neovim.Neovim
release-tag: ${{ github.event.inputs.tag_name || github.ref_name }}
token: ${{ secrets.WINGET_TOKEN }}
+ - name: Fetch nightly build msi from previous job
+ if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
+ uses: actions/download-artifact@v3
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
- name: Get nightly version
+ name: Get version from nightly build msi
id: get-version
run: |
- Invoke-WebRequest https://github.com/neovim/neovim/releases/download/nightly/nvim-win64.msi -OutFile setup.msi
Install-Module -Name 'Carbon.Windows.Installer' -Force
- $VERSION = (Get-CMsi (Resolve-Path .\setup.msi).Path).ProductVersion
+ $VERSION = (Get-CMsi (Resolve-Path .\nvim-win64\nvim-win64.msi).Path).ProductVersion
"version=$VERSION" >> $env:GITHUB_OUTPUT
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
name: Publish nightly
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 86561e468b..8215a79b35 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -143,6 +143,8 @@ jobs:
flags: -D UNSIGNED_CHAR=ON
- cc: clang
runner: macos-12
+ flags: -D CMAKE_FIND_FRAMEWORK=NEVER
+ deps_flags: -D CMAKE_FIND_FRAMEWORK=NEVER
# functionaltest-lua is our dumping ground for non-mainline configurations.
# 1. Check that the tests pass with PUC Lua instead of LuaJIT.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bfc8a2283f..dad3815373 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -114,14 +114,7 @@ set(NVIM_API_LEVEL 11) # Bump this after any API change.
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
set(NVIM_API_PRERELEASE false)
-# Default to -O2 on release builds.
-if(CMAKE_C_FLAGS_RELEASE MATCHES "-O3")
- message(STATUS "Replacing -O3 in CMAKE_C_FLAGS_RELEASE with -O2")
- string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
-endif()
-
# Build-type: RelWithDebInfo
-
# /Og means something different in MSVC
if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -Og -g")
diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index e9314ffe02..5ac34cbf26 100644
--- a/cmake.deps/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -17,7 +17,8 @@ set(DEPS_CMAKE_ARGS
-D CMAKE_C_STANDARD=99
-D CMAKE_GENERATOR=${CMAKE_GENERATOR}
-D CMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- -D CMAKE_POSITION_INDEPENDENT_CODE=ON)
+ -D CMAKE_POSITION_INDEPENDENT_CODE=ON
+ -D CMAKE_FIND_FRAMEWORK=${CMAKE_FIND_FRAMEWORK})
set(DEPS_CMAKE_CACHE_ARGS -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES})
diff --git a/cmake.deps/cmake/BuildLuarocks.cmake b/cmake.deps/cmake/BuildLuarocks.cmake
index 901d1f17da..674a7eaee1 100644
--- a/cmake.deps/cmake/BuildLuarocks.cmake
+++ b/cmake.deps/cmake/BuildLuarocks.cmake
@@ -104,34 +104,29 @@ if(MSVC)
set(PATH PATH=${DEPS_INSTALL_DIR}/luarocks/tools;$ENV{PATH})
endif()
-# mpack
-add_custom_command(OUTPUT ${ROCKS_DIR}/mpack
- COMMAND ${CMAKE_COMMAND} -E env "${PATH}" ${LUAROCKS_BINARY} build mpack 1.0.10-0 ${LUAROCKS_BUILDARGS}
- DEPENDS luarocks)
-add_custom_target(mpack ALL DEPENDS ${ROCKS_DIR}/mpack)
-
-# lpeg
-add_custom_command(OUTPUT ${ROCKS_DIR}/lpeg
- COMMAND ${CMAKE_COMMAND} -E env "${PATH}" ${LUAROCKS_BINARY} build lpeg 1.0.2-1 ${LUAROCKS_BUILDARGS}
- DEPENDS mpack)
-add_custom_target(lpeg ALL DEPENDS ${ROCKS_DIR}/lpeg)
+set(CURRENT_DEP luarocks)
+
+function(Download ROCK VER)
+ if(ARGV2)
+ set(OUTPUT ${ARGV2})
+ else()
+ set(OUTPUT ${ROCKS_DIR}/${ROCK})
+ endif()
+ add_custom_command(OUTPUT ${OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env "${PATH}" ${LUAROCKS_BINARY} build ${ROCK} ${VER} ${LUAROCKS_BUILDARGS}
+ DEPENDS ${CURRENT_DEP})
+ add_custom_target(${ROCK} ALL DEPENDS ${OUTPUT})
+ set(CURRENT_DEP ${ROCK} PARENT_SCOPE)
+endfunction()
+
+Download(mpack 1.0.10-0)
+Download(lpeg 1.0.2-1)
if((NOT USE_BUNDLED_LUAJIT) AND USE_BUNDLED_LUA)
- # luabitop
- add_custom_command(OUTPUT ${ROCKS_DIR}/luabitop
- COMMAND ${CMAKE_COMMAND} -E env "${PATH}" ${LUAROCKS_BINARY} build luabitop 1.0.2-3 ${LUAROCKS_BUILDARGS}
- DEPENDS lpeg)
- add_custom_target(luabitop ALL DEPENDS ${ROCKS_DIR}/luabitop)
+ Download(luabitop 1.0.2-3)
endif()
if(USE_BUNDLED_BUSTED)
- if((NOT USE_BUNDLED_LUAJIT) AND USE_BUNDLED_LUA)
- set(BUSTED_DEPENDS luabitop)
- else()
- set(BUSTED_DEPENDS lpeg)
- endif()
-
- # busted
if(WIN32)
set(BUSTED_EXE "${DEPS_BIN_DIR}/busted.bat")
set(LUACHECK_EXE "${DEPS_BIN_DIR}/luacheck.bat")
@@ -139,22 +134,11 @@ if(USE_BUNDLED_BUSTED)
set(BUSTED_EXE "${DEPS_BIN_DIR}/busted")
set(LUACHECK_EXE "${DEPS_BIN_DIR}/luacheck")
endif()
- add_custom_command(OUTPUT ${BUSTED_EXE}
- COMMAND ${CMAKE_COMMAND} -E env "${PATH}" ${LUAROCKS_BINARY} build busted 2.1.1 ${LUAROCKS_BUILDARGS}
- DEPENDS ${BUSTED_DEPENDS})
- add_custom_target(busted ALL DEPENDS ${BUSTED_EXE})
- # luacheck
- add_custom_command(OUTPUT ${LUACHECK_EXE}
- COMMAND ${CMAKE_COMMAND} -E env "${PATH}" ${LUAROCKS_BINARY} build luacheck 1.1.0-1 ${LUAROCKS_BUILDARGS}
- DEPENDS busted)
- add_custom_target(luacheck ALL DEPENDS ${LUACHECK_EXE})
+ Download(busted 2.1.1 ${BUSTED_EXE})
+ Download(luacheck 1.1.0-1 ${LUACHECK_EXE})
if (USE_BUNDLED_LUA OR NOT USE_BUNDLED_LUAJIT)
- # coxpcall
- add_custom_command(OUTPUT ${ROCKS_DIR}/coxpcall
- COMMAND ${CMAKE_COMMAND} -E env "${PATH}" ${LUAROCKS_BINARY} build coxpcall 1.17.0-1 ${LUAROCKS_BUILDARGS}
- DEPENDS luarocks)
- add_custom_target(coxpcall ALL DEPENDS ${ROCKS_DIR}/coxpcall)
+ Download(coxpcall 1.17.0-1)
endif()
endif()
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
deleted file mode 100644
index 70f525156c..0000000000
--- a/runtime/autoload/health/provider.vim
+++ /dev/null
@@ -1,755 +0,0 @@
-let s:shell_error = 0
-
-function! s:is_bad_response(s) abort
- return a:s =~? '\v(^unable)|(^error)|(^outdated)'
-endfunction
-
-function! s:trim(s) abort
- return substitute(a:s, '^\_s*\|\_s*$', '', 'g')
-endfunction
-
-" Convert '\' to '/'. Collapse '//' and '/./'.
-function! s:normalize_path(s) abort
- return substitute(substitute(a:s, '\', '/', 'g'), '/\./\|/\+', '/', 'g')
-endfunction
-
-" Returns TRUE if `cmd` exits with success, else FALSE.
-function! s:cmd_ok(cmd) abort
- call system(a:cmd)
- return v:shell_error == 0
-endfunction
-
-" Handler for s:system() function.
-function! s:system_handler(jobid, data, event) dict abort
- if a:event ==# 'stderr'
- if self.add_stderr_to_output
- let self.output .= join(a:data, '')
- else
- let self.stderr .= join(a:data, '')
- endif
- elseif a:event ==# 'stdout'
- let self.output .= join(a:data, '')
- elseif a:event ==# 'exit'
- let s:shell_error = a:data
- endif
-endfunction
-
-" Attempts to construct a shell command from an args list.
-" Only for display, to help users debug a failed command.
-function! s:shellify(cmd) abort
- if type(a:cmd) != type([])
- return a:cmd
- endif
- return join(map(copy(a:cmd),
- \'v:val =~# ''\m[^\-.a-zA-Z_/]'' ? shellescape(v:val) : v:val'), ' ')
-endfunction
-
-" Run a system command and timeout after 30 seconds.
-function! s:system(cmd, ...) abort
- let stdin = a:0 ? a:1 : ''
- let ignore_error = a:0 > 2 ? a:3 : 0
- let opts = {
- \ 'add_stderr_to_output': a:0 > 1 ? a:2 : 0,
- \ 'output': '',
- \ 'stderr': '',
- \ 'on_stdout': function('s:system_handler'),
- \ 'on_stderr': function('s:system_handler'),
- \ 'on_exit': function('s:system_handler'),
- \ }
- let jobid = jobstart(a:cmd, opts)
-
- if jobid < 1
- call health#report_error(printf('Command error (job=%d): `%s` (in %s)',
- \ jobid, s:shellify(a:cmd), string(getcwd())))
- let s:shell_error = 1
- return opts.output
- endif
-
- if !empty(stdin)
- call jobsend(jobid, stdin)
- endif
-
- let res = jobwait([jobid], 30000)
- if res[0] == -1
- call health#report_error(printf('Command timed out: %s', s:shellify(a:cmd)))
- call jobstop(jobid)
- elseif s:shell_error != 0 && !ignore_error
- let emsg = printf("Command error (job=%d, exit code %d): `%s` (in %s)",
- \ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd()))
- if !empty(opts.output)
- let emsg .= "\noutput: " . opts.output
- end
- if !empty(opts.stderr)
- let emsg .= "\nstderr: " . opts.stderr
- end
- call health#report_error(emsg)
- endif
-
- return opts.output
-endfunction
-
-function! s:systemlist(cmd, ...) abort
- let stdout = split(s:system(a:cmd, a:0 ? a:1 : ''), "\n")
- if a:0 > 1 && !empty(a:2)
- return filter(stdout, '!empty(v:val)')
- endif
- return stdout
-endfunction
-
-" Fetch the contents of a URL.
-function! s:download(url) abort
- let has_curl = executable('curl')
- if has_curl && system(['curl', '-V']) =~# 'Protocols:.*https'
- let rv = s:system(['curl', '-sL', a:url], '', 1, 1)
- return s:shell_error ? 'curl error with '.a:url.': '.s:shell_error : rv
- elseif executable('python')
- let script = "
- \try:\n
- \ from urllib.request import urlopen\n
- \except ImportError:\n
- \ from urllib2 import urlopen\n
- \\n
- \response = urlopen('".a:url."')\n
- \print(response.read().decode('utf8'))\n
- \"
- let rv = s:system(['python', '-c', script])
- return empty(rv) && s:shell_error
- \ ? 'python urllib.request error: '.s:shell_error
- \ : rv
- endif
- return 'missing `curl` '
- \ .(has_curl ? '(with HTTPS support) ' : '')
- \ .'and `python`, cannot make web request'
-endfunction
-
-" Check for clipboard tools.
-function! s:check_clipboard() abort
- call health#report_start('Clipboard (optional)')
-
- if !empty($TMUX) && executable('tmux') && executable('pbpaste') && !s:cmd_ok('pbpaste')
- let tmux_version = matchstr(system('tmux -V'), '\d\+\.\d\+')
- call health#report_error('pbcopy does not work with tmux version: '.tmux_version,
- \ ['Install tmux 2.6+. https://superuser.com/q/231130',
- \ 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233'])
- endif
-
- let clipboard_tool = provider#clipboard#Executable()
- if exists('g:clipboard') && empty(clipboard_tool)
- call health#report_error(
- \ provider#clipboard#Error(),
- \ ["Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."])
- elseif empty(clipboard_tool)
- call health#report_warn(
- \ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.',
- \ [':help clipboard'])
- else
- call health#report_ok('Clipboard tool found: '. clipboard_tool)
- endif
-endfunction
-
-" Get the latest Nvim Python client (pynvim) version from PyPI.
-function! s:latest_pypi_version() abort
- let pypi_version = 'unable to get pypi response'
- let pypi_response = s:download('https://pypi.python.org/pypi/pynvim/json')
- if !empty(pypi_response)
- try
- let pypi_data = json_decode(pypi_response)
- catch /E474/
- return 'error: '.pypi_response
- endtry
- let pypi_version = get(get(pypi_data, 'info', {}), 'version', 'unable to parse')
- endif
- return pypi_version
-endfunction
-
-" Get version information using the specified interpreter. The interpreter is
-" used directly in case breaking changes were introduced since the last time
-" Nvim's Python client was updated.
-"
-" Returns: [
-" {python executable version},
-" {current nvim version},
-" {current pypi nvim status},
-" {installed version status}
-" ]
-function! s:version_info(python) abort
- let pypi_version = s:latest_pypi_version()
- let python_version = s:trim(s:system([
- \ a:python,
- \ '-c',
- \ 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))',
- \ ]))
-
- if empty(python_version)
- let python_version = 'unable to parse '.a:python.' response'
- endif
-
- let nvim_path = s:trim(s:system([
- \ a:python, '-c',
- \ 'import sys; ' .
- \ 'sys.path = [p for p in sys.path if p != ""]; ' .
- \ 'import neovim; print(neovim.__file__)']))
- if s:shell_error || empty(nvim_path)
- return [python_version, 'unable to load neovim Python module', pypi_version,
- \ nvim_path]
- endif
-
- " Assuming that multiple versions of a package are installed, sort them
- " numerically in descending order.
- function! s:compare(metapath1, metapath2) abort
- let a = matchstr(fnamemodify(a:metapath1, ':p:h:t'), '[0-9.]\+')
- let b = matchstr(fnamemodify(a:metapath2, ':p:h:t'), '[0-9.]\+')
- return a == b ? 0 : a > b ? 1 : -1
- endfunction
-
- " Try to get neovim.VERSION (added in 0.1.11dev).
- let nvim_version = s:system([a:python, '-c',
- \ 'from neovim import VERSION as v; '.
- \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'],
- \ '', 1, 1)
- if empty(nvim_version)
- let nvim_version = 'unable to find pynvim module version'
- let base = fnamemodify(nvim_path, ':h')
- let metas = glob(base.'-*/METADATA', 1, 1)
- \ + glob(base.'-*/PKG-INFO', 1, 1)
- \ + glob(base.'.egg-info/PKG-INFO', 1, 1)
- let metas = sort(metas, 's:compare')
-
- if !empty(metas)
- for meta_line in readfile(metas[0])
- if meta_line =~# '^Version:'
- let nvim_version = matchstr(meta_line, '^Version: \zs\S\+')
- break
- endif
- endfor
- endif
- endif
-
- let nvim_path_base = fnamemodify(nvim_path, ':~:h')
- let version_status = 'unknown; '.nvim_path_base
- if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version)
- if v:lua.vim.version.lt(nvim_version, pypi_version)
- let version_status = 'outdated; from '.nvim_path_base
- else
- let version_status = 'up to date'
- endif
- endif
-
- return [python_version, nvim_version, pypi_version, version_status]
-endfunction
-
-" Check the Python interpreter's usability.
-function! s:check_bin(bin) abort
- if !filereadable(a:bin) && (!has('win32') || !filereadable(a:bin.'.exe'))
- call health#report_error(printf('"%s" was not found.', a:bin))
- return 0
- elseif executable(a:bin) != 1
- call health#report_error(printf('"%s" is not executable.', a:bin))
- return 0
- endif
- return 1
-endfunction
-
-" Check "loaded" var for given a:provider.
-" Returns 1 if the caller should return (skip checks).
-function! s:disabled_via_loaded_var(provider) abort
- let loaded_var = 'g:loaded_'.a:provider.'_provider'
- if exists(loaded_var) && !exists('*provider#'.a:provider.'#Call')
- let v = eval(loaded_var)
- if 0 is v
- call health#report_info('Disabled ('.loaded_var.'='.v.').')
- return 1
- else
- call health#report_info('Disabled ('.loaded_var.'='.v.'). This might be due to some previous error.')
- endif
- endif
- return 0
-endfunction
-
-function! s:check_python() abort
- call health#report_start('Python 3 provider (optional)')
-
- let pyname = 'python3'
- let python_exe = ''
- let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : ''
- let host_prog_var = pyname.'_host_prog'
- let python_multiple = []
-
- if s:disabled_via_loaded_var(pyname)
- return
- endif
-
- let [pyenv, pyenv_root] = s:check_for_pyenv()
-
- if exists('g:'.host_prog_var)
- call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var)))
- endif
-
- let [pyname, pythonx_warnings] = provider#pythonx#Detect(3)
-
- if empty(pyname)
- call health#report_warn('No Python executable found that can `import neovim`. '
- \ . 'Using the first available executable for diagnostics.')
- elseif exists('g:'.host_prog_var)
- let python_exe = pyname
- endif
-
- " No Python executable could `import neovim`, or host_prog_var was used.
- if !empty(pythonx_warnings)
- call health#report_warn(pythonx_warnings, ['See :help provider-python for more information.',
- \ 'You may disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim'])
-
- elseif !empty(pyname) && empty(python_exe)
- if !exists('g:'.host_prog_var)
- call health#report_info(printf('`g:%s` is not set. Searching for '
- \ . '%s in the environment.', host_prog_var, pyname))
- endif
-
- if !empty(pyenv)
- let python_exe = s:trim(s:system([pyenv, 'which', pyname], '', 1))
-
- if empty(python_exe)
- call health#report_warn(printf('pyenv could not find %s.', pyname))
- endif
- endif
-
- if empty(python_exe)
- let python_exe = exepath(pyname)
-
- if exists('$PATH')
- for path in split($PATH, has('win32') ? ';' : ':')
- let path_bin = s:normalize_path(path.'/'.pyname)
- if path_bin != s:normalize_path(python_exe)
- \ && index(python_multiple, path_bin) == -1
- \ && executable(path_bin)
- call add(python_multiple, path_bin)
- endif
- endfor
-
- if len(python_multiple)
- " This is worth noting since the user may install something
- " that changes $PATH, like homebrew.
- call health#report_info(printf('Multiple %s executables found. '
- \ . 'Set `g:%s` to avoid surprises.', pyname, host_prog_var))
- endif
-
- if python_exe =~# '\<shims\>'
- call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_exe), [
- \ '`pyenv` is not in $PATH, your pyenv installation is broken. '
- \ .'Set `g:'.host_prog_var.'` to avoid surprises.',
- \ ])
- endif
- endif
- endif
- endif
-
- if !empty(python_exe) && !exists('g:'.host_prog_var)
- if empty(venv) && !empty(pyenv)
- \ && !empty(pyenv_root) && resolve(python_exe) !~# '^'.pyenv_root.'/'
- call health#report_warn('pyenv is not set up optimally.', [
- \ printf('Create a virtualenv specifically '
- \ . 'for Nvim using pyenv, and set `g:%s`. This will avoid '
- \ . 'the need to install the pynvim module in each '
- \ . 'version/virtualenv.', host_prog_var)
- \ ])
- elseif !empty(venv)
- if !empty(pyenv_root)
- let venv_root = pyenv_root
- else
- let venv_root = fnamemodify(venv, ':h')
- endif
-
- if resolve(python_exe) !~# '^'.venv_root.'/'
- call health#report_warn('Your virtualenv is not set up optimally.', [
- \ printf('Create a virtualenv specifically '
- \ . 'for Nvim and use `g:%s`. This will avoid '
- \ . 'the need to install the pynvim module in each '
- \ . 'virtualenv.', host_prog_var)
- \ ])
- endif
- endif
- endif
-
- if empty(python_exe) && !empty(pyname)
- " An error message should have already printed.
- call health#report_error(printf('`%s` was not found.', pyname))
- elseif !empty(python_exe) && !s:check_bin(python_exe)
- let python_exe = ''
- endif
-
- " Diagnostic output
- call health#report_info('Executable: ' . (empty(python_exe) ? 'Not found' : python_exe))
- if len(python_multiple)
- for path_bin in python_multiple
- call health#report_info('Other python executable: ' . path_bin)
- endfor
- endif
-
- if empty(python_exe)
- " No Python executable can import 'neovim'. Check if any Python executable
- " can import 'pynvim'. If so, that Python failed to import 'neovim' as
- " well, which is most probably due to a failed pip upgrade:
- " https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
- let [pynvim_exe, errors] = provider#pythonx#DetectByModule('pynvim', 3)
- if !empty(pynvim_exe)
- call health#report_error(
- \ 'Detected pip upgrade failure: Python executable can import "pynvim" but '
- \ . 'not "neovim": '. pynvim_exe,
- \ "Use that Python version to reinstall \"pynvim\" and optionally \"neovim\".\n"
- \ . pynvim_exe ." -m pip uninstall pynvim neovim\n"
- \ . pynvim_exe ." -m pip install pynvim\n"
- \ . pynvim_exe ." -m pip install neovim # only if needed by third-party software")
- endif
- else
- let [majorpyversion, current, latest, status] = s:version_info(python_exe)
-
- if 3 != str2nr(majorpyversion)
- call health#report_warn('Unexpected Python version.' .
- \ ' This could lead to confusing error messages.')
- endif
-
- call health#report_info('Python version: ' . majorpyversion)
-
- if s:is_bad_response(status)
- call health#report_info(printf('pynvim version: %s (%s)', current, status))
- else
- call health#report_info(printf('pynvim version: %s', current))
- endif
-
- if s:is_bad_response(current)
- call health#report_error(
- \ "pynvim is not installed.\nError: ".current,
- \ ['Run in shell: '. python_exe .' -m pip install pynvim'])
- endif
-
- if s:is_bad_response(latest)
- call health#report_warn('Could not contact PyPI to get latest version.')
- call health#report_error('HTTP request failed: '.latest)
- elseif s:is_bad_response(status)
- call health#report_warn(printf('Latest pynvim is NOT installed: %s', latest))
- elseif !s:is_bad_response(current)
- call health#report_ok(printf('Latest pynvim is installed.'))
- endif
- endif
-endfunction
-
-" Check if pyenv is available and a valid pyenv root can be found, then return
-" their respective paths. If either of those is invalid, return two empty
-" strings, effectively ignoring pyenv.
-function! s:check_for_pyenv() abort
- let pyenv_path = resolve(exepath('pyenv'))
-
- if empty(pyenv_path)
- return ['', '']
- endif
-
- call health#report_info('pyenv: Path: '. pyenv_path)
-
- let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : ''
-
- if empty(pyenv_root)
- let pyenv_root = s:trim(s:system([pyenv_path, 'root']))
- call health#report_info('pyenv: $PYENV_ROOT is not set. Infer from `pyenv root`.')
- endif
-
- if !isdirectory(pyenv_root)
- call health#report_warn(
- \ printf('pyenv: Root does not exist: %s. '
- \ . 'Ignoring pyenv for all following checks.', pyenv_root))
- return ['', '']
- endif
-
- call health#report_info('pyenv: Root: '.pyenv_root)
-
- return [pyenv_path, pyenv_root]
-endfunction
-
-" Resolves Python executable path by invoking and checking `sys.executable`.
-function! s:python_exepath(invocation) abort
- return s:normalize_path(system(fnameescape(a:invocation)
- \ . ' -c "import sys; sys.stdout.write(sys.executable)"'))
-endfunction
-
-" Checks that $VIRTUAL_ENV Python executables are found at front of $PATH in
-" Nvim and subshells.
-function! s:check_virtualenv() abort
- call health#report_start('Python virtualenv')
- if !exists('$VIRTUAL_ENV')
- call health#report_ok('no $VIRTUAL_ENV')
- return
- endif
- let errors = []
- " Keep hints as dict keys in order to discard duplicates.
- let hints = {}
- " The virtualenv should contain some Python executables, and those
- " executables should be first both on Nvim's $PATH and the $PATH of
- " subshells launched from Nvim.
- let bin_dir = has('win32') ? '/Scripts' : '/bin'
- let venv_bins = glob($VIRTUAL_ENV . bin_dir . '/python*', v:true, v:true)
- " XXX: Remove irrelevant executables found in bin/.
- let venv_bins = filter(venv_bins, 'v:val !~# "python-config"')
- if len(venv_bins)
- for venv_bin in venv_bins
- let venv_bin = s:normalize_path(venv_bin)
- let py_bin_basename = fnamemodify(venv_bin, ':t')
- let nvim_py_bin = s:python_exepath(exepath(py_bin_basename))
- let subshell_py_bin = s:python_exepath(py_bin_basename)
- if venv_bin !=# nvim_py_bin
- call add(errors, '$PATH yields this '.py_bin_basename.' executable: '.nvim_py_bin)
- let hint = '$PATH ambiguities arise if the virtualenv is not '
- \.'properly activated prior to launching Nvim. Close Nvim, activate the virtualenv, '
- \.'check that invoking Python from the command line launches the correct one, '
- \.'then relaunch Nvim.'
- let hints[hint] = v:true
- endif
- if venv_bin !=# subshell_py_bin
- call add(errors, '$PATH in subshells yields this '
- \.py_bin_basename . ' executable: '.subshell_py_bin)
- let hint = '$PATH ambiguities in subshells typically are '
- \.'caused by your shell config overriding the $PATH previously set by the '
- \.'virtualenv. Either prevent them from doing so, or use this workaround: '
- \.'https://vi.stackexchange.com/a/34996'
- let hints[hint] = v:true
- endif
- endfor
- else
- call add(errors, 'no Python executables found in the virtualenv '.bin_dir.' directory.')
- endif
-
- let msg = '$VIRTUAL_ENV is set to: '.$VIRTUAL_ENV
- if len(errors)
- if len(venv_bins)
- let msg .= "\nAnd its ".bin_dir.' directory contains: '
- \.join(map(venv_bins, "fnamemodify(v:val, ':t')"), ', ')
- endif
- let conj = "\nBut "
- for error in errors
- let msg .= conj.error
- let conj = "\nAnd "
- endfor
- let msg .= "\nSo invoking Python may lead to unexpected results."
- call health#report_warn(msg, keys(hints))
- else
- call health#report_info(msg)
- call health#report_info('Python version: '
- \.system('python -c "import platform, sys; sys.stdout.write(platform.python_version())"'))
- call health#report_ok('$VIRTUAL_ENV provides :!python.')
- endif
-endfunction
-
-function! s:check_ruby() abort
- call health#report_start('Ruby provider (optional)')
-
- if s:disabled_via_loaded_var('ruby')
- return
- endif
-
- if !executable('ruby') || !executable('gem')
- call health#report_warn(
- \ '`ruby` and `gem` must be in $PATH.',
- \ ['Install Ruby and verify that `ruby` and `gem` commands work.'])
- return
- endif
- call health#report_info('Ruby: '. s:system(['ruby', '-v']))
-
- let [host, err] = provider#ruby#Detect()
- if empty(host)
- call health#report_warn('`neovim-ruby-host` not found.',
- \ ['Run `gem install neovim` to ensure the neovim RubyGem is installed.',
- \ 'Run `gem environment` to ensure the gem bin directory is in $PATH.',
- \ 'If you are using rvm/rbenv/chruby, try "rehashing".',
- \ 'See :help g:ruby_host_prog for non-standard gem installations.',
- \ 'You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim'])
- return
- endif
- call health#report_info('Host: '. host)
-
- let latest_gem_cmd = has('win32') ? 'cmd /c gem list -ra "^^neovim$"' : 'gem list -ra ^neovim$'
- let latest_gem = s:system(split(latest_gem_cmd))
- if s:shell_error || empty(latest_gem)
- call health#report_error('Failed to run: '. latest_gem_cmd,
- \ ["Make sure you're connected to the internet.",
- \ 'Are you behind a firewall or proxy?'])
- return
- endif
- let latest_gem = get(split(latest_gem, 'neovim (\|, \|)$' ), 0, 'not found')
-
- let current_gem_cmd = [host, '--version']
- let current_gem = s:system(current_gem_cmd)
- if s:shell_error
- call health#report_error('Failed to run: '. join(current_gem_cmd),
- \ ['Report this issue with the output of: ', join(current_gem_cmd)])
- return
- endif
-
- if v:lua.vim.version.lt(current_gem, latest_gem)
- call health#report_warn(
- \ printf('Gem "neovim" is out-of-date. Installed: %s, latest: %s',
- \ current_gem, latest_gem),
- \ ['Run in shell: gem update neovim'])
- else
- call health#report_ok('Latest "neovim" gem is installed: '. current_gem)
- endif
-endfunction
-
-function! s:check_node() abort
- call health#report_start('Node.js provider (optional)')
-
- if s:disabled_via_loaded_var('node')
- return
- endif
-
- if !executable('node') || (!executable('npm') && !executable('yarn') && !executable('pnpm'))
- call health#report_warn(
- \ '`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, '')
- call health#report_info('Node.js: '. node_v)
- if s:shell_error || v:lua.vim.version.lt(node_v[1:], '6.0.0')
- call health#report_warn('Nvim node.js host does not support Node '.node_v)
- " Skip further checks, they are nonsense if nodejs is too old.
- return
- endif
- if !provider#node#can_inspect()
- call health#report_warn('node.js on this system does not support --inspect-brk so $NVIM_NODE_HOST_DEBUG is ignored.')
- endif
-
- let [host, err] = provider#node#Detect()
- if empty(host)
- 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 = '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'
- let latest_npm = s:system(split(latest_npm_cmd))
- if s:shell_error || empty(latest_npm)
- call health#report_error('Failed to run: '. latest_npm_cmd,
- \ ["Make sure you're connected to the internet.",
- \ 'Are you behind a firewall or proxy?'])
- return
- endif
- try
- let pkg_data = json_decode(latest_npm)
- catch /E474/
- return 'error: '.latest_npm
- endtry
- let latest_npm = get(get(pkg_data, 'dist-tags', {}), 'latest', 'unable to parse')
-
- let current_npm_cmd = ['node', host, '--version']
- let current_npm = s:system(current_npm_cmd)
- if s:shell_error
- call health#report_error('Failed to run: '. join(current_npm_cmd),
- \ ['Report this issue with the output of: ', join(current_npm_cmd)])
- return
- endif
-
- if latest_npm !=# 'unable to parse' && v:lua.vim.version.lt(current_npm, latest_npm)
- call health#report_warn(
- \ 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 pnpm): pnpm install -g neovim'])
- else
- call health#report_ok('Latest "neovim" npm/yarn/pnpm package is installed: '. current_npm)
- endif
-endfunction
-
-function! s:check_perl() abort
- call health#report_start('Perl provider (optional)')
-
- if s:disabled_via_loaded_var('perl')
- return
- endif
-
- let [perl_exec, perl_warnings] = provider#perl#Detect()
- if empty(perl_exec)
- if !empty(perl_warnings)
- call health#report_warn(perl_warnings, ['See :help provider-perl for more information.',
- \ 'You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim'])
- else
- call health#report_warn('No usable perl executable found')
- endif
- return
- endif
-
- call health#report_info('perl executable: '. perl_exec)
-
- " we cannot use cpanm that is on the path, as it may not be for the perl
- " set with g:perl_host_prog
- call s:system([perl_exec, '-W', '-MApp::cpanminus', '-e', ''])
- if s:shell_error
- return [perl_exec, '"App::cpanminus" module is not installed']
- endif
-
- let latest_cpan_cmd = [perl_exec,
- \ '-MApp::cpanminus::fatscript', '-e',
- \ 'my $app = App::cpanminus::script->new;
- \ $app->parse_options ("--info", "-q", "Neovim::Ext");
- \ exit $app->doit']
-
- let latest_cpan = s:system(latest_cpan_cmd)
- if s:shell_error || empty(latest_cpan)
- call health#report_error('Failed to run: '. join(latest_cpan_cmd, " "),
- \ ["Make sure you're connected to the internet.",
- \ 'Are you behind a firewall or proxy?'])
- return
- elseif latest_cpan[0] ==# '!'
- let cpanm_errs = split(latest_cpan, '!')
- if cpanm_errs[0] =~# "Can't write to "
- call health#report_warn(cpanm_errs[0], cpanm_errs[1:-2])
- " Last line is the package info
- let latest_cpan = cpanm_errs[-1]
- else
- call health#report_error('Unknown warning from command: ' . latest_cpan_cmd, cpanm_errs)
- return
- endif
- endif
- let latest_cpan = matchstr(latest_cpan, '\(\.\?\d\)\+')
- if empty(latest_cpan)
- call health#report_error('Cannot parse version number from cpanm output: ' . latest_cpan)
- return
- endif
-
- let current_cpan_cmd = [perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION']
- let current_cpan = s:system(current_cpan_cmd)
- if s:shell_error
- call health#report_error('Failed to run: '. join(current_cpan_cmd),
- \ ['Report this issue with the output of: ', join(current_cpan_cmd)])
- return
- endif
-
- if v:lua.vim.version.lt(current_cpan, latest_cpan)
- call health#report_warn(
- \ printf('Module "Neovim::Ext" is out-of-date. Installed: %s, latest: %s',
- \ current_cpan, latest_cpan),
- \ ['Run in shell: cpanm -n Neovim::Ext'])
- else
- call health#report_ok('Latest "Neovim::Ext" cpan module is installed: '. current_cpan)
- endif
-endfunction
-
-function! health#provider#check() abort
- call s:check_clipboard()
- call s:check_python()
- call s:check_virtualenv()
- call s:check_ruby()
- call s:check_node()
- call s:check_perl()
-endfunction
diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim
index e495e8262a..d7d1d8399e 100644
--- a/runtime/autoload/tar.vim
+++ b/runtime/autoload/tar.vim
@@ -735,82 +735,6 @@ fun! s:Rmdir(fname)
" call Dret("Rmdir")
endfun
-" ---------------------------------------------------------------------
-" tar#Vimuntar: installs a tarball in the user's .vim / vimfiles directory {{{2
-fun! tar#Vimuntar(...)
-" call Dfunc("tar#Vimuntar() a:0=".a:0." a:1<".(exists("a:1")? a:1 : "-n/a-").">")
- let tarball = expand("%")
-" call Decho("tarball<".tarball.">")
- let tarbase = substitute(tarball,'\..*$','','')
-" call Decho("tarbase<".tarbase.">")
- let tarhome = expand("%:p")
- if has("win32") || has("win95") || has("win64") || has("win16")
- let tarhome= substitute(tarhome,'\\','/','g')
- endif
- let tarhome= substitute(tarhome,'/[^/]*$','','')
-" call Decho("tarhome<".tarhome.">")
- let tartail = expand("%:t")
-" call Decho("tartail<".tartail.">")
- let curdir = getcwd()
-" call Decho("curdir <".curdir.">")
- " set up vimhome
- if a:0 > 0 && a:1 != ""
- let vimhome= a:1
- else
- let vimhome= vimball#VimballHome()
- endif
-" call Decho("vimhome<".vimhome.">")
-
-" call Decho("curdir<".curdir."> vimhome<".vimhome.">")
- if simplify(curdir) != simplify(vimhome)
- " copy (possibly compressed) tarball to .vim/vimfiles
-" call Decho(netrw#WinPath(g:tar_copycmd)." ".shellescape(tartail)." ".shellescape(vimhome))
- call system(netrw#WinPath(g:tar_copycmd)." ".shellescape(tartail)." ".shellescape(vimhome))
-" call Decho("exe cd ".fnameescape(vimhome))
- exe "cd ".fnameescape(vimhome)
- endif
-" call Decho("getcwd<".getcwd().">")
-
- " if necessary, decompress the tarball; then, extract it
- if tartail =~ '\.tgz'
- if executable("gunzip")
- silent exe "!gunzip ".shellescape(tartail)
- elseif executable("gzip")
- silent exe "!gzip -d ".shellescape(tartail)
- else
- echoerr "unable to decompress<".tartail."> on this system"
- if simplify(curdir) != simplify(tarhome)
- " remove decompressed tarball, restore directory
-" call Decho("delete(".tartail.".tar)")
- call delete(tartail.".tar")
-" call Decho("exe cd ".fnameescape(curdir))
- exe "cd ".fnameescape(curdir)
- endif
-" call Dret("tar#Vimuntar")
- return
- endif
- else
- call vimball#Decompress(tartail,0)
- endif
- let extractcmd= netrw#WinPath(g:tar_extractcmd)
-" call Decho("system(".extractcmd." ".shellescape(tarbase.".tar").")")
- call system(extractcmd." ".shellescape(tarbase.".tar"))
-
- " set up help
- if filereadable("doc/".tarbase.".txt")
-" call Decho("exe helptags ".getcwd()."/doc")
- exe "helptags ".getcwd()."/doc"
- endif
-
- if simplify(tarhome) != simplify(vimhome)
- " remove decompressed tarball, restore directory
- call delete(vimhome."/".tarbase.".tar")
- exe "cd ".fnameescape(curdir)
- endif
-
-" call Dret("tar#Vimuntar")
-endfun
-
" =====================================================================
" Modelines And Restoration: {{{1
let &cpo= s:keepcpo
diff --git a/runtime/bugreport.vim b/runtime/bugreport.vim
deleted file mode 100644
index 27761ca011..0000000000
--- a/runtime/bugreport.vim
+++ /dev/null
@@ -1,87 +0,0 @@
-:" Use this script to create the file "bugreport.txt", which contains
-:" information about the environment of a possible bug in Vim.
-:"
-:" Maintainer: Bram Moolenaar <Bram@vim.org>
-:" Last change: 2019 Jan 27
-:"
-:" To use inside Vim:
-:" :so $VIMRUNTIME/bugreport.vim
-:" Or, from the command line:
-:" vim -s $VIMRUNTIME/bugreport.vim
-:"
-:" The "if 1" lines are to avoid error messages when expression evaluation is
-:" not compiled in.
-:"
-:if 1
-: let more_save = &more
-:endif
-:set nomore
-:if has("unix")
-: !echo "uname -a" >bugreport.txt
-: !uname -a >>bugreport.txt
-:endif
-:redir >>bugreport.txt
-:version
-:if 1
-: func <SID>CheckDir(n)
-: if isdirectory(a:n)
-: echo 'directory "' . a:n . '" exists'
-: else
-: echo 'directory "' . a:n . '" does NOT exist'
-: endif
-: endfun
-: func <SID>CheckFile(n)
-: if filereadable(a:n)
-: echo '"' . a:n . '" is readable'
-: else
-: echo '"' . a:n . '" is NOT readable'
-: endif
-: endfun
-: echo "--- Directories and Files ---"
-: echo '$VIM = "' . $VIM . '"'
-: call <SID>CheckDir($VIM)
-: echo '$VIMRUNTIME = "' . $VIMRUNTIME . '"'
-: call <SID>CheckDir($VIMRUNTIME)
-: call <SID>CheckFile(&helpfile)
-: call <SID>CheckFile(fnamemodify(&helpfile, ":h") . "/tags")
-: call <SID>CheckFile($VIMRUNTIME . "/menu.vim")
-: call <SID>CheckFile($VIMRUNTIME . "/filetype.vim")
-: call <SID>CheckFile($VIMRUNTIME . "/syntax/synload.vim")
-: delfun <SID>CheckDir
-: delfun <SID>CheckFile
-: echo "--- Scripts sourced ---"
-: scriptnames
-:endif
-:set all
-:if has("autocmd")
-: au
-:endif
-:if 1
-: echo "--- Normal/Visual mode mappings ---"
-:endif
-:map
-:if 1
-: echo "--- Insert/Command-line mode mappings ---"
-:endif
-:map!
-:if 1
-: echo "--- Abbreviations ---"
-:endif
-:ab
-:if 1
-: echo "--- Highlighting ---"
-:endif
-:highlight
-:if 1
-: echo "--- Variables ---"
-:endif
-:if 1
-: let
-:endif
-:redir END
-:set more&
-:if 1
-: let &more = more_save
-: unlet more_save
-:endif
-:e bugreport.txt
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 09d260e0cd..d63563cc05 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -3068,8 +3068,8 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
In general, values below 100 are recommended, unless
there is a good reason to overshadow builtin elements.
- • style: Configure the appearance of the window. Currently
- only takes one non-empty value:
+ • style: (optional) Configure the appearance of the window.
+ Currently only supports one value:
• "minimal" Nvim will display the window with many UI
options disabled. This is useful when displaying a
temporary float where the text should not be edited.
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 0e04e9035b..3c940ccfa2 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -3003,7 +3003,6 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
arglist file names in argument list
augroup autocmd groups
buffer buffer names
- behave |:behave| suboptions
breakpoint |:breakadd| and |:breakdel| suboptions
cmdline |cmdline-completion| result
color color schemes
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 3735073867..171d285950 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -120,6 +120,8 @@ LSP FUNCTIONS
{buffer = bufnr} instead.
- *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with
{async = true} instead.
+- *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with
+ {async = false} instead.
- *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()|
or |vim.lsp.buf.format()| instead.
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index fe15ba6115..351690f4df 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -93,7 +93,27 @@ non-zero number it means TRUE: >
:" executed
To test for a non-empty string, use empty(): >
:if !empty("foo")
-<
+
+< *falsy* *truthy*
+An expression can be used as a condition, ignoring the type and only using
+whether the value is "sort of true" or "sort of false". Falsy is:
+ the number zero
+ empty string, blob, list or dictionary
+Other values are truthy. Examples:
+ 0 falsy
+ 1 truthy
+ -1 truthy
+ 0.0 falsy
+ 0.1 truthy
+ '' falsy
+ 'x' truthy
+ [] falsy
+ [0] truthy
+ {} falsy
+ #{x: 1} truthy
+ 0z falsy
+ 0z00 truthy
+
*non-zero-arg*
Function arguments often behave slightly different from |TRUE|: If the
argument is present and it evaluates to a non-zero Number, |v:true| or a
@@ -841,9 +861,12 @@ All expressions within one level are parsed from left to right.
------------------------------------------------------------------------------
-expr1 *expr1* *ternary* *E109*
+expr1 *expr1* *ternary* *falsy-operator* *??* *E109*
+
+The ternary operator: expr2 ? expr1 : expr1
+The falsy operator: expr2 ?? expr1
-expr2 ? expr1 : expr1
+Ternary operator ~
The expression before the '?' is evaluated to a number. If it evaluates to
|TRUE|, the result is the value of the expression between the '?' and ':',
@@ -866,6 +889,23 @@ To keep this readable, using |line-continuation| is suggested: >
You should always put a space before the ':', otherwise it can be mistaken for
use in a variable such as "a:1".
+Falsy operator ~
+
+This is also known as the "null coalescing operator", but that's too
+complicated, thus we just call it the falsy operator.
+
+The expression before the '??' is evaluated. If it evaluates to
+|truthy|, this is used as the result. Otherwise the expression after the '??'
+is evaluated and used as the result. This is most useful to have a default
+value for an expression that may result in zero or empty: >
+ echo theList ?? 'list is empty'
+ echo GetName() ?? 'unknown'
+
+These are similar, but not equal: >
+ expr2 ?? expr1
+ expr2 ? expr2 : expr1
+In the second line "expr2" is evaluated twice.
+
------------------------------------------------------------------------------
expr2 and expr3 *expr2* *expr3*
@@ -2499,7 +2539,7 @@ This does NOT work: >
|List| item.
*:let=<<* *:let-heredoc*
- *E990* *E991* *E172* *E221*
+ *E990* *E991* *E172* *E221* *E1145*
:let {var-name} =<< [trim] {endmarker}
text...
text...
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 174683a8c3..fbd3fccec0 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1159,7 +1159,6 @@ tag command action ~
|:badd| :bad[d] add buffer to the buffer list
|:balt| :balt like ":badd" but also set the alternate file
|:bdelete| :bd[elete] remove a buffer from the buffer list
-|:behave| :be[have] set mouse and selection behavior
|:belowright| :bel[owright] make split window appear right or below
|:bfirst| :bf[irst] go to first buffer in the buffer list
|:blast| :bl[ast] go to last buffer in the buffer list
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 6084a625ba..6fdf3775f6 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1516,9 +1516,10 @@ region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()*
Parameters: ~
• {bufnr} (integer) number of buffer
- • {pos1} integer[] (line, column) tuple marking beginning of
- region
- • {pos2} integer[] (line, column) tuple marking end of region
+ • {pos1} integer[]|string start of region as a (line, column)
+ tuple or string accepted by |getpos()|
+ • {pos2} integer[]|string end of region as a (line, column) tuple
+ or string accepted by |getpos()|
• {regtype} (string) type of selection, see |setreg()|
• {inclusive} (boolean) indicating whether column of pos2 is inclusive
@@ -1697,6 +1698,19 @@ is_callable({f}) *vim.is_callable()*
Return: ~
(boolean) `true` if `f` is callable, else `false`
+list_contains({t}, {value}) *vim.list_contains()*
+ Checks if a list-like table (integer keys without gaps) contains `value`.
+
+ Parameters: ~
+ • {t} (table) Table to check (must be list-like, not validated)
+ • {value} any Value to compare
+
+ Return: ~
+ (boolean) `true` if `t` contains `value`
+
+ See also: ~
+ • |vim.tbl_contains()| for checking values in general tables
+
list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()*
Extends a list-like table with the values of another list-like table.
@@ -1796,16 +1810,31 @@ tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()*
Return: ~
(table) o
-tbl_contains({t}, {value}) *vim.tbl_contains()*
- Checks if a list-like (vector) table contains `value`.
+tbl_contains({t}, {value}, {opts}) *vim.tbl_contains()*
+ Checks if a table contains a given value, specified either directly or via
+ a predicate that is checked for each value.
+
+ Example: >lua
+
+ vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
+ return vim.deep_equal(v, { 'b', 'c' })
+ end, { predicate = true })
+ -- true
+<
Parameters: ~
• {t} (table) Table to check
- • {value} any Value to compare
+ • {value} any Value to compare or predicate function reference
+ • {opts} (table|nil) Keyword arguments |kwargs|:
+ • predicate: (boolean) `value` is a function reference to be
+ checked (default false)
Return: ~
(boolean) `true` if `t` contains `value`
+ See also: ~
+ • |vim.list_contains()| for checking values in list-like tables
+
tbl_count({t}) *vim.tbl_count()*
Counts the number of non-nil values in table `t`.
@@ -1899,6 +1928,20 @@ tbl_get({o}, {...}) *vim.tbl_get()*
Return: ~
any Nested value indexed by key (if it exists), else nil
+tbl_isarray({t}) *vim.tbl_isarray()*
+ Tests if a Lua table can be treated as an array (a table indexed by
+ integers).
+
+ Empty table `{}` is assumed to be an array, unless it was created by
+ |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result,
+ for example from |rpcrequest()| or |vim.fn|.
+
+ Parameters: ~
+ • {t} (table)
+
+ Return: ~
+ (boolean) `true` if array-like table, else `false`.
+
tbl_isempty({t}) *vim.tbl_isempty()*
Checks if a table is empty.
@@ -1912,17 +1955,18 @@ tbl_isempty({t}) *vim.tbl_isempty()*
• https://github.com/premake/premake-core/blob/master/src/base/table.lua
tbl_islist({t}) *vim.tbl_islist()*
- Tests if a Lua table can be treated as an array.
+ Tests if a Lua table can be treated as a list (a table indexed by
+ consecutive integers starting from 1).
- Empty table `{}` is assumed to be an array, unless it was created by
+ Empty table `{}` is assumed to be an list, unless it was created by
|vim.empty_dict()| or returned as a dict-like |API| or Vimscript result,
for example from |rpcrequest()| or |vim.fn|.
Parameters: ~
- • {t} (table) Table
+ • {t} (table)
Return: ~
- (boolean) `true` if array-like table, else `false`
+ (boolean) `true` if list-like table, else `false`.
tbl_keys({t}) *vim.tbl_keys()*
Return a list of all keys used in a table. However, the order of the
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 6e2a1b1d3f..6697b3018a 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -15,9 +15,21 @@ BREAKING CHANGES *news-breaking*
The following changes may require adaptations in user config or plugins.
+• |vim.tbl_islist()| now checks whether a table is actually list-like (i.e.,
+ has integer keys without gaps and starting from 1). For the previous
+ behavior (only check for integer keys, allow gaps or not starting with 1),
+ use |vim.tbl_isarray()|.
+
• "#" followed by a digit no longer stands for a function key at the start of
the lhs of a mapping.
+• `:behave` was removed. if you used `:behave mswin`, the following is equivalent: >vim
+
+ set selection=exclusive
+ set selectmode=mouse,key
+ set mousemodel=popup
+ set keymodel=startsel,stopsel
+
==============================================================================
ADDED FEATURES *news-added*
@@ -30,14 +42,19 @@ CHANGED FEATURES *news-changed*
The following changes to existing APIs or features add new behavior.
-• ...
+• |vim.tbl_contains()| now works for general tables and allows specifying a
+ predicate function that is checked for each value. (Use |vim.list_contains()|
+ for checking list-like tables (integer keys without gaps) for literal values.)
+
+• |vim.region()| can use a string accepted by |getpos()| as position.
==============================================================================
REMOVED FEATURES *news-removed*
The following deprecated functions or APIs were removed.
-• ...
+• Vimball support is removed.
+ - :Vimuntar command removed.
==============================================================================
DEPRECATIONS *news-deprecations*
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index d22a78700f..b4cad51990 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -3589,7 +3589,6 @@ A jump table for the options with a short description can be found at |Q_op|.
stopsel Using a not-shifted special key stops selection.
Special keys in this context are the cursor keys, <End>, <Home>,
<PageUp> and <PageDown>.
- The 'keymodel' option is set by the |:behave| command.
*'keywordprg'* *'kp'*
'keywordprg' 'kp' string (default ":Man", Windows: ":help")
@@ -4168,21 +4167,6 @@ A jump table for the options with a short description can be found at |Q_op|.
'mousehide' hide mouse pointer while typing text
'selectmode' whether to start Select mode or Visual mode
- The :behave command provides some "profiles" for mouse behavior.
- *:behave* *:be*
- :be[have] {model} Set behavior for mouse and selection. Valid
- arguments are:
- mswin MS-Windows behavior
- xterm Xterm behavior
-
- Using ":behave" changes these options:
- option mswin xterm ~
- 'selectmode' "mouse,key" ""
- 'mousemodel' "popup" "extend"
- 'keymodel' "startsel,stopsel" ""
- 'selection' "exclusive" "inclusive"
-
-
*'mousefocus'* *'mousef'* *'nomousefocus'* *'nomousef'*
'mousefocus' 'mousef' boolean (default off)
global
@@ -4250,8 +4234,6 @@ A jump table for the options with a short description can be found at |Q_op|.
"g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click)
"g<RightMouse>" is "<C-RightMouse> ("CTRL-T")
- The 'mousemodel' option is set by the |:behave| command.
-
*'mousemoveevent'* *'mousemev'* *'nomousemoveevent'* *'nomousemev'*
'mousemoveevent' 'mousemev' boolean (default off)
global
@@ -5013,8 +4995,6 @@ A jump table for the options with a short description can be found at |Q_op|.
backwards, you cannot include the last character of a line, when
starting in Normal mode and 'virtualedit' empty.
- The 'selection' option is set by the |:behave| command.
-
*'selectmode'* *'slm'*
'selectmode' 'slm' string (default "")
global
@@ -5025,7 +5005,6 @@ A jump table for the options with a short description can be found at |Q_op|.
key when using shifted special keys
cmd when using "v", "V" or CTRL-V
See |Select-mode|.
- The 'selectmode' option is set by the |:behave| command.
*'sessionoptions'* *'ssop'*
'sessionoptions' 'ssop' string (default: "blank,buffers,curdir,folds,
diff --git a/runtime/doc/pi_health.txt b/runtime/doc/pi_health.txt
index 2ae93b098a..a0e06a7d37 100644
--- a/runtime/doc/pi_health.txt
+++ b/runtime/doc/pi_health.txt
@@ -21,7 +21,7 @@ Plugin authors are encouraged to write new healthchecks. |health-dev|
==============================================================================
Commands *health-commands*
- *:che* *:checkhealth* *:CheckHealth*
+ *:che* *:checkhealth*
:che[ckhealth] Run all healthchecks.
*E5009*
Nvim depends on |$VIMRUNTIME|, 'runtimepath' and 'packpath' to
diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt
index 2230b82dec..e664b98086 100644
--- a/runtime/doc/pi_tar.txt
+++ b/runtime/doc/pi_tar.txt
@@ -33,23 +33,6 @@ Copyright 2005-2017: *tar-copyright*
also write to the file. Currently, one may not make a new file in
tar archives via the plugin.
- *:Vimuntar*
- VIMUNTAR~
-
- :Vimuntar [vimhome]
-
- This command copies, if necessary, the tarball to the .vim or vimfiles
- directory using the first writable directory in the |'runtimepath'|
- when no [vimhome] is specified. Otherwise, the [vimhome] argument
- allows the user to specify that directory, instead.
-
- The copy is done using the command in *g:tar_copycmd* , which is >
- cp for cygwin, unix, macunix
- copy for windows (32, 95, 64, 16)
-< The extraction is done with the command specified with
- *g:tar_extractcmd* , which by default is >
- "tar -xf"
-<
*:TarDiff*
DIFFERENCING SUPPORT~
diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt
index 00b4f9eed4..8f7e393c02 100644
--- a/runtime/doc/usr_05.txt
+++ b/runtime/doc/usr_05.txt
@@ -190,26 +190,21 @@ The ":map" command (with no arguments) lists your current mappings. At
least the ones for Normal mode. More about mappings in section |40.1|.
==============================================================================
-*05.4* Adding a package *add-package* *vimball-install*
+*05.4* Adding a package *add-package*
-A package is a set of files that you can add to Vim. There are two kinds of
-packages: optional and automatically loaded on startup.
-
-The Vim distribution comes with a few packages that you can optionally use.
-For example, the vimball plugin. This plugin supports creating and using
-vimballs (self-installing Vim plugin archives).
-
-To start using the vimball plugin, add one line to your vimrc file: >
- packadd vimball
+You may use |:packadd| to enable packages on demand. This is useful for plugins
+you want to enable only sometimes. To enable `example_package`, use the
+following command: >
+ packadd example_package
-That's all! You can also type the command to try it out. Now you can find
-help about this plugin: >
- :help vimball
+That's all! Now you can find help about this plugin: >
+ :help example_package
This works, because when `:packadd` loaded the plugin it also added the
-package directory in 'runtimepath', so that the help file can be found. The
-tags for vimball's help are already created. If you need to generate the help
-tags for a package, see the `:helptags` command.
+package directory in 'runtimepath', so that the help file can be found.
+
+A package is a set of files that you can add to Vim. There are two kinds of
+packages: optional and automatically loaded on startup.
You can find packages on the Internet in various places. It usually comes as
an archive or as a repository. For an archive you can follow these steps:
diff --git a/runtime/doc/usr_09.txt b/runtime/doc/usr_09.txt
index 8084d13b5d..ea16010dc2 100644
--- a/runtime/doc/usr_09.txt
+++ b/runtime/doc/usr_09.txt
@@ -124,41 +124,13 @@ This adds the 'l' flag to 'guioptions'.
Standards are wonderful. In Microsoft Windows, you can use the mouse to
select text in a standard manner. The X Window system also has a standard
system for using the mouse. Unfortunately, these two standards are not the
-same.
- Fortunately, you can customize Vim. You can make the behavior of the mouse
-work like an X Window system mouse or a Microsoft Windows mouse. The following
-command makes the mouse behave like an X Window mouse: >
+same. Fortunately, you can customize Vim.
- :behave xterm
-
-The following command makes the mouse work like a Microsoft Windows mouse: >
-
- :behave mswin
-
-The default behavior of the mouse on Unix systems is xterm. The default
-behavior on Windows systems is selected during the installation process. For
-details about what the two behaviors are, see |:behave|. Here follows a
-summary.
-
-
-XTERM MOUSE BEHAVIOR
-
-Left mouse click position the cursor
-Left mouse drag select text in Visual mode
-Middle mouse click paste text from the clipboard
-Right mouse click extend the selected text until the mouse
- pointer
-
-
-MSWIN MOUSE BEHAVIOR
-
-Left mouse click position the cursor
-Left mouse drag select text in Select mode (see |09.4|)
-Left mouse click, with Shift extend the selected text until the mouse
- pointer
-Middle mouse click paste text from the clipboard
-Right mouse click display a pop-up menu
+The following commands makes the mouse work more like a Microsoft Windows mouse: >
+ set selection=exclusive
+ set selectmode=mouse,key
+ set keymodel=startsel,stopsel
The mouse can be further tuned. Check out these options if you want to change
the way how the mouse works:
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 7228473676..58f6d6f6f9 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -556,6 +556,7 @@ Aliases:
vimdiff (alias for "nvim -d" |diff-mode|)
Commands:
+ :behave
:fixdel
:hardcopy
:helpfind
@@ -573,6 +574,7 @@ Commands:
:cscope
:lcscope
:scscope
+ :Vimuntar
Compile-time features:
Emacs tags support
@@ -764,5 +766,8 @@ Hardcopy:
`:hardcopy` was removed. Instead, use `:TOhtml` and print the resulting HTML
using a web browser or some other HTML viewer.
+Bundled plugins:
+ vimball *vimball*
+
==============================================================================
vim:tw=78:ts=8:sw=2:et:ft=help:norl:
diff --git a/runtime/ftplugin/cs.lua b/runtime/ftplugin/cs.lua
new file mode 100644
index 0000000000..b4e68148f5
--- /dev/null
+++ b/runtime/ftplugin/cs.lua
@@ -0,0 +1 @@
+vim.bo.commentstring = '/*%s*/'
diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua
index 5b09126788..5188c13284 100644
--- a/runtime/lua/editorconfig.lua
+++ b/runtime/lua/editorconfig.lua
@@ -26,7 +26,7 @@ end
function M.properties.charset(bufnr, val)
assert(
- vim.tbl_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val),
+ vim.list_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val),
'charset must be one of "utf-8", "utf-8-bom", "latin1", "utf-16be", or "utf-16le"'
)
if val == 'utf-8' or val == 'utf-8-bom' then
diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua
index cca9434e9c..1158d80941 100644
--- a/runtime/lua/man.lua
+++ b/runtime/lua/man.lua
@@ -64,6 +64,7 @@ local function system(cmd_, silent, env)
local cmd_str = table.concat(cmd, ' ')
man_error(string.format('command error: %s', cmd_str))
end
+ return ''
end
vim.wait(30000, function()
diff --git a/runtime/lua/nvim/health.lua b/runtime/lua/nvim/health.lua
index b6d84404ec..6f544e9407 100644
--- a/runtime/lua/nvim/health.lua
+++ b/runtime/lua/nvim/health.lua
@@ -325,7 +325,7 @@ local function check_tmux()
-- check for RGB capabilities
local info = vim.fn.system({ 'tmux', 'display-message', '-p', '#{client_termfeatures}' })
info = vim.split(vim.trim(info), ',', { trimempty = true })
- if not vim.tbl_contains(info, 'RGB') then
+ if not vim.list_contains(info, 'RGB') then
local has_rgb = false
if #info == 0 then
-- client_termfeatures may not be supported; fallback to checking show-messages
diff --git a/runtime/lua/provider/health.lua b/runtime/lua/provider/health.lua
new file mode 100644
index 0000000000..a5fe14732c
--- /dev/null
+++ b/runtime/lua/provider/health.lua
@@ -0,0 +1,916 @@
+local M = {}
+
+local start = vim.health.report_start
+local ok = vim.health.report_ok
+local info = vim.health.report_info
+local warn = vim.health.report_warn
+local error = vim.health.report_error
+local iswin = vim.loop.os_uname().sysname == 'Windows_NT'
+
+local shell_error_code = 0
+local function shell_error()
+ return shell_error_code ~= 0
+end
+
+-- Returns true if `cmd` exits with success, else false.
+local function cmd_ok(cmd)
+ vim.fn.system(cmd)
+ return vim.v.shell_error == 0
+end
+
+local function executable(exe)
+ return vim.fn.executable(exe) == 1
+end
+
+local function is_blank(s)
+ return s:find('^%s*$') ~= nil
+end
+
+local function isdir(path)
+ if not path then
+ return false
+ end
+ local stat = vim.loop.fs_stat(path)
+ if not stat then
+ return false
+ end
+ return stat.type == 'directory'
+end
+
+local function isfile(path)
+ if not path then
+ return false
+ end
+ local stat = vim.loop.fs_stat(path)
+ if not stat then
+ return false
+ end
+ return stat.type == 'file'
+end
+
+-- Handler for s:system() function.
+local function system_handler(self, _, data, event)
+ if event == 'stderr' then
+ if self.add_stderr_to_output then
+ self.output = self.output .. vim.fn.join(data, '')
+ else
+ self.stderr = self.stderr .. vim.fn.join(data, '')
+ end
+ elseif event == 'stdout' then
+ self.output = self.output .. vim.fn.join(data, '')
+ elseif event == 'exit' then
+ shell_error_code = data
+ end
+end
+
+-- Attempts to construct a shell command from an args list.
+-- Only for display, to help users debug a failed command.
+local function shellify(cmd)
+ if type(cmd) ~= 'table' then
+ return cmd
+ end
+ return vim.fn.join(
+ vim.fn.map(vim.fn.copy(cmd), [[v:val =~# ''\m[^\-.a-zA-Z_/]'' ? shellescape(v:val) : v:val]]),
+ ' '
+ )
+end
+
+-- Run a system command and timeout after 30 seconds.
+local function system(cmd, ...)
+ local args = { ... }
+ local args_count = vim.tbl_count(args)
+
+ local stdin = (args_count > 0 and args[1] or '')
+ local stderr = (args_count > 1 and args[2] or false)
+ local ignore_error = (args_count > 2 and args[3] or false)
+
+ local opts = {
+ add_stderr_to_output = stderr,
+ output = '',
+ stderr = '',
+ on_stdout = system_handler,
+ on_stderr = system_handler,
+ on_exit = system_handler,
+ }
+ local jobid = vim.fn.jobstart(cmd, opts)
+
+ if jobid < 1 then
+ local message = 'Command error (job='
+ .. jobid
+ .. '): `'
+ .. shellify(cmd)
+ .. '` (in '
+ .. vim.fn.string(vim.fn.getcwd())
+ .. ')'
+
+ error(message)
+ shell_error_code = 1
+ return opts.output
+ end
+
+ if not is_blank(stdin) then
+ vim.cmd([[call jobsend(jobid, stdin)]])
+ end
+
+ local res = vim.fn.jobwait({ jobid }, 30000)
+ if res[1] == -1 then
+ error('Command timed out: ' .. shellify(cmd))
+ vim.cmd([[call jobstop(jobid)]])
+ elseif shell_error() and not ignore_error then
+ local emsg = 'Command error (job='
+ .. jobid
+ .. ', exit code '
+ .. shell_error_code
+ .. '): `'
+ .. shellify(cmd)
+ .. '` (in '
+ .. vim.fn.string(vim.fn.getcwd())
+ .. ')'
+ if not is_blank(opts.output) then
+ emsg = emsg .. '\noutput: ' .. opts.output
+ end
+ if not is_blank(opts.stderr) then
+ emsg = emsg .. '\nstderr: ' .. opts.stderr
+ end
+ error(emsg)
+ end
+
+ -- return opts.output
+ local _ = ...
+ return vim.trim(vim.fn.system(cmd))
+end
+
+local function clipboard()
+ start('Clipboard (optional)')
+
+ if
+ os.getenv('TMUX')
+ and executable('tmux')
+ and executable('pbpaste')
+ and not cmd_ok('pbpaste')
+ then
+ local tmux_version = string.match(vim.fn.system('tmux -V'), '%d+%.%d+')
+ local advice = {
+ 'Install tmux 2.6+. https://superuser.com/q/231130',
+ 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233',
+ }
+ error('pbcopy does not work with tmux version: ' .. tmux_version, advice)
+ end
+
+ local clipboard_tool = vim.fn['provider#clipboard#Executable']()
+ if vim.g.clipboard and is_blank(clipboard_tool) then
+ local error_message = vim.fn['provider#clipboard#Error']()
+ error(
+ error_message,
+ "Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."
+ )
+ elseif is_blank(clipboard_tool) then
+ warn(
+ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.',
+ ':help clipboard'
+ )
+ else
+ ok('Clipboard tool found: ' .. clipboard_tool)
+ end
+end
+
+local function disabled_via_loaded_var(provider)
+ local loaded_var = 'loaded_' .. provider .. '_provider'
+ local v = vim.g[loaded_var]
+ if v == 0 then
+ info('Disabled (' .. loaded_var .. '=' .. v .. ').')
+ return true
+ end
+ return false
+end
+
+-- Check if pyenv is available and a valid pyenv root can be found, then return
+-- their respective paths. If either of those is invalid, return two empty
+-- strings, effectively ignoring pyenv.
+local function check_for_pyenv()
+ local pyenv_path = vim.fn.resolve(vim.fn.exepath('pyenv'))
+
+ if is_blank(pyenv_path) then
+ return { '', '' }
+ end
+
+ info('pyenv: Path: ' .. pyenv_path)
+
+ local pyenv_root = os.getenv('PYENV_ROOT') and vim.fn.resolve('$PYENV_ROOT') or ''
+
+ if is_blank(pyenv_root) then
+ pyenv_root = vim.trim(system({ pyenv_path, 'root' }))
+ info('pyenv: $PYENV_ROOT is not set. Infer from `pyenv root`.')
+ end
+
+ if not isdir(pyenv_root) then
+ local message = 'pyenv: Root does not exist: '
+ .. pyenv_root
+ .. '. Ignoring pyenv for all following checks.'
+ warn(message)
+ return { '', '' }
+ end
+
+ info('pyenv: Root: ' .. pyenv_root)
+
+ return { pyenv_path, pyenv_root }
+end
+
+-- Check the Python interpreter's usability.
+local function check_bin(bin)
+ if not isfile(bin) and (not iswin or not isfile(bin .. '.exe')) then
+ error('"' .. bin .. '" was not found.')
+ return false
+ elseif not executable(bin) then
+ error('"' .. bin .. '" is not executable.')
+ return false
+ end
+ return true
+end
+
+-- Fetch the contents of a URL.
+local function download(url)
+ local has_curl = executable('curl')
+ if has_curl and vim.fn.system({ 'curl', '-V' }):find('Protocols:.*https') then
+ local rv = system({ 'curl', '-sL', url }, '', 1, 1)
+ if shell_error() then
+ return 'curl error with ' .. url .. ': ' .. shell_error_code
+ else
+ return rv
+ end
+ elseif executable('python') then
+ local script = "try:\n\
+ from urllib.request import urlopen\n\
+ except ImportError:\n\
+ from urllib2 import urlopen\n\
+ response = urlopen('" .. url .. "')\n\
+ print(response.read().decode('utf8'))\n"
+ local rv = system({ 'python', '-c', script })
+ if is_blank(rv) and shell_error() then
+ return 'python urllib.request error: ' .. shell_error_code
+ else
+ return rv
+ end
+ end
+
+ local message = 'missing `curl` '
+
+ if has_curl then
+ message = message .. '(with HTTPS support) '
+ end
+ message = message .. 'and `python`, cannot make web request'
+
+ return message
+end
+
+-- Get the latest Nvim Python client (pynvim) version from PyPI.
+local function latest_pypi_version()
+ local pypi_version = 'unable to get pypi response'
+ local pypi_response = download('https://pypi.python.org/pypi/pynvim/json')
+ if not is_blank(pypi_response) then
+ local pcall_ok, output = pcall(vim.fn.json_decode, pypi_response)
+ local pypi_data
+ if pcall_ok then
+ pypi_data = output
+ else
+ return 'error: ' .. pypi_response
+ end
+
+ local pypi_element = pypi_data['info'] or {}
+ pypi_version = pypi_element['version'] or 'unable to parse'
+ end
+ return pypi_version
+end
+
+local function is_bad_response(s)
+ local lower = s:lower()
+ return vim.startswith(lower, 'unable')
+ or vim.startswith(lower, 'error')
+ or vim.startswith(lower, 'outdated')
+end
+
+-- Get version information using the specified interpreter. The interpreter is
+-- used directly in case breaking changes were introduced since the last time
+-- Nvim's Python client was updated.
+--
+-- Returns: {
+-- {python executable version},
+-- {current nvim version},
+-- {current pypi nvim status},
+-- {installed version status}
+-- }
+local function version_info(python)
+ local pypi_version = latest_pypi_version()
+
+ local python_version = vim.trim(system({
+ python,
+ '-c',
+ 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))',
+ }))
+
+ if is_blank(python_version) then
+ python_version = 'unable to parse ' .. python .. ' response'
+ end
+
+ local nvim_path = vim.trim(system({
+ python,
+ '-c',
+ 'import sys; sys.path = [p for p in sys.path if p != ""]; import neovim; print(neovim.__file__)',
+ }))
+ if shell_error() or is_blank(nvim_path) then
+ return { python_version, 'unable to load neovim Python module', pypi_version, nvim_path }
+ end
+
+ -- Assuming that multiple versions of a package are installed, sort them
+ -- numerically in descending order.
+ local function compare(metapath1, metapath2)
+ local a = vim.fn.matchstr(vim.fn.fnamemodify(metapath1, ':p:h:t'), [[[0-9.]\+]])
+ local b = vim.fn.matchstr(vim.fn.fnamemodify(metapath2, ':p:h:t'), [[[0-9.]\+]])
+ if a == b then
+ return 0
+ elseif a > b then
+ return 1
+ else
+ return -1
+ end
+ end
+
+ -- Try to get neovim.VERSION (added in 0.1.11dev).
+ local nvim_version = system({
+ python,
+ '-c',
+ 'from neovim import VERSION as v; print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))',
+ }, '', 1, 1)
+ if is_blank(nvim_version) then
+ nvim_version = 'unable to find pynvim module version'
+ local base = vim.fs.basename(nvim_path, ':h')
+ local metas = vim.fn.glob(base .. '-*/METADATA', 1, 1)
+ vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', 1, 1))
+ vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', 1, 1))
+ metas = table.sort(metas, compare)
+
+ if metas and next(metas) ~= nil then
+ for _, meta_line in ipairs(vim.fn.readfile(metas[1])) do
+ if vim.startswith(meta_line, 'Version:') then
+ nvim_version = vim.fn.matchstr(meta_line, [[^Version: \zs\S\+]])
+ break
+ end
+ end
+ end
+ end
+
+ local nvim_path_base = vim.fn.fnamemodify(nvim_path, [[:~:h]])
+ local version_status = 'unknown; ' .. nvim_path_base
+ if is_bad_response(nvim_version) and is_bad_response(pypi_version) then
+ if vim.version.lt(nvim_version, pypi_version) then
+ version_status = 'outdated; from ' .. nvim_path_base
+ else
+ version_status = 'up to date'
+ end
+ end
+
+ return { python_version, nvim_version, pypi_version, version_status }
+end
+
+-- Resolves Python executable path by invoking and checking `sys.executable`.
+local function python_exepath(invocation)
+ return vim.fs.normalize(
+ system(vim.fn.fnameescape(invocation) .. ' -c "import sys; sys.stdout.write(sys.executable)"')
+ )
+end
+
+local function python()
+ start('Python 3 provider (optional)')
+
+ local pyname = 'python3'
+ local python_exe = ''
+ local virtual_env = os.getenv('VIRTUAL_ENV')
+ local venv = virtual_env and vim.fn.resolve(virtual_env) or ''
+ local host_prog_var = pyname .. '_host_prog'
+ local python_multiple = {}
+
+ if disabled_via_loaded_var(pyname) then
+ return
+ end
+
+ local pyenv_table = check_for_pyenv()
+ local pyenv = pyenv_table[1]
+ local pyenv_root = pyenv_table[2]
+
+ if vim.g[host_prog_var] then
+ local message = 'Using: g:' .. host_prog_var .. ' = "' .. vim.g[host_prog_var] .. '"'
+ info(message)
+ end
+
+ local python_table = vim.fn['provider#pythonx#Detect'](3)
+ pyname = python_table[1]
+ local pythonx_warnings = python_table[2]
+
+ if is_blank(pyname) then
+ warn(
+ 'No Python executable found that can `import neovim`. '
+ .. 'Using the first available executable for diagnostics.'
+ )
+ elseif vim.g[host_prog_var] then
+ python_exe = pyname
+ end
+
+ -- No Python executable could `import neovim`, or host_prog_var was used.
+ if not is_blank(pythonx_warnings) then
+ warn(pythonx_warnings, {
+ 'See :help provider-python for more information.',
+ 'You may disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim',
+ })
+ elseif not is_blank(pyname) and is_blank(python_exe) then
+ if not vim.g[host_prog_var] then
+ local message = '`g:'
+ .. host_prog_var
+ .. '` is not set. Searching for '
+ .. pyname
+ .. ' in the environment.'
+ info(message)
+ end
+
+ if not is_blank(pyenv) then
+ python_exe = vim.trim(system({ pyenv, 'which', pyname }, '', 1))
+ if is_blank(python_exe) then
+ warn('pyenv could not find ' .. pyname .. '.')
+ end
+ end
+
+ if is_blank(python_exe) then
+ python_exe = vim.fn.exepath(pyname)
+
+ if os.getenv('PATH') then
+ local path_sep = iswin and ';' or ':'
+ local paths = vim.split(os.getenv('PATH') or '', path_sep)
+
+ for _, path in ipairs(paths) do
+ local path_bin = vim.fs.normalize(path .. '/' .. pyname)
+ if
+ path_bin ~= vim.fs.normalize(python_exe)
+ and vim.list_contains(python_multiple, path_bin)
+ and executable(path_bin)
+ then
+ python_multiple[#python_multiple + 1] = path_bin
+ end
+ end
+
+ if vim.tbl_count(python_multiple) > 0 then
+ -- This is worth noting since the user may install something
+ -- that changes $PATH, like homebrew.
+ local message = 'Multiple '
+ .. pyname
+ .. ' executables found. '
+ .. 'Set `g:'
+ .. host_prog_var
+ .. '` to avoid surprises.'
+ info(message)
+ end
+
+ if python_exe:find('shims') then
+ local message = '`' .. python_exe .. '` appears to be a pyenv shim.'
+ local advice = '`pyenv` is not in $PATH, your pyenv installation is broken. Set `g:'
+ .. host_prog_var
+ .. '` to avoid surprises.'
+
+ warn(message, advice)
+ end
+ end
+ end
+ end
+
+ if not is_blank(python_exe) and not vim.g[host_prog_var] then
+ if
+ is_blank(venv)
+ and not is_blank(pyenv)
+ and not is_blank(pyenv_root)
+ and vim.startswith(vim.fn.resolve(python_exe), pyenv_root .. '/')
+ then
+ local advice = 'Create a virtualenv specifically for Nvim using pyenv, and set `g:'
+ .. host_prog_var
+ .. '`. This will avoid the need to install the pynvim module in each version/virtualenv.'
+ warn('pyenv is not set up optimally.', advice)
+ elseif not is_blank(venv) then
+ local venv_root
+ if not is_blank(pyenv_root) then
+ venv_root = pyenv_root
+ else
+ venv_root = vim.fs.dirname(venv)
+ end
+
+ if vim.startswith(vim.fn.resolve(python_exe), venv_root .. '/') then
+ local advice = 'Create a virtualenv specifically for Nvim and use `g:'
+ .. host_prog_var
+ .. '`. This will avoid the need to install the pynvim module in each virtualenv.'
+ warn('Your virtualenv is not set up optimally.', advice)
+ end
+ end
+ end
+
+ if is_blank(python_exe) and not is_blank(pyname) then
+ -- An error message should have already printed.
+ error('`' .. pyname .. '` was not found.')
+ elseif not is_blank(python_exe) and not check_bin(python_exe) then
+ python_exe = ''
+ end
+
+ -- Diagnostic output
+ info('Executable: ' .. (is_blank(python_exe) and 'Not found' or python_exe))
+ if vim.tbl_count(python_multiple) > 0 then
+ for _, path_bin in ipairs(python_multiple) do
+ info('Other python executable: ' .. path_bin)
+ end
+ end
+
+ if is_blank(python_exe) then
+ -- No Python executable can import 'neovim'. Check if any Python executable
+ -- can import 'pynvim'. If so, that Python failed to import 'neovim' as
+ -- well, which is most probably due to a failed pip upgrade:
+ -- https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
+ local pynvim_table = vim.fn['provider#pythonx#DetectByModule']('pynvim', 3)
+ local pynvim_exe = pynvim_table[1]
+ if not is_blank(pynvim_exe) then
+ local message = 'Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": '
+ .. pynvim_exe
+ local advice = {
+ 'Use that Python version to reinstall "pynvim" and optionally "neovim".',
+ pynvim_exe .. ' -m pip uninstall pynvim neovim',
+ pynvim_exe .. ' -m pip install pynvim',
+ pynvim_exe .. ' -m pip install neovim # only if needed by third-party software',
+ }
+ error(message, advice)
+ end
+ else
+ local version_info_table = version_info(python_exe)
+ local majorpyversion = version_info_table[1]
+ local current = version_info_table[2]
+ local latest = version_info_table[3]
+ local status = version_info_table[4]
+
+ if vim.fn.str2nr(majorpyversion) ~= 3 then
+ warn('Unexpected Python version. This could lead to confusing error messages.')
+ end
+
+ info('Python version: ' .. majorpyversion)
+
+ if is_bad_response(status) then
+ info('pynvim version: ' .. current .. ' (' .. status .. ')')
+ else
+ info('pynvim version: ' .. current)
+ end
+
+ if is_bad_response(current) then
+ error(
+ 'pynvim is not installed.\nError: ' .. current,
+ 'Run in shell: ' .. python_exe .. ' -m pip install pynvim'
+ )
+ end
+
+ if is_bad_response(latest) then
+ warn('Could not contact PyPI to get latest version.')
+ error('HTTP request failed: ' .. latest)
+ elseif is_bad_response(status) then
+ warn('Latest pynvim is NOT installed: ' .. latest)
+ elseif not is_bad_response(current) then
+ ok('Latest pynvim is installed.')
+ end
+ end
+
+ start('Python virtualenv')
+ if not virtual_env then
+ ok('no $VIRTUAL_ENV')
+ return
+ end
+ local errors = {}
+ -- Keep hints as dict keys in order to discard duplicates.
+ local hints = {}
+ -- The virtualenv should contain some Python executables, and those
+ -- executables should be first both on Nvim's $PATH and the $PATH of
+ -- subshells launched from Nvim.
+ local bin_dir = iswin and 'Scripts' or 'bin'
+ local venv_bins = vim.tbl_filter(function(v)
+ -- XXX: Remove irrelevant executables found in bin/.
+ return not v:match('python%-config')
+ end, vim.fn.glob(string.format('%s/%s/python*', virtual_env, bin_dir), true, true))
+ if vim.tbl_count(venv_bins) > 0 then
+ for _, venv_bin in pairs(venv_bins) do
+ venv_bin = vim.fs.normalize(venv_bin)
+ local py_bin_basename = vim.fs.basename(venv_bin)
+ local nvim_py_bin = python_exepath(vim.fn.exepath(py_bin_basename))
+ local subshell_py_bin = python_exepath(py_bin_basename)
+ if venv_bin ~= nvim_py_bin then
+ errors[#errors + 1] = '$PATH yields this '
+ .. py_bin_basename
+ .. ' executable: '
+ .. nvim_py_bin
+ local hint = '$PATH ambiguities arise if the virtualenv is not '
+ .. 'properly activated prior to launching Nvim. Close Nvim, activate the virtualenv, '
+ .. 'check that invoking Python from the command line launches the correct one, '
+ .. 'then relaunch Nvim.'
+ hints[hint] = true
+ end
+ if venv_bin ~= subshell_py_bin then
+ errors[#errors + 1] = '$PATH in subshells yields this '
+ .. py_bin_basename
+ .. ' executable: '
+ .. subshell_py_bin
+ local hint = '$PATH ambiguities in subshells typically are '
+ .. 'caused by your shell config overriding the $PATH previously set by the '
+ .. 'virtualenv. Either prevent them from doing so, or use this workaround: '
+ .. 'https://vi.stackexchange.com/a/34996'
+ hints[hint] = true
+ end
+ end
+ else
+ errors[#errors + 1] = 'no Python executables found in the virtualenv '
+ .. bin_dir
+ .. ' directory.'
+ end
+
+ local msg = '$VIRTUAL_ENV is set to: ' .. virtual_env
+ if vim.tbl_count(errors) > 0 then
+ if vim.tbl_count(venv_bins) > 0 then
+ msg = msg
+ .. '\nAnd its '
+ .. bin_dir
+ .. ' directory contains: '
+ .. vim.fn.join(vim.fn.map(venv_bins, [[fnamemodify(v:val, ':t')]]), ', ')
+ end
+ local conj = '\nBut '
+ for _, err in ipairs(errors) do
+ msg = msg .. conj .. err
+ conj = '\nAnd '
+ end
+ msg = msg .. '\nSo invoking Python may lead to unexpected results.'
+ warn(msg, vim.fn.keys(hints))
+ else
+ info(msg)
+ info(
+ 'Python version: '
+ .. system('python -c "import platform, sys; sys.stdout.write(platform.python_version())"')
+ )
+ ok('$VIRTUAL_ENV provides :!python.')
+ end
+end
+
+local function ruby()
+ start('Ruby provider (optional)')
+
+ if disabled_via_loaded_var('ruby') then
+ return
+ end
+
+ if not executable('ruby') or not executable('gem') then
+ warn(
+ '`ruby` and `gem` must be in $PATH.',
+ 'Install Ruby and verify that `ruby` and `gem` commands work.'
+ )
+ return
+ end
+ info('Ruby: ' .. system({ 'ruby', '-v' }))
+
+ local ruby_detect_table = vim.fn['provider#ruby#Detect']()
+ local host = ruby_detect_table[1]
+ if is_blank(host) then
+ warn('`neovim-ruby-host` not found.', {
+ 'Run `gem install neovim` to ensure the neovim RubyGem is installed.',
+ 'Run `gem environment` to ensure the gem bin directory is in $PATH.',
+ 'If you are using rvm/rbenv/chruby, try "rehashing".',
+ 'See :help g:ruby_host_prog for non-standard gem installations.',
+ 'You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim',
+ })
+ return
+ end
+ info('Host: ' .. host)
+
+ local latest_gem_cmd = (iswin and 'cmd /c gem list -ra "^^neovim$"' or 'gem list -ra ^neovim$')
+ local latest_gem = system(vim.fn.split(latest_gem_cmd))
+ if shell_error() or is_blank(latest_gem) then
+ error(
+ 'Failed to run: ' .. latest_gem_cmd,
+ { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' }
+ )
+ return
+ end
+ local gem_split = vim.split(latest_gem, [[neovim (\|, \|)$]])
+ latest_gem = gem_split[1] or 'not found'
+
+ local current_gem_cmd = { host, '--version' }
+ local current_gem = system(current_gem_cmd)
+ if shell_error() then
+ error(
+ 'Failed to run: ' .. table.concat(current_gem_cmd, ' '),
+ { 'Report this issue with the output of: ', table.concat(current_gem_cmd, ' ') }
+ )
+ return
+ end
+
+ if vim.version.lt(current_gem, latest_gem) then
+ local message = 'Gem "neovim" is out-of-date. Installed: '
+ .. current_gem
+ .. ', latest: '
+ .. latest_gem
+ warn(message, 'Run in shell: gem update neovim')
+ else
+ ok('Latest "neovim" gem is installed: ' .. current_gem)
+ end
+end
+
+local function node()
+ start('Node.js provider (optional)')
+
+ if disabled_via_loaded_var('node') then
+ return
+ end
+
+ if
+ not executable('node')
+ or (not executable('npm') and not executable('yarn') and not executable('pnpm'))
+ then
+ warn(
+ '`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
+ end
+
+ -- local node_v = vim.fn.split(system({'node', '-v'}), "\n")[1] or ''
+ local node_v = system({ 'node', '-v' })
+ info('Node.js: ' .. node_v)
+ if shell_error() or vim.version.lt(node_v, '6.0.0') then
+ warn('Nvim node.js host does not support Node ' .. node_v)
+ -- Skip further checks, they are nonsense if nodejs is too old.
+ return
+ end
+ if vim.fn['provider#node#can_inspect']() == 0 then
+ warn(
+ 'node.js on this system does not support --inspect-brk so $NVIM_NODE_HOST_DEBUG is ignored.'
+ )
+ end
+
+ local node_detect_table = vim.fn['provider#node#Detect']()
+ local host = node_detect_table[1]
+ if is_blank(host) then
+ 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
+ end
+ info('Nvim node.js host: ' .. host)
+
+ local manager = 'npm'
+ if executable('yarn') then
+ manager = 'yarn'
+ elseif executable('pnpm') then
+ manager = 'pnpm'
+ end
+
+ local latest_npm_cmd = (
+ iswin and 'cmd /c ' .. manager .. ' info neovim --json' or manager .. ' info neovim --json'
+ )
+ local latest_npm = system(vim.fn.split(latest_npm_cmd))
+ if shell_error() or is_blank(latest_npm) then
+ error(
+ 'Failed to run: ' .. latest_npm_cmd,
+ { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' }
+ )
+ return
+ end
+
+ local pcall_ok, output = pcall(vim.fn.json_decode, latest_npm)
+ local pkg_data
+ if pcall_ok then
+ pkg_data = output
+ else
+ return 'error: ' .. latest_npm
+ end
+ local latest_npm_subtable = pkg_data['dist-tags'] or {}
+ latest_npm = latest_npm_subtable['latest'] or 'unable to parse'
+
+ local current_npm_cmd = { 'node', host, '--version' }
+ local current_npm = system(current_npm_cmd)
+ if shell_error() then
+ error(
+ 'Failed to run: ' .. table.concat(current_npm_cmd, ' '),
+ { 'Report this issue with the output of: ', table.concat(current_npm_cmd, ' ') }
+ )
+ return
+ end
+
+ if latest_npm ~= 'unable to parse' and vim.version.lt(current_npm, latest_npm) then
+ local message = 'Package "neovim" is out-of-date. Installed: '
+ .. current_npm
+ .. ' latest: '
+ .. latest_npm
+ warn(message({
+ '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',
+ }))
+ else
+ ok('Latest "neovim" npm/yarn/pnpm package is installed: ' .. current_npm)
+ end
+end
+
+local function perl()
+ start('Perl provider (optional)')
+
+ if disabled_via_loaded_var('perl') then
+ return
+ end
+
+ local perl_detect_table = vim.fn['provider#perl#Detect']()
+ local perl_exec = perl_detect_table[1]
+ local perl_warnings = perl_detect_table[2]
+
+ if is_blank(perl_exec) then
+ if not is_blank(perl_warnings) then
+ warn(perl_warnings, {
+ 'See :help provider-perl for more information.',
+ 'You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim',
+ })
+ else
+ warn('No usable perl executable found')
+ end
+ return
+ end
+
+ info('perl executable: ' .. perl_exec)
+
+ -- we cannot use cpanm that is on the path, as it may not be for the perl
+ -- set with g:perl_host_prog
+ system({ perl_exec, '-W', '-MApp::cpanminus', '-e', '' })
+ if shell_error() then
+ return { perl_exec, '"App::cpanminus" module is not installed' }
+ end
+
+ local latest_cpan_cmd = {
+ perl_exec,
+ '-MApp::cpanminus::fatscript',
+ '-e',
+ 'my $app = App::cpanminus::script->new; $app->parse_options ("--info", "-q", "Neovim::Ext"); exit $app->doit',
+ }
+
+ local latest_cpan = system(latest_cpan_cmd)
+ if shell_error() or is_blank(latest_cpan) then
+ error(
+ 'Failed to run: ' .. table.concat(latest_cpan_cmd, ' '),
+ { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' }
+ )
+ return
+ elseif latest_cpan[1] == '!' then
+ local cpanm_errs = vim.split(latest_cpan, '!')
+ if cpanm_errs[1]:find("Can't write to ") then
+ local advice = {}
+ for i = 2, #cpanm_errs do
+ advice[#advice + 1] = cpanm_errs[i]
+ end
+
+ warn(cpanm_errs[1], advice)
+ -- Last line is the package info
+ latest_cpan = cpanm_errs[#cpanm_errs]
+ else
+ error('Unknown warning from command: ' .. latest_cpan_cmd, cpanm_errs)
+ return
+ end
+ end
+ latest_cpan = vim.fn.matchstr(latest_cpan, [[\(\.\?\d\)\+]])
+ if is_blank(latest_cpan) then
+ error('Cannot parse version number from cpanm output: ' .. latest_cpan)
+ return
+ end
+
+ local current_cpan_cmd = { perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION' }
+ local current_cpan = system(current_cpan_cmd)
+ if shell_error then
+ error(
+ 'Failed to run: ' .. table.concat(current_cpan_cmd, ' '),
+ { 'Report this issue with the output of: ', table.concat(current_cpan_cmd, ' ') }
+ )
+ return
+ end
+
+ if vim.version.lt(current_cpan, latest_cpan) then
+ local message = 'Module "Neovim::Ext" is out-of-date. Installed: '
+ .. current_cpan
+ .. ', latest: '
+ .. latest_cpan
+ warn(message, 'Run in shell: cpanm -n Neovim::Ext')
+ else
+ ok('Latest "Neovim::Ext" cpan module is installed: ' .. current_cpan)
+ end
+end
+
+function M.check()
+ clipboard()
+ python()
+ ruby()
+ node()
+ perl()
+end
+
+return M
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index fa0980563a..c922ec93db 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -402,8 +402,8 @@ end
--- Input and output positions are (0,0)-indexed and indicate byte positions.
---
---@param bufnr integer number of buffer
----@param pos1 integer[] (line, column) tuple marking beginning of region
----@param pos2 integer[] (line, column) tuple marking end of region
+---@param pos1 integer[]|string start of region as a (line, column) tuple or string accepted by |getpos()|
+---@param pos2 integer[]|string end of region as a (line, column) tuple or string accepted by |getpos()|
---@param regtype string type of selection, see |setreg()|
---@param inclusive boolean indicating whether column of pos2 is inclusive
---@return table region Table of the form `{linenr = {startcol,endcol}}`.
@@ -414,6 +414,24 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
vim.fn.bufload(bufnr)
end
+ if type(pos1) == 'string' then
+ local pos = vim.fn.getpos(pos1)
+ pos1 = { pos[2] - 1, pos[3] - 1 + pos[4] }
+ end
+ if type(pos2) == 'string' then
+ local pos = vim.fn.getpos(pos2)
+ pos2 = { pos[2] - 1, pos[3] - 1 + pos[4] }
+ end
+
+ if pos1[1] > pos2[1] or (pos1[1] == pos2[1] and pos1[2] > pos2[2]) then
+ pos1, pos2 = pos2, pos1
+ end
+
+ -- getpos() may return {0,0,0,0}
+ if pos1[1] < 0 or pos1[2] < 0 then
+ return {}
+ end
+
-- check that region falls within current buffer
local buf_line_count = vim.api.nvim_buf_line_count(bufnr)
pos1[1] = math.min(pos1[1], buf_line_count - 1)
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 714038f8e4..d1b50304c7 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -744,7 +744,7 @@ function M.get_namespaces()
end
---@class Diagnostic
----@field buffer integer
+---@field bufnr integer
---@field lnum integer 0-indexed
---@field end_lnum nil|integer 0-indexed
---@field col integer 0-indexed
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index d71a806ea8..a6cfcb730f 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -15,8 +15,8 @@ M.priorities = {
---@param bufnr integer Buffer number to apply highlighting to
---@param ns integer Namespace to add highlight to
---@param higroup string Highlight group to use for highlighting
----@param start { [1]: integer, [2]: integer } Start position {line, col}
----@param finish { [1]: integer, [2]: integer } Finish position {line, col}
+---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()|
+---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()|
---@param opts table|nil Optional parameters
-- - regtype type of range (see |setreg()|, default charwise)
-- - inclusive boolean indicating whether the range is end-inclusive (default false)
@@ -27,11 +27,6 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
local inclusive = opts.inclusive or false
local priority = opts.priority or M.priorities.user
- -- sanity check
- if start[2] < 0 or finish[1] < start[1] then
- return
- end
-
local region = vim.region(bufnr, start, finish, regtype, inclusive)
for linenr, cols in pairs(region) do
local end_row
@@ -104,18 +99,12 @@ function M.on_yank(opts)
yank_timer:close()
end
- local pos1 = vim.fn.getpos("'[")
- local pos2 = vim.fn.getpos("']")
-
- pos1 = { pos1[2] - 1, pos1[3] - 1 + pos1[4] }
- pos2 = { pos2[2] - 1, pos2[3] - 1 + pos2[4] }
-
M.range(
bufnr,
yank_ns,
higroup,
- pos1,
- pos2,
+ "'[",
+ "']",
{ regtype = event.regtype, inclusive = event.inclusive, priority = M.priorities.user }
)
diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua
index 38b1e9fc0f..66627fe4e7 100644
--- a/runtime/lua/vim/loader.lua
+++ b/runtime/lua/vim/loader.lua
@@ -5,7 +5,7 @@ local loaders = package.loaders
local M = {}
----@alias CacheHash {mtime: {sec:number, nsec:number}, size:number, type: string}
+---@alias CacheHash {mtime: {nsec: integer, sec: integer}, size: integer, type?: uv.aliases.fs_stat_types}
---@alias CacheEntry {hash:CacheHash, chunk:string}
---@class ModuleFindOpts
@@ -28,12 +28,11 @@ M.enabled = false
---@field _rtp string[]
---@field _rtp_pure string[]
---@field _rtp_key string
+---@field _hashes? table<string, CacheHash>
local Loader = {
VERSION = 3,
---@type table<string, table<string,ModuleInfo>>
_indexed = {},
- ---@type table<string, CacheHash>
- _hashes = {},
---@type table<string, string[]>
_topmods = {},
_loadfile = loadfile,
@@ -44,9 +43,13 @@ local Loader = {
}
--- @param path string
---- @return uv.fs_stat.result
+--- @return CacheHash
--- @private
function Loader.get_hash(path)
+ if not Loader._hashes then
+ return uv.fs_stat(path) --[[@as CacheHash]]
+ end
+
if not Loader._hashes[path] then
-- Note we must never save a stat for a non-existent path.
-- For non-existent paths fs_stat() will return nil.
@@ -163,13 +166,16 @@ end
---@return string|function
---@private
function Loader.loader(modname)
+ Loader._hashes = {}
local ret = M.find(modname)[1]
if ret then
-- Make sure to call the global loadfile so we respect any augmentations done elsewhere.
-- E.g. profiling
local chunk, err = loadfile(ret.modpath)
+ Loader._hashes = nil
return chunk or error(err)
end
+ Loader._hashes = nil
return '\ncache_loader: module ' .. modname .. ' not found'
end
@@ -373,7 +379,9 @@ function M.reset(path)
end
-- Path could be a directory so just clear all the hashes.
- Loader._hashes = {}
+ if Loader._hashes then
+ Loader._hashes = {}
+ end
end
--- Enables the experimental Lua module loader:
@@ -441,7 +449,7 @@ function Loader.lsmod(path)
if topname then
Loader._indexed[path][topname] = { modpath = modpath, modname = topname }
Loader._topmods[topname] = Loader._topmods[topname] or {}
- if not vim.tbl_contains(Loader._topmods[topname], path) then
+ if not vim.list_contains(Loader._topmods[topname], path) then
table.insert(Loader._topmods[topname], path)
end
end
@@ -515,7 +523,7 @@ function M._inspect(opts)
{ ms(Loader._stats[stat].time / Loader._stats[stat].total) .. '\n', 'Bold' },
})
for k, v in pairs(Loader._stats[stat]) do
- if not vim.tbl_contains({ 'time', 'total' }, k) then
+ if not vim.list_contains({ 'time', 'total' }, k) then
chunks[#chunks + 1] = { '* ' .. k .. ':' .. string.rep(' ', 9 - #k) }
chunks[#chunks + 1] = { tostring(v) .. '\n', 'Number' }
end
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 2d39f2d45d..5c78bd7580 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -171,7 +171,7 @@ local function for_each_buffer_client(bufnr, fn, restrict_client_ids)
if restrict_client_ids and #restrict_client_ids > 0 then
local filtered_client_ids = {}
for client_id in pairs(client_ids) do
- if vim.tbl_contains(restrict_client_ids, client_id) then
+ if vim.list_contains(restrict_client_ids, client_id) then
filtered_client_ids[client_id] = true
end
end
@@ -2186,7 +2186,7 @@ function lsp.formatexpr(opts)
opts = opts or {}
local timeout_ms = opts.timeout_ms or 500
- if vim.tbl_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then
+ if vim.list_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then
-- `formatexpr` is also called when exceeding `textwidth` in insert mode
-- fall back to internal formatting
return 1
@@ -2384,4 +2384,3 @@ lsp.commands = setmetatable({}, {
})
return lsp
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua
index 797d8960d5..e7ada5415f 100644
--- a/runtime/lua/vim/lsp/_snippet.lua
+++ b/runtime/lua/vim/lsp/_snippet.lua
@@ -17,14 +17,14 @@ P.take_until = function(targets, specials)
table.insert(raw, '\\')
new_pos = new_pos + 1
c = string.sub(input, new_pos, new_pos)
- if not vim.tbl_contains(targets, c) and not vim.tbl_contains(specials, c) then
+ if not vim.list_contains(targets, c) and not vim.list_contains(specials, c) then
table.insert(esc, '\\')
end
table.insert(raw, c)
table.insert(esc, c)
new_pos = new_pos + 1
else
- if vim.tbl_contains(targets, c) then
+ if vim.list_contains(targets, c) then
break
end
table.insert(raw, c)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 8bf3764f5e..3d9011656f 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -806,4 +806,3 @@ function M.execute_command(command_params)
end
return M
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index 81cac6a511..005a0047fa 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -42,7 +42,7 @@ local function execute_lens(lens, bufnr, client_id)
-- Need to use the client that returned the lens → must not use buf_request
local command_provider = client.server_capabilities.executeCommandProvider
local commands = type(command_provider) == 'table' and command_provider.commands or {}
- if not vim.tbl_contains(commands, command.command) then
+ if not vim.list_contains(commands, command.command) then
vim.notify(
string.format(
'Language server does not support command `%s`. This command may require a client extension.',
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index d01f8e6159..71bef43bc1 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -644,4 +644,3 @@ for k, fn in pairs(M) do
end
return M
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index 51dcb7d21d..3d5bc06c3f 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -174,4 +174,3 @@ function log.should_log(level)
end
return log
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index f4489ad17d..2cb8fc7955 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -894,4 +894,3 @@ function protocol.resolve_capabilities(server_capabilities)
end
return protocol
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 30b61d01d6..af3190c9bd 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -753,4 +753,3 @@ return {
client_errors = client_errors,
create_read_loop = create_read_loop,
}
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index ebde7af16c..31af2afb0b 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1476,7 +1476,7 @@ end
local function close_preview_window(winnr, bufnrs)
vim.schedule(function()
-- exit if we are in one of ignored buffers
- if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then
+ if bufnrs and vim.list_contains(bufnrs, api.nvim_get_current_buf()) then
return
end
@@ -2154,4 +2154,3 @@ M._get_line_byte_from_position = get_line_byte_from_position
M.buf_versions = {}
return M
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index eb734fb512..f700f4a6b3 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -252,12 +252,53 @@ function vim.tbl_filter(func, t)
return rettab
end
---- Checks if a list-like (vector) table contains `value`.
+--- Checks if a table contains a given value, specified either directly or via
+--- a predicate that is checked for each value.
+---
+--- Example:
+--- <pre>lua
+--- vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
+--- return vim.deep_equal(v, { 'b', 'c' })
+--- end, { predicate = true })
+--- -- true
+--- </pre>
+---
+---@see |vim.list_contains()| for checking values in list-like tables
---
---@param t table Table to check
+---@param value any Value to compare or predicate function reference
+---@param opts (table|nil) Keyword arguments |kwargs|:
+--- - predicate: (boolean) `value` is a function reference to be checked (default false)
+---@return boolean `true` if `t` contains `value`
+function vim.tbl_contains(t, value, opts)
+ vim.validate({ t = { t, 't' }, opts = { opts, 't', true } })
+
+ local pred
+ if opts and opts.predicate then
+ vim.validate({ value = { value, 'c' } })
+ pred = value
+ else
+ pred = function(v)
+ return v == value
+ end
+ end
+
+ for _, v in pairs(t) do
+ if pred(v) then
+ return true
+ end
+ end
+ return false
+end
+
+--- Checks if a list-like table (integer keys without gaps) contains `value`.
+---
+---@see |vim.tbl_contains()| for checking values in general tables
+---
+---@param t table Table to check (must be list-like, not validated)
---@param value any Value to compare
---@return boolean `true` if `t` contains `value`
-function vim.tbl_contains(t, value)
+function vim.list_contains(t, value)
vim.validate({ t = { t, 't' } })
for _, v in ipairs(t) do
@@ -279,10 +320,10 @@ function vim.tbl_isempty(t)
return next(t) == nil
end
---- We only merge empty tables or tables that are not a list
+--- We only merge empty tables or tables that are not an array (indexed by integers)
---@private
local function can_merge(v)
- return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_islist(v))
+ return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_isarray(v))
end
local function tbl_extend(behavior, deep_extend, ...)
@@ -513,15 +554,15 @@ function vim.spairs(t)
end
end
---- Tests if a Lua table can be treated as an array.
+--- Tests if a Lua table can be treated as an array (a table indexed by integers).
---
--- Empty table `{}` is assumed to be an array, unless it was created by
--- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result,
--- for example from |rpcrequest()| or |vim.fn|.
---
----@param t table Table
----@return boolean `true` if array-like table, else `false`
-function vim.tbl_islist(t)
+---@param t table
+---@return boolean `true` if array-like table, else `false`.
+function vim.tbl_isarray(t)
if type(t) ~= 'table' then
return false
end
@@ -529,7 +570,8 @@ function vim.tbl_islist(t)
local count = 0
for k, _ in pairs(t) do
- if type(k) == 'number' then
+ --- Check if the number k is an integer
+ if type(k) == 'number' and k == math.floor(k) then
count = count + 1
else
return false
@@ -548,6 +590,38 @@ function vim.tbl_islist(t)
end
end
+--- Tests if a Lua table can be treated as a list (a table indexed by consecutive integers starting from 1).
+---
+--- Empty table `{}` is assumed to be an list, unless it was created by
+--- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result,
+--- for example from |rpcrequest()| or |vim.fn|.
+---
+---@param t table
+---@return boolean `true` if list-like table, else `false`.
+function vim.tbl_islist(t)
+ if type(t) ~= 'table' then
+ return false
+ end
+
+ local num_elem = vim.tbl_count(t)
+
+ if num_elem == 0 then
+ -- TODO(bfredl): in the future, we will always be inside nvim
+ -- then this check can be deleted.
+ if vim._empty_dict_mt == nil then
+ return nil
+ end
+ return getmetatable(t) ~= vim._empty_dict_mt
+ else
+ for i = 1, num_elem do
+ if t[i] == nil then
+ return false
+ end
+ end
+ return true
+ end
+end
+
--- Counts the number of non-nil values in table `t`.
---
--- <pre>lua
@@ -811,4 +885,3 @@ function vim.defaulttable(create)
end
return vim
--- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 5b87e6ac31..8a747ba14c 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -458,7 +458,7 @@ local directive_handlers = {
metadata[id] = {}
end
- local pattern, replacement = pred[3], pred[3]
+ local pattern, replacement = pred[3], pred[4]
assert(type(pattern) == 'string')
assert(type(replacement) == 'string')
diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua
index 38759fcdc0..08ed829114 100644
--- a/runtime/lua/vim/uri.lua
+++ b/runtime/lua/vim/uri.lua
@@ -134,4 +134,3 @@ return {
uri_to_fname = uri_to_fname,
uri_to_bufnr = uri_to_bufnr,
}
--- vim:sw=2 ts=2 et
diff --git a/runtime/mswin.vim b/runtime/mswin.vim
index 2b04c1aea3..482b720c53 100644
--- a/runtime/mswin.vim
+++ b/runtime/mswin.vim
@@ -15,7 +15,10 @@ endif
set cpo&vim
" set 'selection', 'selectmode', 'mousemodel' and 'keymodel' for MS-Windows
-behave mswin
+set selection=exclusive
+set selectmode=mouse,key
+set mousemodel=popup
+set keymodel=startsel,stopsel
" backspace and cursor keys wrap to previous/next line
set backspace=indent,eol,start whichwrap+=<,>,[,]
diff --git a/runtime/pack/dist/opt/vimball/autoload/vimball.vim b/runtime/pack/dist/opt/vimball/autoload/vimball.vim
deleted file mode 100644
index 9c7dcbda0f..0000000000
--- a/runtime/pack/dist/opt/vimball/autoload/vimball.vim
+++ /dev/null
@@ -1,775 +0,0 @@
-" vimball.vim : construct a file containing both paths and files
-" Author: Charles E. Campbell
-" Date: Apr 11, 2016
-" Version: 37
-" GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim
-" Copyright: (c) 2004-2011 by Charles E. Campbell
-" The VIM LICENSE applies to Vimball.vim, and Vimball.txt
-" (see |copyright|) except use "Vimball" instead of "Vim".
-" No warranty, express or implied.
-" *** *** Use At-Your-Own-Risk! *** ***
-
-" ---------------------------------------------------------------------
-" Load Once: {{{1
-if &cp || exists("g:loaded_vimball")
- finish
-endif
-let g:loaded_vimball = "v37"
-if v:version < 702
- echohl WarningMsg
- echo "***warning*** this version of vimball needs vim 7.2"
- echohl Normal
- finish
-endif
-let s:keepcpo= &cpo
-set cpo&vim
-"DechoTabOn
-
-" =====================================================================
-" Constants: {{{1
-if !exists("s:USAGE")
- let s:USAGE = 0
- let s:WARNING = 1
- let s:ERROR = 2
-
- " determine if cygwin is in use or not
- if !exists("g:netrw_cygwin")
- if has("win32") || has("win95") || has("win64") || has("win16")
- if &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$'
- let g:netrw_cygwin= 1
- else
- let g:netrw_cygwin= 0
- endif
- else
- let g:netrw_cygwin= 0
- endif
- endif
-
- " set up g:vimball_mkdir if the mkdir() call isn't defined
- if !exists("*mkdir")
- if exists("g:netrw_local_mkdir")
- let g:vimball_mkdir= g:netrw_local_mkdir
- elseif executable("mkdir")
- let g:vimball_mkdir= "mkdir"
- elseif executable("makedir")
- let g:vimball_mkdir= "makedir"
- endif
- if !exists(g:vimball_mkdir)
- call vimball#ShowMesg(s:WARNING,"(vimball) g:vimball_mkdir undefined")
- endif
- endif
-endif
-
-" =====================================================================
-" Functions: {{{1
-
-" ---------------------------------------------------------------------
-" vimball#MkVimball: creates a vimball given a list of paths to files {{{2
-" Input:
-" line1,line2: a range of lines containing paths to files to be included in the vimball
-" writelevel : if true, force a write to filename.vmb, even if it exists
-" (usually accomplished with :MkVimball! ...
-" filename : base name of file to be created (ie. filename.vmb)
-" Output: a filename.vmb using vimball format:
-" path
-" filesize
-" [file]
-" path
-" filesize
-" [file]
-fun! vimball#MkVimball(line1,line2,writelevel,...) range
-" call Dfunc("MkVimball(line1=".a:line1." line2=".a:line2." writelevel=".a:writelevel." vimballname<".a:1.">) a:0=".a:0)
- if a:1 =~ '\.vim$' || a:1 =~ '\.txt$'
- let vbname= substitute(a:1,'\.\a\{3}$','.vmb','')
- else
- let vbname= a:1
- endif
- if vbname !~ '\.vmb$'
- let vbname= vbname.'.vmb'
- endif
-" call Decho("vbname<".vbname.">")
- if !a:writelevel && a:1 =~ '[\/]'
- call vimball#ShowMesg(s:ERROR,"(MkVimball) vimball name<".a:1."> should not include slashes; use ! to insist")
-" call Dret("MkVimball : vimball name<".a:1."> should not include slashes")
- return
- endif
- if !a:writelevel && filereadable(vbname)
- call vimball#ShowMesg(s:ERROR,"(MkVimball) file<".vbname."> exists; use ! to insist")
-" call Dret("MkVimball : file<".vbname."> already exists; use ! to insist")
- return
- endif
-
- " user option bypass
- call vimball#SaveSettings()
-
- if a:0 >= 2
- " allow user to specify where to get the files
- let home= expand(a:2)
- else
- " use first existing directory from rtp
- let home= vimball#VimballHome()
- endif
-
- " save current directory
- let curdir = getcwd()
- call s:ChgDir(home)
-
- " record current tab, initialize while loop index
- let curtabnr = tabpagenr()
- let linenr = a:line1
-" call Decho("curtabnr=".curtabnr)
-
- while linenr <= a:line2
- let svfile = getline(linenr)
-" call Decho("svfile<".svfile.">")
-
- if !filereadable(svfile)
- call vimball#ShowMesg(s:ERROR,"unable to read file<".svfile.">")
- call s:ChgDir(curdir)
- call vimball#RestoreSettings()
-" call Dret("MkVimball")
- return
- endif
-
- " create/switch to mkvimball tab
- if !exists("vbtabnr")
- tabnew
- sil! file Vimball
- let vbtabnr= tabpagenr()
- else
- exe "tabn ".vbtabnr
- endif
-
- let lastline= line("$") + 1
- if lastline == 2 && getline("$") == ""
- call setline(1,'" Vimball Archiver by Charles E. Campbell')
- call setline(2,'UseVimball')
- call setline(3,'finish')
- let lastline= line("$") + 1
- endif
- call setline(lastline ,substitute(svfile,'$',' [[[1',''))
- call setline(lastline+1,0)
-
- " write the file from the tab
-" call Decho("exe $r ".fnameescape(svfile))
- exe "$r ".fnameescape(svfile)
-
- call setline(lastline+1,line("$") - lastline - 1)
-" call Decho("lastline=".lastline." line$=".line("$"))
-
- " restore to normal tab
- exe "tabn ".curtabnr
- let linenr= linenr + 1
- endwhile
-
- " write the vimball
- exe "tabn ".vbtabnr
- call s:ChgDir(curdir)
- setlocal ff=unix
- if a:writelevel
-" call Decho("exe w! ".fnameescape(vbname))
- exe "w! ".fnameescape(vbname)
- else
-" call Decho("exe w ".fnameescape(vbname))
- exe "w ".fnameescape(vbname)
- endif
-" call Decho("Vimball<".vbname."> created")
- echo "Vimball<".vbname."> created"
-
- " remove the evidence
- setlocal nomod bh=wipe
- exe "tabn ".curtabnr
- exe "tabc! ".vbtabnr
-
- " restore options
- call vimball#RestoreSettings()
-
-" call Dret("MkVimball")
-endfun
-
-" ---------------------------------------------------------------------
-" vimball#Vimball: extract and distribute contents from a vimball {{{2
-" (invoked the the UseVimball command embedded in
-" vimballs' prologue)
-fun! vimball#Vimball(really,...)
-" call Dfunc("vimball#Vimball(really=".a:really.") a:0=".a:0)
-
- if v:version < 701 || (v:version == 701 && !exists('*fnameescape'))
- echoerr "your vim is missing the fnameescape() function (pls upgrade to vim 7.2 or later)"
-" call Dret("vimball#Vimball : needs 7.1 with patch 299 or later")
- return
- endif
-
- if getline(1) !~ '^" Vimball Archiver'
- echoerr "(Vimball) The current file does not appear to be a Vimball!"
-" call Dret("vimball#Vimball")
- return
- endif
-
- " set up standard settings
- call vimball#SaveSettings()
- let curtabnr = tabpagenr()
- let vimballfile = expand("%:tr")
-
- " set up vimball tab
-" call Decho("setting up vimball tab")
- tabnew
- sil! file Vimball
- let vbtabnr= tabpagenr()
- let didhelp= ""
-
- " go to vim plugin home
- if a:0 > 0
- " let user specify the directory where the vimball is to be unpacked.
- " If, however, the user did not specify a full path, set the home to be below the current directory
- let home= expand(a:1)
- if has("win32") || has("win95") || has("win64") || has("win16")
- if home !~ '^\a:[/\\]'
- let home= getcwd().'/'.a:1
- endif
- elseif home !~ '^/'
- let home= getcwd().'/'.a:1
- endif
- else
- let home= vimball#VimballHome()
- endif
-" call Decho("home<".home.">")
-
- " save current directory and remove older same-named vimball, if any
- let curdir = getcwd()
-" call Decho("home<".home.">")
-" call Decho("curdir<".curdir.">")
-
- call s:ChgDir(home)
- let s:ok_unablefind= 1
- call vimball#RmVimball(vimballfile)
- unlet s:ok_unablefind
-
- let linenr = 4
- let filecnt = 0
-
- " give title to listing of (extracted) files from Vimball Archive
- if a:really
- echohl Title | echomsg "Vimball Archive" | echohl None
- else
- echohl Title | echomsg "Vimball Archive Listing" | echohl None
- echohl Statement | echomsg "files would be placed under: ".home | echohl None
- endif
-
- " apportion vimball contents to various files
-" call Decho("exe tabn ".curtabnr)
- exe "tabn ".curtabnr
-" call Decho("linenr=".linenr." line$=".line("$"))
- while 1 < linenr && linenr < line("$")
- let fname = substitute(getline(linenr),'\t\[\[\[1$','','')
- let fname = substitute(fname,'\\','/','g')
- let fsize = substitute(getline(linenr+1),'^\(\d\+\).\{-}$','\1','')+0
- let fenc = substitute(getline(linenr+1),'^\d\+\s*\(\S\{-}\)$','\1','')
- let filecnt = filecnt + 1
-" call Decho("fname<".fname."> fsize=".fsize." filecnt=".filecnt. " fenc=".fenc)
-
- if a:really
- echomsg "extracted <".fname.">: ".fsize." lines"
- else
- echomsg "would extract <".fname.">: ".fsize." lines"
- endif
-" call Decho("using L#".linenr.": will extract file<".fname.">")
-" call Decho("using L#".(linenr+1).": fsize=".fsize)
-
- " Allow AsNeeded/ directory to take place of plugin/ directory
- " when AsNeeded/filename is filereadable or was present in VimballRecord
- if fname =~ '\<plugin/'
- let anfname= substitute(fname,'\<plugin/','AsNeeded/','')
- if filereadable(anfname) || (exists("s:VBRstring") && s:VBRstring =~# anfname)
-" call Decho("using anfname<".anfname."> instead of <".fname.">")
- let fname= anfname
- endif
- endif
-
- " make directories if they don't exist yet
- if a:really
-" call Decho("making directories if they don't exist yet (fname<".fname.">)")
- let fnamebuf= substitute(fname,'\\','/','g')
- let dirpath = substitute(home,'\\','/','g')
-" call Decho("init: fnamebuf<".fnamebuf.">")
-" call Decho("init: dirpath <".dirpath.">")
- while fnamebuf =~ '/'
- let dirname = dirpath."/".substitute(fnamebuf,'/.*$','','')
- let dirpath = dirname
- let fnamebuf = substitute(fnamebuf,'^.\{-}/\(.*\)$','\1','')
-" call Decho("dirname<".dirname.">")
-" call Decho("dirpath<".dirpath.">")
- if !isdirectory(dirname)
-" call Decho("making <".dirname.">")
- if exists("g:vimball_mkdir")
- call system(g:vimball_mkdir." ".shellescape(dirname))
- else
- call mkdir(dirname)
- endif
- call s:RecordInVar(home,"rmdir('".dirname."')")
- endif
- endwhile
- endif
- call s:ChgDir(home)
-
- " grab specified qty of lines and place into "a" buffer
- " (skip over path/filename and qty-lines)
- let linenr = linenr + 2
- let lastline = linenr + fsize - 1
-" call Decho("exe ".linenr.",".lastline."yank a")
- " no point in handling a zero-length file
- if lastline >= linenr
- exe "silent ".linenr.",".lastline."yank a"
-
- " copy "a" buffer into tab
-" call Decho('copy "a buffer into tab#'.vbtabnr)
- exe "tabn ".vbtabnr
- setlocal ma
- sil! %d
- silent put a
- 1
- sil! d
-
- " write tab to file
- if a:really
- let fnamepath= home."/".fname
-" call Decho("exe w! ".fnameescape(fnamepath))
- if fenc != ""
- exe "silent w! ++enc=".fnameescape(fenc)." ".fnameescape(fnamepath)
- else
- exe "silent w! ".fnameescape(fnamepath)
- endif
- echo "wrote ".fnameescape(fnamepath)
- call s:RecordInVar(home,"call delete('".fnamepath."')")
- endif
-
- " return to tab with vimball
-" call Decho("exe tabn ".curtabnr)
- exe "tabn ".curtabnr
-
- " set up help if it's a doc/*.txt file
-" call Decho("didhelp<".didhelp."> fname<".fname.">")
- if a:really && didhelp == "" && fname =~ 'doc/[^/]\+\.\(txt\|..x\)$'
- let didhelp= substitute(fname,'^\(.*\<doc\)[/\\][^.]*\.\(txt\|..x\)$','\1','')
-" call Decho("didhelp<".didhelp.">")
- endif
- endif
-
- " update for next file
-" call Decho("update linenr= [linenr=".linenr."] + [fsize=".fsize."] = ".(linenr+fsize))
- let linenr= linenr + fsize
- endwhile
-
- " set up help
-" call Decho("about to set up help: didhelp<".didhelp.">")
- if didhelp != ""
- let htpath= home."/".didhelp
-" call Decho("exe helptags ".htpath)
- exe "helptags ".fnameescape(htpath)
- echo "did helptags"
- endif
-
- " make sure a "Press ENTER..." prompt appears to keep the messages showing!
- while filecnt <= &ch
- echomsg " "
- let filecnt= filecnt + 1
- endwhile
-
- " record actions in <.VimballRecord>
- call s:RecordInFile(home)
-
- " restore events, delete tab and buffer
- exe "sil! tabn ".vbtabnr
- setlocal nomod bh=wipe
- exe "sil! tabn ".curtabnr
- exe "sil! tabc! ".vbtabnr
- call vimball#RestoreSettings()
- call s:ChgDir(curdir)
-
-" call Dret("vimball#Vimball")
-endfun
-
-" ---------------------------------------------------------------------
-" vimball#RmVimball: remove any files, remove any directories made by any {{{2
-" previous vimball extraction based on a file of the current
-" name.
-" Usage: RmVimball (assume current file is a vimball; remove)
-" RmVimball vimballname
-fun! vimball#RmVimball(...)
-" call Dfunc("vimball#RmVimball() a:0=".a:0)
- if exists("g:vimball_norecord")
-" call Dret("vimball#RmVimball : (g:vimball_norecord)")
- return
- endif
-
- if a:0 == 0
- let curfile= expand("%:tr")
-" call Decho("case a:0=0: curfile<".curfile."> (used expand(%:tr))")
- else
- if a:1 =~ '[\/]'
- call vimball#ShowMesg(s:USAGE,"RmVimball vimballname [path]")
-" call Dret("vimball#RmVimball : suspect a:1<".a:1.">")
- return
- endif
- let curfile= a:1
-" call Decho("case a:0=".a:0.": curfile<".curfile.">")
- endif
- if curfile =~ '\.vmb$'
- let curfile= substitute(curfile,'\.vmb','','')
- elseif curfile =~ '\.vba$'
- let curfile= substitute(curfile,'\.vba','','')
- endif
- if a:0 >= 2
- let home= expand(a:2)
- else
- let home= vimball#VimballHome()
- endif
- let curdir = getcwd()
-" call Decho("home <".home.">")
-" call Decho("curfile<".curfile.">")
-" call Decho("curdir <".curdir.">")
-
- call s:ChgDir(home)
- if filereadable(".VimballRecord")
-" call Decho(".VimballRecord is readable")
-" call Decho("curfile<".curfile.">")
- keepalt keepjumps 1split
- sil! keepalt keepjumps e .VimballRecord
- let keepsrch= @/
-" call Decho('search for ^\M'.curfile.'.\m: ')
-" call Decho('search for ^\M'.curfile.'.\m{vba|vmb}: ')
-" call Decho('search for ^\M'.curfile.'\m[-0-9.]*\.{vba|vmb}: ')
- if search('^\M'.curfile."\m: ".'cw')
- let foundit= 1
- elseif search('^\M'.curfile.".\mvmb: ",'cw')
- let foundit= 2
- elseif search('^\M'.curfile.'\m[-0-9.]*\.vmb: ','cw')
- let foundit= 2
- elseif search('^\M'.curfile.".\mvba: ",'cw')
- let foundit= 1
- elseif search('^\M'.curfile.'\m[-0-9.]*\.vba: ','cw')
- let foundit= 1
- else
- let foundit = 0
- endif
- if foundit
- if foundit == 1
- let exestring = substitute(getline("."),'^\M'.curfile.'\m\S\{-}\.vba: ','','')
- else
- let exestring = substitute(getline("."),'^\M'.curfile.'\m\S\{-}\.vmb: ','','')
- endif
- let s:VBRstring= substitute(exestring,'call delete(','','g')
- let s:VBRstring= substitute(s:VBRstring,"[')]",'','g')
-" call Decho("exe ".exestring)
- sil! keepalt keepjumps exe exestring
- sil! keepalt keepjumps d
- let exestring= strlen(substitute(exestring,'call delete(.\{-})|\=',"D","g"))
-" call Decho("exestring<".exestring.">")
- echomsg "removed ".exestring." files"
- else
- let s:VBRstring= ''
- let curfile = substitute(curfile,'\.vmb','','')
-" call Decho("unable to find <".curfile."> in .VimballRecord")
- if !exists("s:ok_unablefind")
- call vimball#ShowMesg(s:WARNING,"(RmVimball) unable to find <".curfile."> in .VimballRecord")
- endif
- endif
- sil! keepalt keepjumps g/^\s*$/d
- sil! keepalt keepjumps wq!
- let @/= keepsrch
- endif
- call s:ChgDir(curdir)
-
-" call Dret("vimball#RmVimball")
-endfun
-
-" ---------------------------------------------------------------------
-" vimball#Decompress: attempts to automatically decompress vimballs {{{2
-fun! vimball#Decompress(fname,...)
-" call Dfunc("Decompress(fname<".a:fname.">) a:0=".a:0)
-
- " decompression:
- if expand("%") =~ '.*\.gz' && executable("gunzip")
- " handle *.gz with gunzip
- silent exe "!gunzip ".shellescape(a:fname)
- if v:shell_error != 0
- call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) gunzip may have failed with <".a:fname.">")
- endif
- let fname= substitute(a:fname,'\.gz$','','')
- exe "e ".escape(fname,' \')
- if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif
-
- elseif expand("%") =~ '.*\.gz' && executable("gzip")
- " handle *.gz with gzip -d
- silent exe "!gzip -d ".shellescape(a:fname)
- if v:shell_error != 0
- call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "gzip -d" may have failed with <'.a:fname.">")
- endif
- let fname= substitute(a:fname,'\.gz$','','')
- exe "e ".escape(fname,' \')
- if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif
-
- elseif expand("%") =~ '.*\.bz2' && executable("bunzip2")
- " handle *.bz2 with bunzip2
- silent exe "!bunzip2 ".shellescape(a:fname)
- if v:shell_error != 0
- call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) bunzip2 may have failed with <".a:fname.">")
- endif
- let fname= substitute(a:fname,'\.bz2$','','')
- exe "e ".escape(fname,' \')
- if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif
-
- elseif expand("%") =~ '.*\.bz2' && executable("bzip2")
- " handle *.bz2 with bzip2 -d
- silent exe "!bzip2 -d ".shellescape(a:fname)
- if v:shell_error != 0
- call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "bzip2 -d" may have failed with <'.a:fname.">")
- endif
- let fname= substitute(a:fname,'\.bz2$','','')
- exe "e ".escape(fname,' \')
- if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif
-
- elseif expand("%") =~ '.*\.zip' && executable("unzip")
- " handle *.zip with unzip
- silent exe "!unzip ".shellescape(a:fname)
- if v:shell_error != 0
- call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) unzip may have failed with <".a:fname.">")
- endif
- let fname= substitute(a:fname,'\.zip$','','')
- exe "e ".escape(fname,' \')
- if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif
- endif
-
- if a:0 == 0| setlocal noma bt=nofile fmr=[[[,]]] fdm=marker | endif
-
-" call Dret("Decompress")
-endfun
-
-" ---------------------------------------------------------------------
-" vimball#ShowMesg: {{{2
-fun! vimball#ShowMesg(level,msg)
-" call Dfunc("vimball#ShowMesg(level=".a:level." msg<".a:msg.">)")
-
- let rulerkeep = &ruler
- let showcmdkeep = &showcmd
- set noruler noshowcmd
- redraw!
-
- if &fo =~# '[ta]'
- echomsg "***vimball*** ".a:msg
- else
- if a:level == s:WARNING || a:level == s:USAGE
- echohl WarningMsg
- elseif a:level == s:ERROR
- echohl Error
- endif
- echomsg "***vimball*** ".a:msg
- echohl None
- endif
-
- if a:level != s:USAGE
- call inputsave()|let ok= input("Press <cr> to continue")|call inputrestore()
- endif
-
- let &ruler = rulerkeep
- let &showcmd = showcmdkeep
-
-" call Dret("vimball#ShowMesg")
-endfun
-" =====================================================================
-" s:ChgDir: change directory (in spite of Windoze) {{{2
-fun! s:ChgDir(newdir)
-" call Dfunc("ChgDir(newdir<".a:newdir.">)")
- if (has("win32") || has("win95") || has("win64") || has("win16"))
- try
- exe 'silent cd '.fnameescape(substitute(a:newdir,'/','\\','g'))
- catch /^Vim\%((\a\+)\)\=:E/
- call mkdir(fnameescape(substitute(a:newdir,'/','\\','g')))
- exe 'silent cd '.fnameescape(substitute(a:newdir,'/','\\','g'))
- endtry
- else
- try
- exe 'silent cd '.fnameescape(a:newdir)
- catch /^Vim\%((\a\+)\)\=:E/
- call mkdir(fnameescape(a:newdir))
- exe 'silent cd '.fnameescape(a:newdir)
- endtry
- endif
-" call Dret("ChgDir : curdir<".getcwd().">")
-endfun
-
-" ---------------------------------------------------------------------
-" s:RecordInVar: record a un-vimball command in the .VimballRecord file {{{2
-fun! s:RecordInVar(home,cmd)
-" call Dfunc("RecordInVar(home<".a:home."> cmd<".a:cmd.">)")
- if a:cmd =~ '^rmdir'
-" if !exists("s:recorddir")
-" let s:recorddir= substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
-" else
-" let s:recorddir= s:recorddir."|".substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
-" endif
- elseif !exists("s:recordfile")
- let s:recordfile= a:cmd
- else
- let s:recordfile= s:recordfile."|".a:cmd
- endif
-" call Dret("RecordInVar : s:recordfile<".(exists("s:recordfile")? s:recordfile : "")."> s:recorddir<".(exists("s:recorddir")? s:recorddir : "").">")
-endfun
-
-" ---------------------------------------------------------------------
-" s:RecordInFile: {{{2
-fun! s:RecordInFile(home)
-" call Dfunc("s:RecordInFile()")
- if exists("g:vimball_norecord")
-" call Dret("s:RecordInFile : g:vimball_norecord")
- return
- endif
-
- if exists("s:recordfile") || exists("s:recorddir")
- let curdir= getcwd()
- call s:ChgDir(a:home)
- keepalt keepjumps 1split
-
- let cmd= expand("%:tr").": "
-" call Decho("cmd<".cmd.">")
-
- sil! keepalt keepjumps e .VimballRecord
- setlocal ma
- $
- if exists("s:recordfile") && exists("s:recorddir")
- let cmd= cmd.s:recordfile."|".s:recorddir
- elseif exists("s:recorddir")
- let cmd= cmd.s:recorddir
- elseif exists("s:recordfile")
- let cmd= cmd.s:recordfile
- else
-" call Dret("s:RecordInFile : neither recordfile nor recorddir exist")
- return
- endif
-" call Decho("cmd<".cmd.">")
-
- " put command into buffer, write .VimballRecord `file
- keepalt keepjumps put=cmd
- sil! keepalt keepjumps g/^\s*$/d
- sil! keepalt keepjumps wq!
- call s:ChgDir(curdir)
-
- if exists("s:recorddir")
-" call Decho("unlet s:recorddir<".s:recorddir.">")
- unlet s:recorddir
- endif
- if exists("s:recordfile")
-" call Decho("unlet s:recordfile<".s:recordfile.">")
- unlet s:recordfile
- endif
- else
-" call Decho("s:record[file|dir] doesn't exist")
- endif
-
-" call Dret("s:RecordInFile")
-endfun
-
-" ---------------------------------------------------------------------
-" vimball#VimballHome: determine/get home directory path (usually from rtp) {{{2
-fun! vimball#VimballHome()
-" call Dfunc("vimball#VimballHome()")
- if exists("g:vimball_home")
- let home= g:vimball_home
- else
- " go to vim plugin home
- for home in split(&rtp,',') + ['']
- if isdirectory(home) && filewritable(home) | break | endif
- let basehome= substitute(home,'[/\\]\.vim$','','')
- if isdirectory(basehome) && filewritable(basehome)
- let home= basehome."/.vim"
- break
- endif
- endfor
- if home == ""
- " just pick the first directory
- let home= substitute(&rtp,',.*$','','')
- endif
- if (has("win32") || has("win95") || has("win64") || has("win16"))
- let home= substitute(home,'/','\\','g')
- endif
- endif
- " insure that the home directory exists
-" call Decho("picked home<".home.">")
- if !isdirectory(home)
- if exists("g:vimball_mkdir")
-" call Decho("home<".home."> isn't a directory -- making it now with g:vimball_mkdir<".g:vimball_mkdir.">")
-" call Decho("system(".g:vimball_mkdir." ".shellescape(home).")")
- call system(g:vimball_mkdir." ".shellescape(home))
- else
-" call Decho("home<".home."> isn't a directory -- making it now with mkdir()")
- call mkdir(home)
- endif
- endif
-" call Dret("vimball#VimballHome <".home.">")
- return home
-endfun
-
-" ---------------------------------------------------------------------
-" vimball#SaveSettings: {{{2
-fun! vimball#SaveSettings()
-" call Dfunc("SaveSettings()")
- let s:makeep = getpos("'a")
- let s:regakeep= @a
- if exists("+acd")
- let s:acdkeep = &acd
- endif
- let s:eikeep = &ei
- let s:fenkeep = &l:fen
- let s:hidkeep = &hidden
- let s:ickeep = &ic
- let s:lzkeep = &lz
- let s:pmkeep = &pm
- let s:repkeep = &report
- let s:vekeep = &ve
- let s:ffkeep = &l:ff
- let s:swfkeep = &l:swf
- if exists("+acd")
- setlocal ei=all ve=all noacd nofen noic report=999 nohid bt= ma lz pm= ff=unix noswf
- else
- setlocal ei=all ve=all nofen noic report=999 nohid bt= ma lz pm= ff=unix noswf
- endif
- " vimballs should be in unix format
- setlocal ff=unix
-" call Dret("SaveSettings")
-endfun
-
-" ---------------------------------------------------------------------
-" vimball#RestoreSettings: {{{2
-fun! vimball#RestoreSettings()
-" call Dfunc("RestoreSettings()")
- let @a = s:regakeep
- if exists("+acd")
- let &acd = s:acdkeep
- endif
- let &l:fen = s:fenkeep
- let &hidden = s:hidkeep
- let &ic = s:ickeep
- let &lz = s:lzkeep
- let &pm = s:pmkeep
- let &report = s:repkeep
- let &ve = s:vekeep
- let &ei = s:eikeep
- let &l:ff = s:ffkeep
- if s:makeep[0] != 0
- " restore mark a
-" call Decho("restore mark-a: makeep=".string(makeep))
- call setpos("'a",s:makeep)
- endif
- if exists("+acd")
- unlet s:acdkeep
- endif
- unlet s:regakeep s:eikeep s:fenkeep s:hidkeep s:ickeep s:repkeep s:vekeep s:makeep s:lzkeep s:pmkeep s:ffkeep
-" call Dret("RestoreSettings")
-endfun
-
-let &cpo = s:keepcpo
-unlet s:keepcpo
-
-" ---------------------------------------------------------------------
-" Modelines: {{{1
-" vim: fdm=marker
diff --git a/runtime/pack/dist/opt/vimball/doc/vimball.txt b/runtime/pack/dist/opt/vimball/doc/vimball.txt
deleted file mode 100644
index 602fe85954..0000000000
--- a/runtime/pack/dist/opt/vimball/doc/vimball.txt
+++ /dev/null
@@ -1,273 +0,0 @@
-*vimball.txt* For Vim version 7.4. Last change: 2012 Jan 17
-
- ----------------
- Vimball Archiver
- ----------------
-
-Author: Charles E. Campbell, Jr. <NdrOchip@ScampbellPfamily.AbizM>
- (remove NOSPAM from Campbell's email first)
-Copyright: (c) 2004-2012 by Charles E. Campbell, Jr. *Vimball-copyright*
- The VIM LICENSE (see |copyright|) applies to the files in this
- package, including vimballPlugin.vim, vimball.vim, and pi_vimball.txt.
- except use "vimball" instead of "VIM". Like anything else that's free,
- vimball.vim and its associated files are provided *as is* and comes with
- no warranty of any kind, either expressed or implied. No guarantees
- of merchantability. No guarantees of suitability for any purpose. By
- using this plugin, you agree that in no event will the copyright
- holder be liable for any damages resulting from the use of this
- software. Use at your own risk!
-
-==============================================================================
-1. Contents *vba* *vimball* *vimball-contents*
-
- 1. Contents......................................: |vimball-contents|
- 2. Vimball Introduction..........................: |vimball-intro|
- 3. Vimball Manual................................: |vimball-manual|
- MkVimball.....................................: |:MkVimball|
- UseVimball....................................: |:UseVimball|
- RmVimball.....................................: |:RmVimball|
- 4. Vimball History...............................: |vimball-history|
-
-
-==============================================================================
-2. Vimball Introduction *vimball-intro*
-
- Vimball is intended to make life simpler for users of plugins. All
- a user needs to do with a vimball is: >
- vim someplugin.vba
- :so %
- :q
-< and the plugin and all its components will be installed into their
- appropriate directories. Note that one doesn't need to be in any
- particular directory when one does this. Plus, any help for the
- plugin will also be automatically installed.
-
- If a user has decided to use the AsNeeded plugin, vimball is smart
- enough to put scripts nominally intended for .vim/plugin/ into
- .vim/AsNeeded/ instead.
-
- Removing a plugin that was installed with vimball is really easy: >
- vim
- :RmVimball someplugin
-< This operation is not at all easy for zips and tarballs, for example.
-
- Vimball examines the user's |'runtimepath'| to determine where to put
- the scripts. The first directory mentioned on the runtimepath is
- usually used if possible. Use >
- :echo &rtp
-< to see that directory.
-
-
-==============================================================================
-3. Vimball Manual *vimball-manual*
-
-MAKING A VIMBALL *:MkVimball*
- :[range]MkVimball[!] filename [path]
-
- The range is composed of lines holding paths to files to be included
- in your new vimball, omitting the portion of the paths that is
- normally specified by the runtimepath (|'rtp'|). As an example: >
- plugin/something.vim
- doc/something.txt
-< using >
- :[range]MkVimball filename
-<
- on this range of lines will create a file called "filename.vba" which
- can be used by Vimball.vim to re-create these files. If the
- "filename.vba" file already exists, then MkVimball will issue a
- warning and not create the file. Note that these paths are relative
- to your .vim (vimfiles) directory, and the files should be in that
- directory. The vimball plugin normally uses the first |'runtimepath'|
- directory that exists as a prefix; don't use absolute paths, unless
- the user has specified such a path.
-
- If you use the exclamation point (!), then MkVimball will create the
- "filename.vba" file, overwriting it if it already exists. This
- behavior resembles that for |:w|.
-
- If you wish to force slashes into the filename, that can also be done
- by using the exclamation mark (ie. :MkVimball! path/filename).
-
- The tip at https://vim.wikia.com/wiki/Using_VimBall_with_%27Make%27
- has a good idea on how to automate the production of vimballs using
- make.
-
-
-MAKING DIRECTORIES VIA VIMBALLS *g:vimball_mkdir*
-
- First, the |mkdir()| command is tried (not all systems support it).
-
- If it doesn't exist, then if g:vimball_mkdir doesn't exist, it is set
- as follows: >
- |g:netrw_local_mkdir|, if it exists
- "mkdir" , if it is executable
- "makedir" , if it is executable
- Otherwise , it is undefined.
-< One may explicitly specify the directory making command using
- g:vimball_mkdir. This command is used to make directories that
- are needed as indicated by the vimball.
-
-
-CONTROLLING THE VIMBALL EXTRACTION DIRECTORY *g:vimball_home*
-
- You may override the use of the |'runtimepath'| by specifying a
- variable, g:vimball_home.
-
- *vimball-extract*
- vim filename.vba
-
- Simply editing a Vimball will cause Vimball.vim to tell the user to
- source the file to extract its contents.
-
- Extraction will only proceed if the first line of a putative vimball
- file holds the "Vimball Archiver by Charles E. Campbell, Jr., Ph.D."
- line.
-
-LISTING FILES IN A VIMBALL *:VimballList*
-
- :VimballList
-
- This command will tell Vimball to list the files in the archive, along
- with their lengths in lines.
-
-MANUALLY INVOKING VIMBALL EXTRACTION *:UseVimball*
-
- :UseVimball [path]
-
- This command is contained within the vimball itself; it invokes the
- vimball#Vimball() routine which is responsible for unpacking the
- vimball. One may choose to execute it by hand instead of sourcing
- the vimball; one may also choose to specify a path for the
- installation, thereby overriding the automatic choice of the first
- existing directory on the |'runtimepath'|.
-
-REMOVING A VIMBALL *:RmVimball*
-
- :RmVimball vimballfile [path]
-
- This command removes all files generated by the specified vimball
- (but not any directories it may have made). One may choose a path
- for de-installation, too (see |'runtimepath'|); otherwise, the
- default is the first existing directory on the |'runtimepath'|.
- To implement this, a file (.VimballRecord) is made in that directory
- containing a record of what files need to be removed for all vimballs
- used thus far.
-
-PREVENTING LOADING
-
- If for some reason you don't want to be able to extract plugins
- using vimballs: you may prevent the loading of vimball.vim by
- putting the following two variables in your <.vimrc>: >
-
- let g:loaded_vimballPlugin= 1
- let g:loaded_vimball = 1
-<
-WINDOWS *vimball-windows*
-
- Many vimball files are compressed with gzip. Windows, unfortunately,
- does not come provided with a tool to decompress gzip'ped files.
- Fortunately, there are a number of tools available for Windows users
- to un-gzip files:
->
- Item Tool/Suite Free Website
- ---- ---------- ---- -------
- 7zip tool y https://www.7-zip.org/
- Winzip tool n https://www.winzip.com/downwz.htm
- unxutils suite y https://unxutils.sourceforge.net/
- cygwin suite y https://www.cygwin.com/
- GnuWin32 suite y https://gnuwin32.sourceforge.net/
- MinGW suite y https://www.mingw.org/
-<
-
-==============================================================================
-4. Vimball History *vimball-history* {{{1
-
- 34 : Sep 22, 2011 * "UseVimball path" now supports a non-full path by
- prepending the current directory to it.
- 33 : Apr 02, 2011 * Gave priority to *.vmb over *.vba
- * Changed silent! to sil! (shorter)
- * Safed |'swf'| setting (during vimball extraction,
- its now turned off)
- 32 : May 19, 2010 * (Christian Brabandt) :so someplugin.vba and
- :so someplugin.vba.gz (and the other supported
- compression types) now works
- * (Jan Steffens) added support for xz compression
- * fenc extraction was erroneously picking up the
- end of the line number when no file encoding
- was present. Fixed.
- * By request, beginning the switchover from the vba
- extension to vmb. Currently both are supported;
- MkVimball, however, now will create *.vmb files.
- Feb 11, 2011 * motoyakurotsu reported an error with vimball's
- handling of zero-length files
- 30 : Dec 08, 2008 * fnameescape() inserted to protect error
- messaging using corrupted filenames from
- causing problems
- * RmVimball supports filenames that would
- otherwise be considered to have "magic"
- characters (ie. Abc[1].vba)
- Feb 18, 2009 * s:Escape(), g:vimball_shq, and g:netrw_shq
- removed (shellescape() used directly)
- Oct 05, 2009 * (Nikolai Weibull) suggested that MkVimball
- be allowed to use slashes in the filename.
- 26 : May 27, 2008 * g:vimball_mkdir usage installed. Makes the
- $HOME/.vim (or $HOME\vimfiles) directory if
- necessary.
- May 30, 2008 * (tnx to Bill McCarthy) found and fixed a bug:
- vimball wasn't updating plugins to AsNeeded/
- when it should
- 25 : Mar 24, 2008 * changed vimball#Vimball() to recognize doc/*.??x
- files as help files, too.
- Apr 18, 2008 * RmVimball command is now protected by saving and
- restoring settings -- in particular, acd was
- causing problems as reported by Zhang Shuhan
- 24 : Nov 15, 2007 * g:vimball_path_escape used by s:Path() to
- prevent certain characters from causing trouble
- (defunct: |fnameescape()| and |shellescape()|
- now used instead)
- 22 : Mar 21, 2007 * uses setlocal instead of set during BufEnter
- 21 : Nov 27, 2006 * (tnx to Bill McCarthy) vimball had a header
- handling problem and it now changes \s to /s
- 20 : Nov 20, 2006 * substitute() calls have all had the 'e' flag
- removed.
- 18 : Aug 01, 2006 * vimballs now use folding to easily display their
- contents.
- * if a user has AsNeeded/somefile, then vimball
- will extract plugin/somefile to the AsNeeded/
- directory
- 17 : Jun 28, 2006 * changes all \s to /s internally for Windows
- 16 : Jun 15, 2006 * A. Mechelynck's idea to allow users to specify
- installation root paths implemented for
- UseVimball, MkVimball, and RmVimball.
- * RmVimball implemented
- 15 : Jun 13, 2006 * bugfix
- 14 : May 26, 2006 * bugfixes
- 13 : May 01, 2006 * exists("&acd") used to determine if the acd
- option exists
- 12 : May 01, 2006 * bugfix - the |'acd'| option is not always defined
- 11 : Apr 27, 2006 * VimballList would create missing subdirectories that
- the vimball specified were needed. Fixed.
- 10 : Apr 27, 2006 * moved all setting saving/restoration to a pair of
- functions. Included some more settings in them
- which frequently cause trouble.
- 9 : Apr 26, 2006 * various changes to support Windows' predilection
- for backslashes and spaces in file and directory
- names.
- 7 : Apr 25, 2006 * bypasses foldenable
- * uses more exe and less norm! (:yank :put etc)
- * does better at insuring a "Press ENTER" prompt
- appears to keep its messages visible
- 4 : Mar 31, 2006 * BufReadPost seems to fire twice; BufReadEnter
- only fires once, so the "Source this file..."
- message is now issued only once.
- 3 : Mar 20, 2006 * removed query, now requires sourcing to be
- extracted (:so %). Message to that effect
- included.
- * :VimballList now shows files that would be
- extracted.
- 2 : Mar 20, 2006 * query, :UseVimball included
- 1 : Mar 20, 2006 * initial release
-
-
-==============================================================================
-vim:tw=78:ts=8:ft=help:fdm=marker
diff --git a/runtime/pack/dist/opt/vimball/plugin/vimballPlugin.vim b/runtime/pack/dist/opt/vimball/plugin/vimballPlugin.vim
deleted file mode 100644
index d7473a0296..0000000000
--- a/runtime/pack/dist/opt/vimball/plugin/vimballPlugin.vim
+++ /dev/null
@@ -1,43 +0,0 @@
-" vimballPlugin : construct a file containing both paths and files
-" Author: Charles E. Campbell
-" Copyright: (c) 2004-2014 by Charles E. Campbell
-" The VIM LICENSE applies to Vimball.vim, and Vimball.txt
-" (see |copyright|) except use "Vimball" instead of "Vim".
-" No warranty, express or implied.
-" *** *** Use At-Your-Own-Risk! *** ***
-"
-" (Rom 2:1 WEB) Therefore you are without excuse, O man, whoever you are who
-" judge. For in that which you judge another, you condemn yourself. For
-" you who judge practice the same things.
-" GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim
-
-" ---------------------------------------------------------------------
-" Load Once: {{{1
-if &cp || exists("g:loaded_vimballPlugin")
- finish
-endif
-let g:loaded_vimballPlugin = "v37"
-let s:keepcpo = &cpo
-set cpo&vim
-
-" ------------------------------------------------------------------------------
-" Public Interface: {{{1
-com! -range -complete=file -nargs=+ -bang MkVimball call vimball#MkVimball(<line1>,<line2>,<bang>0,<f-args>)
-com! -nargs=? -complete=dir UseVimball call vimball#Vimball(1,<f-args>)
-com! -nargs=0 VimballList call vimball#Vimball(0)
-com! -nargs=* -complete=dir RmVimball call vimball#SaveSettings()|call vimball#RmVimball(<f-args>)|call vimball#RestoreSettings()
-augroup Vimball
- au!
- au BufEnter *.vba,*.vba.gz,*.vba.bz2,*.vba.zip,*.vba.xz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
- au SourceCmd *.vba.gz,*.vba.bz2,*.vba.zip,*.vba.xz let s:origfile=expand("%")|if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|endif|call vimball#Decompress(expand("<amatch>"))|so %|if s:origfile!=expand("<afile>")|close|endif
- au SourceCmd *.vba if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|call vimball#Vimball(1)|close|else|call vimball#Vimball(1)|endif
- au BufEnter *.vmb,*.vmb.gz,*.vmb.bz2,*.vmb.zip,*.vmb.xz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif
- au SourceCmd *.vmb.gz,*.vmb.bz2,*.vmb.zip,*.vmb.xz let s:origfile=expand("%")|if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|endif|call vimball#Decompress(expand("<amatch>"))|so %|if s:origfile!=expand("<afile>")|close|endif
- au SourceCmd *.vmb if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|call vimball#Vimball(1)|close|else|call vimball#Vimball(1)|endif
-augroup END
-
-" =====================================================================
-" Restoration And Modelines: {{{1
-" vim: fdm=marker
-let &cpo= s:keepcpo
-unlet s:keepcpo
diff --git a/runtime/plugin/health.vim b/runtime/plugin/health.vim
deleted file mode 100644
index 66ae8fb239..0000000000
--- a/runtime/plugin/health.vim
+++ /dev/null
@@ -1 +0,0 @@
-autocmd CmdUndefined CheckHealth checkhealth
diff --git a/runtime/plugin/tarPlugin.vim b/runtime/plugin/tarPlugin.vim
index d55492a93e..384a3ed823 100644
--- a/runtime/plugin/tarPlugin.vim
+++ b/runtime/plugin/tarPlugin.vim
@@ -47,7 +47,6 @@ augroup tar
au BufReadCmd *.tar.zst call tar#Browse(expand("<amatch>"))
au BufReadCmd *.tzs call tar#Browse(expand("<amatch>"))
augroup END
-com! -nargs=? -complete=file Vimuntar call tar#Vimuntar(<q-args>)
" ---------------------------------------------------------------------
" Restoration And Modelines: {{{1
diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua
index 367ce60765..e2ab70eca2 100644
--- a/scripts/gen_help_html.lua
+++ b/scripts/gen_help_html.lua
@@ -491,7 +491,7 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
return '' -- Discard common "noise" lines.
end
-- XXX: Avoid newlines (too much whitespace) after block elements in old (preformatted) layout.
- local div = opt.old and root:child(0) and vim.tbl_contains({'column_heading', 'h1', 'h2', 'h3'}, root:child(0):type())
+ local div = opt.old and root:child(0) and vim.list_contains({'column_heading', 'h1', 'h2', 'h3'}, root:child(0):type())
return string.format('%s%s', div and trim(text) or text, div and '' or '\n')
elseif node_name == 'line_li' then
local sib = root:prev_sibling()
@@ -522,7 +522,7 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
s = fix_tab_after_conceal(s, node_text(root:next_sibling()))
end
return s
- elseif vim.tbl_contains({'codespan', 'keycode'}, node_name) then
+ elseif vim.list_contains({'codespan', 'keycode'}, node_name) then
if root:has_error() then
return text
end
@@ -554,7 +554,7 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
if root:has_error() then
return text
end
- local in_heading = vim.tbl_contains({'h1', 'h2', 'h3'}, parent)
+ local in_heading = vim.list_contains({'h1', 'h2', 'h3'}, parent)
local cssclass = (not in_heading and get_indent(node_text()) > 8) and 'help-tag-right' or 'help-tag'
local tagname = node_text(root:child(1), false)
if vim.tbl_count(stats.first_tags) < 2 then
@@ -601,7 +601,7 @@ local function get_helpfiles(include)
for f, type in vim.fs.dir(dir) do
if (vim.endswith(f, '.txt')
and type == 'file'
- and (not include or vim.tbl_contains(include, f))) then
+ and (not include or vim.list_contains(include, f))) then
local fullpath = vim.fn.fnamemodify(('%s/%s'):format(dir, f), ':p')
table.insert(rv, fullpath)
end
diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua
index 92b07c4f73..f9a4631b7e 100644
--- a/scripts/lintcommit.lua
+++ b/scripts/lintcommit.lua
@@ -51,7 +51,7 @@ local function validate_commit(commit_message)
return nil
end
- local commit_split = vim.split(commit_message, ":")
+ local commit_split = vim.split(commit_message, ":", {plain = true})
-- Return nil if the type is vim-patch since most of the normal rules don't
-- apply.
if commit_split[1] == "vim-patch" then
@@ -63,13 +63,22 @@ local function validate_commit(commit_message)
return [[Commit message is too long, a maximum of 80 characters is allowed.]]
end
+ local before_colon = commit_split[1]
- if vim.tbl_count(commit_split) < 2 then
+ local after_idx = 2
+ if before_colon:match('^[^%(]*%([^%)]*$') then
+ -- Need to find the end of commit scope when commit scope contains colons.
+ while after_idx <= vim.tbl_count(commit_split) do
+ after_idx = after_idx + 1
+ if commit_split[after_idx - 1]:find(')') then
+ break
+ end
+ end
+ end
+ if after_idx > vim.tbl_count(commit_split) then
return [[Commit message does not include colons.]]
end
-
- local before_colon = commit_split[1]
- local after_colon = commit_split[#commit_split]
+ local after_colon = commit_split[after_idx]
-- Check if commit introduces a breaking change.
if vim.endswith(before_colon, "!") then
@@ -77,7 +86,7 @@ local function validate_commit(commit_message)
end
-- Check if type is correct
- local type = vim.split(before_colon, "%(")[1]
+ local type = vim.split(before_colon, "(", {plain = true})[1]
local allowed_types = {'build', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'test', 'vim-patch'}
if not vim.tbl_contains(allowed_types, type) then
return string.format(
@@ -234,7 +243,16 @@ function M._test()
['ci: Capitalized first word'] = false,
['ci: UPPER_CASE First Word'] = true,
['unknown: using unknown type'] = false,
+ ['feat: foo:bar'] = true,
+ ['feat(something): foo:bar'] = true,
['feat(:grep): read from pipe'] = true,
+ ['feat(:grep/:make): read from pipe'] = true,
+ ['feat(:grep): foo:bar'] = true,
+ ['feat(:grep/:make): foo:bar'] = true,
+ ['feat(:grep)'] = false,
+ ['feat(:grep/:make)'] = false,
+ ['feat(:grep'] = false,
+ ['feat(:grep/:make'] = false,
['ci: you\'re saying this commit message just goes on and on and on and on and on and on for way too long?'] = false,
}
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index 20e337afcb..385005a442 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -212,7 +212,7 @@ preprocess_patch() {
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<\%('"${na_src_testdir}"'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove testdir/test_*.vim files
- local na_src_testdir='balloon.*\|channel.*\|crypt\.vim\|cscope\.vim\|gui.*\|hardcopy\.vim\|job_fails\.vim\|json\.vim\|mzscheme\.vim\|netbeans.*\|paste\.vim\|popupwin.*\|restricted\.vim\|shortpathname\.vim\|tcl\.vim\|terminal.*\|xxd\.vim'
+ local na_src_testdir='balloon.*\|behave\.vim\|channel.*\|crypt\.vim\|cscope\.vim\|gui.*\|hardcopy\.vim\|job_fails\.vim\|json\.vim\|mzscheme\.vim\|netbeans.*\|paste\.vim\|popupwin.*\|restricted\.vim\|shortpathname\.vim\|tcl\.vim\|terminal.*\|xxd\.vim'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<test_\%('"${na_src_testdir}"'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove version.c #7555
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 2438a5cf1d..208aa165c9 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -172,7 +172,8 @@ Object nvim_eval(String expr, Error *err)
int ok;
TRY_WRAP(err, {
- ok = eval0(expr.data, &rettv, NULL, true);
+ ok = eval0(expr.data, &rettv, NULL, &EVALARG_EVALUATE);
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
});
if (!ERROR_SET(err)) {
@@ -290,10 +291,11 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
switch (dict.type) {
case kObjectTypeString:
try_start();
- if (eval0(dict.data.string.data, &rettv, NULL, true) == FAIL) {
+ if (eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE) == FAIL) {
api_set_error(err, kErrorTypeException,
"Failed to evaluate dict expression");
}
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
if (try_end(err)) {
return rv;
}
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 23bd5d2f48..c267fee39a 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -108,8 +108,8 @@
/// The default value for floats are 50. In general, values below 100 are
/// recommended, unless there is a good reason to overshadow builtin
/// elements.
-/// - style: Configure the appearance of the window. Currently only takes
-/// one non-empty value:
+/// - style: (optional) Configure the appearance of the window. Currently
+/// only supports one value:
/// - "minimal" Nvim will display the window with many UI options
/// disabled. This is useful when displaying a temporary
/// float where the text should not be edited. Disables
@@ -222,9 +222,11 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
win_config_float(win, fconfig);
win->w_pos_changed = true;
}
- if (fconfig.style == kWinStyleMinimal) {
- win_set_minimal_style(win);
- didset_window_options(win, true);
+ if (HAS_KEY(config->style)) {
+ if (fconfig.style == kWinStyleMinimal) {
+ win_set_minimal_style(win);
+ didset_window_options(win, true);
+ }
}
}
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index f1ce919942..726344a42b 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1421,6 +1421,8 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
aco->save_curwin_handle = curwin->handle;
aco->save_curbuf = curbuf;
aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle;
+ aco->save_State = State;
+
if (win != NULL) {
// There is a window for "buf" in the current tab page, make it the
// curwin. This is preferred, it has the least side effects (esp. if
@@ -1497,6 +1499,10 @@ void aucmd_restbuf(aco_save_T *aco)
win_found:
// May need to stop Insert mode if we were in a prompt buffer.
leaving_window(curwin);
+ // Do not stop Insert mode when already in Insert mode before.
+ if (aco->save_State & MODE_INSERT) {
+ stop_insert_mode = false;
+ }
// Remove the window.
win_remove(curwin, NULL);
pmap_del(handle_T)(&window_handles, curwin->handle);
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index 791b589167..6dbd18ba7c 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -32,6 +32,7 @@ typedef struct {
bufref_T new_curbuf; ///< new curbuf
char *globaldir; ///< saved value of globaldir
bool save_VIsual_active; ///< saved VIsual_active
+ int save_State; ///< saved State
} aco_save_T;
typedef struct AutoCmd_S AutoCmd;
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 774e16f73d..f2776191b7 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -2104,10 +2104,6 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
xp->xp_context = EXPAND_CHECKHEALTH;
xp->xp_pattern = (char *)arg;
break;
- case CMD_behave:
- xp->xp_context = EXPAND_BEHAVE;
- xp->xp_pattern = (char *)arg;
- break;
case CMD_messages:
xp->xp_context = EXPAND_MESSAGES;
@@ -2479,19 +2475,6 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
-/// ":behave {mswin,xterm}" command.
-static char *get_behave_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
-{
- if (idx == 0) {
- return "mswin";
- }
- if (idx == 1) {
- return "xterm";
- }
- return NULL;
-}
-
-/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":breakadd {expr, file, func, here}" command.
/// ":breakdel {func, file, here}" command.
static char *get_breakadd_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
@@ -2585,7 +2568,6 @@ static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches
int escaped;
} tab[] = {
{ EXPAND_COMMANDS, get_command_name, false, true },
- { EXPAND_BEHAVE, get_behave_arg, true, true },
{ EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
{ EXPAND_MESSAGES, get_messages_arg, true, true },
{ EXPAND_HISTORY, get_history_arg, true, true },
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 3c9a63f5a3..90723372a9 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -494,7 +494,7 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
{
// Disable error messages, a bad expression would make Vim unusable.
emsg_off++;
- typval_T *const tv = eval_expr(bp->dbg_name);
+ typval_T *const tv = eval_expr(bp->dbg_name, NULL);
emsg_off--;
return tv;
}
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 7d6349e552..de3808e4f5 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -2203,7 +2203,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
curwin = wp;
STRCPY(buf, "b:keymap_name"); // must be writable
emsg_skip++;
- s = p = eval_to_string(buf, NULL, false);
+ s = p = eval_to_string(buf, false);
emsg_skip--;
curbuf = old_curbuf;
curwin = old_curwin;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index a05fb4e08e..2268a7cd3e 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -656,7 +656,15 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T
wp->w_statuscol_line_count = wp->w_nrwidth_line_count;
set_vim_var_nr(VV_VIRTNUM, 0);
build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, stcp);
- stcp->width += stcp->truncate;
+ if (stcp->truncate > 0) {
+ // Add truncated width to avoid unnecessary redraws
+ int addwidth = MIN(stcp->truncate, MAX_NUMBERWIDTH - wp->w_nrwidth);
+ stcp->truncate = 0;
+ stcp->width += addwidth;
+ wp->w_nrwidth += addwidth;
+ wp->w_nrwidth_width = wp->w_nrwidth;
+ wp->w_valid &= ~VALID_WCOL;
+ }
}
set_vim_var_nr(VV_VIRTNUM, virtnum);
@@ -2668,7 +2676,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) {
col_attr = cuc_attr;
} else if (draw_color_col && VCOL_HLC == *color_cols) {
- col_attr = mc_attr;
+ col_attr = hl_combine_attr(wlv.line_attr_lowprio, mc_attr);
}
col_attr = hl_combine_attr(col_attr, wlv.line_attr);
@@ -2967,16 +2975,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
wlv.need_showbreak = true;
}
if (statuscol.draw) {
- if (wlv.row == startrow + wlv.filler_lines + 1
- || wlv.row == startrow + wlv.filler_lines) {
- // Re-evaluate 'statuscolumn' for the first wrapped row and non filler line
- statuscol.textp = NULL;
- } else if (statuscol.textp) {
+ if (wlv.row == startrow + wlv.filler_lines) {
+ statuscol.textp = NULL; // re-evaluate for first non-filler line
+ } else if (vim_strchr(p_cpo, CPO_NUMCOL) && wlv.row > startrow + wlv.filler_lines) {
+ statuscol.draw = false; // don't draw status column if "n" is in 'cpo'
+ } else if (wlv.row == startrow + wlv.filler_lines + 1) {
+ statuscol.textp = NULL; // re-evaluate for first wrapped line
+ } else {
// Draw the already built 'statuscolumn' on the next wrapped or filler line
statuscol.textp = statuscol.text;
statuscol.hlrecp = statuscol.hlrec;
- } // Fall back to default columns if the 'n' flag isn't in 'cpo'
- statuscol.draw = vim_strchr(p_cpo, CPO_NUMCOL) == NULL;
+ }
}
wlv.filler_todo--;
virt_line_offset = -1;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ff33fdeccb..6a8b24ceed 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -692,21 +692,35 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
}
+void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
+{
+ *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE };
+ if (eap != NULL) {
+ if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
+ evalarg->eval_getline = eap->getline;
+ evalarg->eval_cookie = eap->cookie;
+ }
+ }
+}
+
/// 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)
+int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
{
typval_T tv;
bool retval = false;
+ evalarg_T evalarg;
+
+ fill_evalarg_from_eap(&evalarg, eap, skip);
if (skip) {
emsg_skip++;
}
- if (eval0(arg, &tv, nextcmd, !skip) == FAIL) {
+ if (eval0(arg, &tv, eap, &evalarg) == FAIL) {
*error = true;
} else {
*error = false;
@@ -718,19 +732,23 @@ int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
if (skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
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)
+static int eval1_emsg(char **arg, typval_T *rettv, exarg_T *eap)
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;
+ evalarg_T evalarg;
+
+ fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
- const int ret = eval1(arg, rettv, evaluate);
+ const int ret = eval1(arg, rettv, &evalarg);
if (ret == FAIL) {
// Report the invalid expression unless the expression evaluation has
// been cancelled due to an aborting error, an interrupt, or an
@@ -742,6 +760,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate)
semsg(_(e_invexpr2), start);
}
}
+ clear_evalarg(&evalarg, eap);
return ret;
}
@@ -786,7 +805,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r
return FAIL;
}
s = skipwhite(s);
- if (eval1_emsg(&s, rettv, true) == FAIL) {
+ if (eval1_emsg(&s, rettv, NULL) == FAIL) {
return FAIL;
}
if (*skipwhite(s) != NUL) { // check for trailing chars after expr
@@ -817,22 +836,23 @@ bool eval_expr_to_bool(const typval_T *expr, bool *error)
/// 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)
+char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{
typval_T tv;
char *retval;
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, skip);
if (skip) {
emsg_skip++;
}
- if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) {
+ if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) {
retval = NULL;
} else {
retval = xstrdup(tv_get_string(&tv));
@@ -841,6 +861,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
if (skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
return retval;
}
@@ -848,12 +869,24 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
/// Skip over an expression at "*pp".
///
/// @return FAIL for an error, OK otherwise.
-int skip_expr(char **pp)
+int skip_expr(char **pp, evalarg_T *const evalarg)
{
- typval_T rettv;
+ const int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+
+ // Don't evaluate the expression.
+ if (evalarg != NULL) {
+ evalarg->eval_flags &= ~EVAL_EVALUATE;
+ }
*pp = skipwhite(*pp);
- return eval1(pp, &rettv, false);
+ typval_T rettv;
+ int res = eval1(pp, &rettv, NULL);
+
+ if (evalarg != NULL) {
+ evalarg->eval_flags = save_flags;
+ }
+
+ return res;
}
/// Top level evaluation function, returning a string.
@@ -862,13 +895,13 @@ int skip_expr(char **pp)
/// a Float to a String.
///
/// @return pointer to allocated memory, or NULL for failure.
-char *eval_to_string(char *arg, char **nextcmd, bool convert)
+char *eval_to_string(char *arg, bool convert)
{
typval_T tv;
char *retval;
garray_T ga;
- if (eval0(arg, &tv, nextcmd, true) == FAIL) {
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = NULL;
} else {
if (convert && tv.v_type == VAR_LIST) {
@@ -890,6 +923,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
}
tv_clear(&tv);
}
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
return retval;
}
@@ -898,7 +932,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
-char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
+char *eval_to_string_safe(char *arg, int use_sandbox)
{
char *retval;
funccal_entry_T funccal_entry;
@@ -908,7 +942,7 @@ char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
sandbox++;
}
textlock++;
- retval = eval_to_string(arg, nextcmd, false);
+ retval = eval_to_string(arg, false);
if (use_sandbox) {
sandbox--;
}
@@ -929,7 +963,7 @@ varnumber_T eval_to_number(char *expr)
emsg_off++;
- if (eval1(&p, &rettv, true) == FAIL) {
+ if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) {
retval = -1;
} else {
retval = tv_get_number_chk(&rettv, NULL);
@@ -944,12 +978,18 @@ varnumber_T eval_to_number(char *expr)
///
/// @return an allocated typval_T with the result or
/// NULL when there is an error.
-typval_T *eval_expr(char *arg)
+typval_T *eval_expr(char *arg, exarg_T *eap)
{
typval_T *tv = xmalloc(sizeof(*tv));
- if (eval0(arg, tv, NULL, true) == FAIL) {
+ evalarg_T evalarg;
+
+ fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
+
+ if (eval0(arg, tv, eap, &evalarg) == FAIL) {
XFREE_CLEAR(tv);
}
+
+ clear_evalarg(&evalarg, eap);
return tv;
}
@@ -1024,7 +1064,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
emsg_off++;
}
- if (eval1(&p, &rettv, true) == OK) {
+ if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) {
if (rettv.v_type != VAR_LIST) {
tv_clear(&rettv);
} else {
@@ -1171,7 +1211,7 @@ int eval_foldexpr(char *arg, int *cp)
}
textlock++;
*cp = NUL;
- if (eval0(arg, &tv, NULL, true) == FAIL) {
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = 0;
} else {
// If the result is a number, just return the number.
@@ -1195,6 +1235,7 @@ int eval_foldexpr(char *arg, int *cp)
sandbox--;
}
textlock--;
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
return (int)retval;
}
@@ -1346,7 +1387,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
empty1 = true;
} else {
empty1 = false;
- if (eval1(&p, &var1, true) == FAIL) { // Recursive!
+ if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive!
return NULL;
}
if (!tv_check_str(&var1)) {
@@ -1380,7 +1421,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_empty2 = true;
} else {
lp->ll_empty2 = false;
- if (eval1(&p, &var2, true) == FAIL) { // Recursive!
+ // Recursive!
+ if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) {
tv_clear(&var1);
return NULL;
}
@@ -1632,8 +1674,8 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
// handle +=, -=, *=, /=, %= and .=
di = NULL;
- if (get_var_tv(lp->ll_name, (int)strlen(lp->ll_name),
- &tv, &di, true, false) == OK) {
+ if (eval_variable(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)))
@@ -1776,12 +1818,13 @@ notify:
/// @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)
+void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const evalarg)
{
forinfo_T *fi = xcalloc(1, sizeof(forinfo_T));
const char *expr;
typval_T tv;
list_T *l;
+ const bool skip = !(evalarg->eval_flags & EVAL_EVALUATE);
*errp = true; // Default: there is an error.
@@ -1791,7 +1834,8 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
}
expr = skipwhite(expr);
- if (expr[0] != 'i' || expr[1] != 'n' || !ascii_iswhite(expr[2])) {
+ if (expr[0] != 'i' || expr[1] != 'n'
+ || !(expr[2] == NUL || ascii_iswhite(expr[2]))) {
emsg(_("E690: Missing \"in\" after :for"));
return fi;
}
@@ -1799,7 +1843,8 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
if (skip) {
emsg_skip++;
}
- if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
+ expr = skipwhite(expr + 2);
+ if (eval0((char *)expr, &tv, eap, evalarg) == OK) {
*errp = false;
if (!skip) {
if (tv.v_type == VAR_LIST) {
@@ -2163,10 +2208,12 @@ int pattern_match(const char *pat, const char *text, bool ic)
/// @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)
+static int eval_func(char **const arg, evalarg_T *const evalarg, char *const name,
+ const int name_len, typval_T *const rettv, const int flags,
+ typval_T *const basetv)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 5)
{
+ const bool evaluate = flags & EVAL_EVALUATE;
char *s = name;
int len = name_len;
@@ -2190,7 +2237,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
funcexe.fe_evaluate = evaluate;
funcexe.fe_partial = partial;
funcexe.fe_basetv = basetv;
- int ret = get_func_tv(s, len, rettv, arg, &funcexe);
+ int ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
xfree(s);
@@ -2214,6 +2261,26 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
return ret;
}
+/// After using "evalarg" filled from "eap": free the memory.
+void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
+{
+ if (evalarg != NULL) {
+ if (evalarg->eval_tofree != NULL) {
+ if (eap != NULL) {
+ // We may need to keep the original command line, e.g. for
+ // ":let" it has the variable names. But we may also need the
+ // new one, "nextcmd" points into it. Keep both.
+ xfree(eap->cmdline_tofree);
+ eap->cmdline_tofree = *eap->cmdlinep;
+ *eap->cmdlinep = evalarg->eval_tofree;
+ } else {
+ xfree(evalarg->eval_tofree);
+ }
+ evalarg->eval_tofree = NULL;
+ }
+ }
+}
+
/// 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.
@@ -2223,8 +2290,10 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
/// Put the result in "rettv" when returning OK and "evaluate" is true.
/// Note: "rettv.v_lock" is not set.
///
+/// @param evalarg can be NULL, &EVALARG_EVALUATE or a pointer.
+///
/// @return OK or FAIL.
-int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
+int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
{
int ret;
char *p;
@@ -2233,7 +2302,7 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
bool end_error = false;
p = skipwhite(arg);
- ret = eval1(&p, rettv, evaluate);
+ ret = eval1(&p, rettv, evalarg);
if (ret != FAIL) {
end_error = !ends_excmd(*p);
@@ -2246,7 +2315,8 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
// 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
+ if (!aborting()
+ && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before) {
if (end_error) {
semsg(_(e_trailing_arg), p);
@@ -2256,8 +2326,9 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
}
ret = FAIL;
}
- if (nextcmd != NULL) {
- *nextcmd = check_nextcmd(p);
+
+ if (eap != NULL) {
+ eap->nextcmd = check_nextcmd(p);
}
return ret;
@@ -2265,6 +2336,7 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// Handle top level expression:
/// expr2 ? expr1 : expr1
+/// expr2 ?? expr1
///
/// "arg" must point to the first non-white of the expression.
/// "arg" is advanced to the next non-white after the recognized expression.
@@ -2272,54 +2344,89 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// Note: "rettv.v_lock" is not set.
///
/// @return OK or FAIL.
-int eval1(char **arg, typval_T *rettv, int evaluate)
+int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
-
// Get the first variable.
- if (eval2(arg, rettv, evaluate) == FAIL) {
+ if (eval2(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- if ((*arg)[0] == '?') {
+ char *p = *arg;
+ if (*p == '?') {
+ const bool op_falsy = p[1] == '?';
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
bool result = false;
if (evaluate) {
bool error = false;
- if (tv_get_number_chk(rettv, &error) != 0) {
+ if (op_falsy) {
+ result = tv2bool(rettv);
+ } else if (tv_get_number_chk(rettv, &error) != 0) {
result = true;
}
- tv_clear(rettv);
+ if (error || !op_falsy || !result) {
+ tv_clear(rettv);
+ }
if (error) {
return FAIL;
}
}
- // Get the second variable.
+ // Get the second variable. Recursive!
+ if (op_falsy) {
+ (*arg)++;
+ }
*arg = skipwhite(*arg + 1);
- if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive!
+ evalarg_used->eval_flags = (op_falsy ? !result : result)
+ ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval1(arg, &var2, evalarg_used) == FAIL) {
+ evalarg_used->eval_flags = orig_flags;
return FAIL;
}
+ if (!op_falsy || !result) {
+ *rettv = var2;
+ }
- // Check for the ":".
- if ((*arg)[0] != ':') {
- emsg(_("E109: Missing ':' after '?'"));
- if (evaluate && result) {
- tv_clear(rettv);
+ if (!op_falsy) {
+ // Check for the ":".
+ p = *arg;
+ if (*p != ':') {
+ emsg(_("E109: Missing ':' after '?'"));
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
+ evalarg_used->eval_flags = orig_flags;
+ return FAIL;
}
- return FAIL;
- }
- // Get the third variable.
- *arg = skipwhite(*arg + 1);
- if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive!
- if (evaluate && result) {
- tv_clear(rettv);
+ // Get the third variable. Recursive!
+ *arg = skipwhite(*arg + 1);
+ evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ if (eval1(arg, &var2, evalarg_used) == FAIL) {
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
+ evalarg_used->eval_flags = orig_flags;
+ return FAIL;
+ }
+ if (evaluate && !result) {
+ *rettv = var2;
}
- return FAIL;
}
- if (evaluate && !result) {
- *rettv = var2;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2333,21 +2440,29 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
/// "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)
+static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- bool error = false;
-
// Get the first variable.
- if (eval3(arg, rettv, evaluate) == FAIL) {
+ if (eval3(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- // Repeat until there is no following "||".
- bool first = true;
- bool result = false;
- while ((*arg)[0] == '|' && (*arg)[1] == '|') {
- if (evaluate && first) {
+ // Handle the "||" operator.
+ char *p = *arg;
+ if (p[0] == '|' && p[1] == '|') {
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
+ bool result = false;
+
+ if (evaluate) {
+ bool error = false;
if (tv_get_number_chk(rettv, &error) != 0) {
result = true;
}
@@ -2355,28 +2470,41 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
if (error) {
return FAIL;
}
- first = false;
}
- // Get the second variable.
- *arg = skipwhite(*arg + 2);
- if (eval3(arg, &var2, evaluate && !result) == FAIL) {
- return FAIL;
- }
+ // Repeat until there is no following "||".
+ while (p[0] == '|' && p[1] == '|') {
+ // Get the second variable.
+ *arg = skipwhite(*arg + 2);
+ evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval3(arg, &var2, evalarg_used) == FAIL) {
+ return FAIL;
+ }
- // Compute the result.
- if (evaluate && !result) {
- if (tv_get_number_chk(&var2, &error) != 0) {
- result = true;
+ // Compute the result.
+ if (evaluate && !result) {
+ bool error = false;
+ if (tv_get_number_chk(&var2, &error) != 0) {
+ result = true;
+ }
+ tv_clear(&var2);
+ if (error) {
+ return FAIL;
+ }
}
- tv_clear(&var2);
- if (error) {
- return FAIL;
+ if (evaluate) {
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = result;
}
+
+ p = *arg;
}
- if (evaluate) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = result;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2390,21 +2518,29 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
/// `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)
+static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- bool error = false;
-
// Get the first variable.
- if (eval4(arg, rettv, evaluate) == FAIL) {
+ if (eval4(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- // Repeat until there is no following "&&".
- bool first = true;
- bool result = true;
- while ((*arg)[0] == '&' && (*arg)[1] == '&') {
- if (evaluate && first) {
+ char *p = *arg;
+ // Handle the "&&" operator.
+ if (p[0] == '&' && p[1] == '&') {
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
+ bool result = true;
+
+ if (evaluate) {
+ bool error = false;
if (tv_get_number_chk(rettv, &error) == 0) {
result = false;
}
@@ -2412,28 +2548,41 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
if (error) {
return FAIL;
}
- first = false;
}
- // Get the second variable.
- *arg = skipwhite(*arg + 2);
- if (eval4(arg, &var2, evaluate && result) == FAIL) {
- return FAIL;
- }
+ // Repeat until there is no following "&&".
+ while (p[0] == '&' && p[1] == '&') {
+ // Get the second variable.
+ *arg = skipwhite(*arg + 2);
+ evalarg_used->eval_flags = result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval4(arg, &var2, evalarg_used) == FAIL) {
+ return FAIL;
+ }
- // Compute the result.
- if (evaluate && result) {
- if (tv_get_number_chk(&var2, &error) == 0) {
- result = false;
+ // Compute the result.
+ if (evaluate && result) {
+ bool error = false;
+ if (tv_get_number_chk(&var2, &error) == 0) {
+ result = false;
+ }
+ tv_clear(&var2);
+ if (error) {
+ return FAIL;
+ }
}
- tv_clear(&var2);
- if (error) {
- return FAIL;
+ if (evaluate) {
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = result;
}
+
+ p = *arg;
}
- if (evaluate) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = result;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2456,19 +2605,18 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
/// "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)
+static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
typval_T var2;
- char *p;
exprtype_T type = EXPR_UNKNOWN;
int len = 2;
// Get the first variable.
- if (eval5(arg, rettv, evaluate) == FAIL) {
+ if (eval5(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- p = *arg;
+ char *p = *arg;
switch (p[0]) {
case '=':
if (p[1] == '=') {
@@ -2528,11 +2676,11 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
// Get the second variable.
*arg = skipwhite(p + len);
- if (eval5(arg, &var2, evaluate) == FAIL) {
+ if (eval5(arg, &var2, evalarg) == FAIL) {
tv_clear(rettv);
return FAIL;
}
- if (evaluate) {
+ if (evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE)) {
const int ret = typval_compare(rettv, &var2, type, ic);
tv_clear(&var2);
@@ -2586,25 +2734,22 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2)
/// `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)
+static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- varnumber_T n1, n2;
- float_T f1 = 0, f2 = 0;
- char *p;
-
// Get the first variable.
- if (eval6(arg, rettv, evaluate, false) == FAIL) {
+ if (eval6(arg, rettv, evalarg, false) == FAIL) {
return FAIL;
}
// Repeat computing, until no '+', '-' or '.' is following.
for (;;) {
int op = (uint8_t)(**arg);
- if (op != '+' && op != '-' && op != '.') {
+ bool concat = op == '.';
+ if (op != '+' && op != '-' && !concat) {
break;
}
+ const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB))
&& (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) {
// For "list + ...", an illegal use of the first operand as
@@ -2625,7 +2770,8 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
(*arg)++;
}
*arg = skipwhite(*arg + 1);
- if (eval6(arg, &var2, evaluate, op == '.') == FAIL) {
+ typval_T var2;
+ if (eval6(arg, &var2, evalarg, op == '.') == FAIL) {
tv_clear(rettv);
return FAIL;
}
@@ -2643,7 +2789,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
tv_clear(&var2);
return FAIL;
}
- p = concat_str(s1, s2);
+ char *p = concat_str(s1, s2);
tv_clear(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
@@ -2655,6 +2801,8 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
}
} else {
bool error = false;
+ varnumber_T n1, n2;
+ float_T f1 = 0, f2 = 0;
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
@@ -2724,32 +2872,30 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
/// 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)
+static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool 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) {
+ if (eval7(arg, rettv, evalarg, want_string) == FAIL) {
return FAIL;
}
// Repeat computing, until no '*', '/' or '%' is following.
for (;;) {
- op = (uint8_t)(**arg);
+ int op = (uint8_t)(**arg);
if (op != '*' && op != '/' && op != '%') {
break;
}
+ varnumber_T n1, n2;
+ float_T f1 = 0, f2 = 0;
+ bool error = false;
+ const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
if (evaluate) {
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
@@ -2768,7 +2914,8 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
// Get the second variable.
*arg = skipwhite(*arg + 1);
- if (eval7(arg, &var2, evaluate, false) == FAIL) {
+ typval_T var2;
+ if (eval7(arg, &var2, evalarg, false) == FAIL) {
return FAIL;
}
@@ -2859,8 +3006,9 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
/// @param want_string after "." operator
///
/// @return OK or FAIL.
-static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
+static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK;
static int recurse = 0;
@@ -2901,7 +3049,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '7':
case '8':
case '9':
- ret = get_number_tv(arg, rettv, evaluate, want_string);
+ ret = eval_number(arg, rettv, evaluate, want_string);
// Apply prefixed "-" and "+" now. Matters especially when
// "->" follows.
@@ -2912,24 +3060,24 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// String constant: "string".
case '"':
- ret = get_string_tv(arg, rettv, evaluate);
+ ret = eval_string(arg, rettv, evaluate);
break;
// Literal string constant: 'str''ing'.
case '\'':
- ret = get_lit_string_tv(arg, rettv, evaluate);
+ ret = eval_lit_string(arg, rettv, evaluate);
break;
// List: [expr, expr]
case '[':
- ret = get_list_tv(arg, rettv, evaluate);
+ ret = eval_list(arg, rettv, evalarg);
break;
// Dictionary: #{key: val, key: val}
case '#':
if ((*arg)[1] == '{') {
(*arg)++;
- ret = eval_dict(arg, rettv, evaluate, true);
+ ret = eval_dict(arg, rettv, evalarg, true);
} else {
ret = NOTDONE;
}
@@ -2938,19 +3086,19 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Lambda: {arg, arg -> expr}
// Dictionary: {'key': val, 'key': val}
case '{':
- ret = get_lambda_tv(arg, rettv, evaluate);
+ ret = get_lambda_tv(arg, rettv, evalarg);
if (ret == NOTDONE) {
- ret = eval_dict(arg, rettv, evaluate, false);
+ ret = eval_dict(arg, rettv, evalarg, false);
}
break;
// Option value: &name
case '&':
- ret = get_option_tv((const char **)arg, rettv, evaluate);
+ ret = eval_option((const char **)arg, rettv, evaluate);
break;
// Environment variable: $VAR.
case '$':
- ret = get_env_tv(arg, rettv, evaluate);
+ ret = eval_env_var(arg, rettv, evaluate);
break;
// Register contents: @r.
@@ -2968,7 +3116,8 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// nested expression: (expression).
case '(':
*arg = skipwhite(*arg + 1);
- ret = eval1(arg, rettv, evaluate); // recursive!
+
+ ret = eval1(arg, rettv, evalarg); // recursive!
if (**arg == ')') {
(*arg)++;
} else if (ret == OK) {
@@ -2996,11 +3145,15 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
if (len <= 0) {
ret = FAIL;
} else {
- if (**arg == '(') { // recursive!
- ret = eval_func(arg, s, len, rettv, evaluate, NULL);
+ const int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+ if (**arg == '(') {
+ // "name(..." recursive!
+ ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
} else if (evaluate) {
- ret = get_var_tv(s, len, rettv, NULL, true, false);
+ // get value of variable
+ ret = eval_variable(s, len, rettv, NULL, true, false);
} else {
+ // skip the name
check_vars(s, (size_t)len);
ret = OK;
}
@@ -3013,7 +3166,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// 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);
+ ret = handle_subscript((const char **)arg, rettv, evalarg, true);
}
// Apply logical NOT and unary '-', from right to left, ignore '+'.
@@ -3089,10 +3242,10 @@ static int eval7_leader(typval_T *const rettv, const bool numeric_only,
/// 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,
+static int call_func_rettv(char **const arg, evalarg_T *const evalarg, 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)
+ FUNC_ATTR_NONNULL_ARG(1, 3)
{
partial_T *pt = NULL;
typval_T functv;
@@ -3124,7 +3277,7 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
funcexe.fe_selfdict = selfdict;
funcexe.fe_basetv = basetv;
const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
- arg, &funcexe);
+ arg, evalarg, &funcexe);
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
@@ -3143,16 +3296,17 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
/// @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,
+static int eval_lambda(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
const bool verbose)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
// Skip over the ->.
*arg += 2;
typval_T base = *rettv;
rettv->v_type = VAR_UNKNOWN;
- int ret = get_lambda_tv(arg, rettv, evaluate);
+ int ret = get_lambda_tv(arg, rettv, evalarg);
if (ret != OK) {
return FAIL;
} else if (**arg != '(') {
@@ -3166,7 +3320,7 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
tv_clear(rettv);
ret = FAIL;
} else {
- ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL);
+ ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, NULL);
}
// Clear the funcref afterwards, so that deleting it while
@@ -3183,10 +3337,12 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
/// @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,
+static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
const bool verbose)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
+
// Skip over the ->.
*arg += 2;
typval_T base = *rettv;
@@ -3236,9 +3392,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
rettv->vval.v_partial = vvlua_partial;
rettv->vval.v_partial->pt_refcount++;
}
- ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname);
+ ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname);
} else {
- ret = eval_func(arg, name, len, rettv, evaluate, &base);
+ ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base);
}
}
@@ -3257,8 +3413,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
/// @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)
+static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool verbose)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
bool empty1 = false;
bool empty2 = false;
ptrdiff_t len = -1;
@@ -3313,7 +3470,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
*arg = skipwhite(*arg + 1);
if (**arg == ':') {
empty1 = true;
- } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive!
+ } else if (eval1(arg, &var1, evalarg) == FAIL) { // Recursive!
return FAIL;
} else if (evaluate && !tv_check_str(&var1)) {
// Not a number or string.
@@ -3327,7 +3484,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
*arg = skipwhite(*arg + 1);
if (**arg == ']') {
empty2 = true;
- } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive!
+ } else if (eval1(arg, &var2, evalarg) == FAIL) { // Recursive!
if (!empty1) {
tv_clear(&var1);
}
@@ -3557,7 +3714,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
/// @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)
+int eval_option(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
const bool working = (**arg == '+'); // has("+option")
@@ -3620,7 +3777,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
/// Allocate a variable for a number constant. Also deals with "0z" for blob.
///
/// @return OK or FAIL.
-static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_string)
+static int eval_number(char **arg, typval_T *rettv, bool evaluate, bool want_string)
{
char *p = skipdigits(*arg + 1);
bool get_float = false;
@@ -3705,7 +3862,7 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s
/// Allocate a variable for a string constant.
///
/// @return OK or FAIL.
-static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_string(char **arg, typval_T *rettv, int evaluate)
{
char *p;
unsigned int extra = 0;
@@ -3818,7 +3975,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
if (extra != 0) {
name += extra;
if (name >= rettv->vval.v_string + len) {
- iemsg("get_string_tv() used more space than allocated");
+ iemsg("eval_string() used more space than allocated");
}
break;
}
@@ -3845,7 +4002,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
/// 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)
+static int eval_lit_string(char **arg, typval_T *rettv, int evaluate)
{
char *p;
int reduce = 0;
@@ -3929,9 +4086,11 @@ void partial_unref(partial_T *pt)
/// Allocate a variable for a List and fill it from "*arg".
///
+/// @param arg "*arg" points to the "[".
/// @return OK or FAIL.
-static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_list(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
+ const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
list_T *l = NULL;
if (evaluate) {
@@ -3941,7 +4100,7 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
*arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL) {
typval_T tv;
- if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
+ if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
goto failret;
}
if (evaluate) {
@@ -3949,14 +4108,20 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
tv_list_append_owned_tv(l, tv);
}
+ // the comma must come after the value
+ bool had_comma = **arg == ',';
+ if (had_comma) {
+ *arg = skipwhite(*arg + 1);
+ }
+
if (**arg == ']') {
break;
}
- if (**arg != ',') {
+
+ if (!had_comma) {
semsg(_("E696: Missing comma in List: %s"), *arg);
goto failret;
}
- *arg = skipwhite(*arg + 1);
}
if (**arg != ']') {
@@ -4573,11 +4738,14 @@ 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}
+///
+/// @param arg "*arg" points to the "{".
+/// @param literal true for #{key: val}
///
/// @return OK or FAIL. Returns NOTDONE for {expr}.
-static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
+static int eval_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool literal)
{
+ const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
typval_T tv;
char *key = NULL;
char *curly_expr = skipwhite(*arg + 1);
@@ -4591,7 +4759,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
// "#{abc}" is never a curly-braces expression.
if (*curly_expr != '}'
&& !literal
- && eval1(&curly_expr, &tv, false) == OK
+ && eval1(&curly_expr, &tv, NULL) == OK
&& *skipwhite(curly_expr) == '}') {
return NOTDONE;
}
@@ -4608,7 +4776,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
while (**arg != '}' && **arg != NUL) {
if ((literal
? get_literal_key(arg, &tvkey)
- : eval1(arg, &tvkey, evaluate)) == FAIL) { // recursive!
+ : eval1(arg, &tvkey, evalarg)) == FAIL) { // recursive!
goto failret;
}
if (**arg != ':') {
@@ -4626,7 +4794,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
}
*arg = skipwhite(*arg + 1);
- if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
+ if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
if (evaluate) {
tv_clear(&tvkey);
}
@@ -4649,14 +4817,19 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
}
tv_clear(&tvkey);
+ // the comma must come after the value
+ bool had_comma = **arg == ',';
+ if (had_comma) {
+ *arg = skipwhite(*arg + 1);
+ }
+
if (**arg == '}') {
break;
}
- if (**arg != ',') {
+ if (!had_comma) {
semsg(_("E722: Missing comma in Dictionary: %s"), *arg);
goto failret;
}
- *arg = skipwhite(*arg + 1);
}
if (**arg != '}') {
@@ -4713,7 +4886,7 @@ size_t string2float(const char *const text, float_T *const ret_value)
/// @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)
+static int eval_env_var(char **arg, typval_T *rettv, int evaluate)
{
(*arg)++;
char *name = *arg;
@@ -6501,15 +6674,14 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
}
char *retval = NULL;
- char *nextcmd = NULL;
*expr_start = NUL;
*expr_end = NUL;
char c1 = *in_end;
*in_end = NUL;
- char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
- if (temp_result != NULL && nextcmd == NULL) {
+ char *temp_result = eval_to_string(expr_start + 1, false);
+ if (temp_result != NULL) {
retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start)
+ (size_t)(in_end - expr_end) + 1);
STRCPY(retval, in_start);
@@ -6937,12 +7109,13 @@ int check_luafunc_name(const char *const str, const bool paren)
///
/// 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)
+int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const evalarg,
+ bool verbose)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK;
dict_T *selfdict = NULL;
const char *lua_funcname = NULL;
@@ -6971,7 +7144,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
&& !ascii_iswhite(*(*arg - 1)))
|| (**arg == '-' && (*arg)[1] == '>'))) {
if (**arg == '(') {
- ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname);
+ ret = call_func_rettv((char **)arg, evalarg, 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
@@ -6987,10 +7160,10 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
} else if (**arg == '-') {
if ((*arg)[2] == '{') {
// expr->{lambda}()
- ret = eval_lambda((char **)arg, rettv, evaluate, verbose);
+ ret = eval_lambda((char **)arg, rettv, evalarg, verbose);
} else {
// expr->name()
- ret = eval_method((char **)arg, rettv, evaluate, verbose);
+ ret = eval_method((char **)arg, rettv, evalarg, verbose);
}
} else { // **arg == '[' || **arg == '.'
tv_dict_unref(selfdict);
@@ -7002,7 +7175,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
} else {
selfdict = NULL;
}
- if (eval_index((char **)arg, rettv, evaluate, verbose) == FAIL) {
+ if (eval_index((char **)arg, rettv, evalarg, verbose) == FAIL) {
tv_clear(rettv);
ret = FAIL;
}
@@ -7374,6 +7547,9 @@ void ex_echo(exarg_T *eap)
bool need_clear = true;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
+ evalarg_T evalarg;
+
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) {
emsg_skip++;
@@ -7385,7 +7561,7 @@ void ex_echo(exarg_T *eap)
{
char *p = arg;
- if (eval1(&arg, &rettv, !eap->skip) == FAIL) {
+ if (eval1(&arg, &rettv, &evalarg) == FAIL) {
// Report the invalid expression unless the expression evaluation
// has been cancelled due to an aborting error, an interrupt, or an
// exception.
@@ -7425,6 +7601,7 @@ void ex_echo(exarg_T *eap)
arg = skipwhite(arg);
}
eap->nextcmd = check_nextcmd(arg);
+ clear_evalarg(&evalarg, eap);
if (eap->skip) {
emsg_skip--;
@@ -7463,7 +7640,7 @@ void ex_execute(exarg_T *eap)
emsg_skip++;
}
while (*arg != NUL && *arg != '|' && *arg != '\n') {
- ret = eval1_emsg(&arg, &rettv, !eap->skip);
+ ret = eval1_emsg(&arg, &rettv, eap);
if (ret == FAIL) {
break;
}
@@ -8246,14 +8423,14 @@ bool eval_has_provider(const char *feat)
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) {
+ if (eval_variable(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) {
+ if (eval_variable(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(buf) && p_lpl) {
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index aa034cb2b3..e9cdb108a8 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -266,6 +266,27 @@ typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int);
// Used for checking if local variables or arguments used in a lambda.
extern bool *eval_lavars_used;
+/// Struct passed through eval() functions.
+/// See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE.
+typedef struct {
+ int eval_flags; ///< EVAL_ flag values below
+
+ /// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
+ LineGetter eval_getline;
+ void *eval_cookie; ///< argument for eval_getline()
+
+ /// pointer to the last line obtained with getsourceline()
+ char *eval_tofree;
+} evalarg_T;
+
+/// Flag for expression evaluation.
+enum {
+ EVAL_EVALUATE = 1, ///< when missing don't actually evaluate
+};
+
+/// Passed to an eval() function to enable evaluation.
+EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL, NULL, NULL });
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h"
#endif
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7f224f371c..23d0242ce4 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1550,7 +1550,7 @@ static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
const char *const expr_start = s;
- if (s == NULL || eval1((char **)&s, rettv, true) == FAIL) {
+ if (s == NULL || eval1((char **)&s, rettv, &EVALARG_EVALUATE) == FAIL) {
if (expr_start != NULL && !aborting()) {
semsg(_(e_invexpr2), expr_start);
}
@@ -1722,7 +1722,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(exp);
}
} else if (*p == '&' || *p == '+') { // Option.
- n = (get_option_tv(&p, NULL, true) == OK);
+ n = (eval_option(&p, NULL, true) == OK);
if (*skipwhite(p) != NUL) {
n = false; // Trailing garbage.
}
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 3e67571053..91be41751e 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -4201,3 +4201,34 @@ const char *tv_get_string_buf(const typval_T *const tv, char *const buf)
return res != NULL ? res : "";
}
+
+/// Return true when "tv" is not falsy: non-zero, non-empty string, non-empty
+/// list, etc. Mostly like what JavaScript does, except that empty list and
+/// empty dictionary are false.
+bool tv2bool(const typval_T *const tv)
+{
+ switch (tv->v_type) {
+ case VAR_NUMBER:
+ return tv->vval.v_number != 0;
+ case VAR_FLOAT:
+ return tv->vval.v_float != 0.0;
+ case VAR_PARTIAL:
+ return tv->vval.v_partial != NULL;
+ case VAR_FUNC:
+ case VAR_STRING:
+ return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
+ case VAR_LIST:
+ return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
+ case VAR_DICT:
+ return tv->vval.v_dict != NULL && tv->vval.v_dict->dv_hashtab.ht_used > 0;
+ case VAR_BOOL:
+ return tv->vval.v_bool == kBoolVarTrue;
+ case VAR_SPECIAL:
+ return tv->vval.v_special == kSpecialVarNull;
+ case VAR_BLOB:
+ return tv->vval.v_blob != NULL && tv->vval.v_blob->bv_ga.ga_len > 0;
+ case VAR_UNKNOWN:
+ break;
+ }
+ return false;
+}
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index f09da6b79b..178f9fd6b6 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -71,6 +71,8 @@ static const char *e_funcref = N_("E718: Funcref required");
static const char *e_nofunc = N_("E130: Unknown function: %s");
static const char e_no_white_space_allowed_before_str_str[]
= N_("E1068: No white space allowed before '%s': %s");
+static const char e_missing_heredoc_end_marker_str[]
+ = N_("E1145: Missing heredoc end marker: %s");
void func_init(void)
{
@@ -152,7 +154,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
p = skipwhite(p) + 1;
p = skipwhite(p);
char *expr = p;
- if (eval1(&p, &rettv, false) != FAIL) {
+ if (eval1(&p, &rettv, NULL) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
@@ -252,22 +254,23 @@ static void set_ufunc_name(ufunc_T *fp, char *name)
/// Parse a lambda expression and get a Funcref from "*arg".
///
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
-int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
+int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
garray_T newargs = GA_EMPTY_INIT_VALUE;
garray_T *pnewargs;
ufunc_T *fp = NULL;
partial_T *pt = NULL;
int varargs;
int ret;
- char *start = skipwhite(*arg + 1);
- char *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
+ char *tofree = NULL;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
- if (ret == FAIL || *start != '>') {
+ char *s = skipwhite(*arg + 1);
+ ret = get_function_args(&s, '-', NULL, NULL, NULL, true);
+ if (ret == FAIL || *s != '>') {
return NOTDONE;
}
@@ -290,12 +293,18 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
// Get the start and the end of the expression.
*arg = skipwhite((*arg) + 1);
- s = *arg;
- ret = skip_expr(arg);
+ char *start = *arg;
+ ret = skip_expr(arg, evalarg);
+ char *end = *arg;
if (ret == FAIL) {
goto errret;
}
- e = *arg;
+ if (evalarg != NULL) {
+ // avoid that the expression gets freed when another line break follows
+ tofree = evalarg->eval_tofree;
+ evalarg->eval_tofree = NULL;
+ }
+
*arg = skipwhite(*arg);
if (**arg != '}') {
semsg(_("E451: Expected }: %s"), *arg);
@@ -317,11 +326,11 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
ga_grow(&newlines, 1);
// Add "return " before the expression.
- size_t len = (size_t)(7 + e - s + 1);
+ size_t len = (size_t)(7 + end - start + 1);
p = xmalloc(len);
((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
- xstrlcpy(p + 7, s, (size_t)(e - s) + 1);
+ xstrlcpy(p + 7, start, (size_t)(end - start) + 1);
if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure.
flags |= FC_NOARGS;
@@ -359,12 +368,22 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
}
eval_lavars_used = old_eval_lavars;
+ if (evalarg != NULL && evalarg->eval_tofree == NULL) {
+ evalarg->eval_tofree = tofree;
+ } else {
+ xfree(tofree);
+ }
return OK;
errret:
ga_clear_strings(&newargs);
xfree(fp);
xfree(pt);
+ if (evalarg != NULL && evalarg->eval_tofree == NULL) {
+ evalarg->eval_tofree = tofree;
+ } else {
+ xfree(tofree);
+ }
eval_lavars_used = old_eval_lavars;
return FAIL;
}
@@ -448,7 +467,8 @@ void emsg_funcname(const char *errmsg, const char *name)
/// @param funcexe various values
///
/// @return OK or FAIL.
-int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
+int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, evalarg_T *const evalarg,
+ funcexe_T *funcexe)
{
char *argp;
int ret = OK;
@@ -463,7 +483,7 @@ int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[argcount], evalarg) == FAIL) {
ret = FAIL;
break;
}
@@ -972,7 +992,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
default_expr = ((char **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
- if (eval1(&default_expr, &def_rettv, true) == FAIL) {
+ if (eval1(&default_expr, &def_rettv, &EVALARG_EVALUATE) == FAIL) {
default_arg_err = true;
break;
}
@@ -1109,7 +1129,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
ex_nesting_level++;
- (void)eval1(&p, rettv, true);
+ (void)eval1(&p, rettv, &EVALARG_EVALUATE);
ex_nesting_level--;
} else {
// call do_cmdline() to execute the lines
@@ -2301,7 +2321,11 @@ void ex_function(exarg_T *eap)
lines_left = Rows - 1;
}
if (theline == NULL) {
- emsg(_("E126: Missing :endfunction"));
+ if (skip_until != NULL) {
+ semsg(_(e_missing_heredoc_end_marker_str), skip_until);
+ } else {
+ emsg(_("E126: Missing :endfunction"));
+ }
goto erret;
}
if (show_block) {
@@ -2947,13 +2971,15 @@ void ex_return(exarg_T *eap)
return;
}
+ evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE };
+
if (eap->skip) {
emsg_skip++;
}
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
+ && eval0(arg, &rettv, eap, &evalarg) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -2982,6 +3008,7 @@ void ex_return(exarg_T *eap)
if (eap->skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
}
/// ":1,25call func(arg1, arg2)" function call.
@@ -2998,16 +3025,19 @@ void ex_call(exarg_T *eap)
bool failed = false;
funcdict_T fudi;
partial_T *partial = NULL;
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) {
// trans_function_name() doesn't work well when skipping, use eval0()
// instead to skip to any following command, e.g. for:
// :if 0 | call dict.foo().bar() | endif.
emsg_skip++;
- if (eval0(eap->arg, &rettv, &eap->nextcmd, false) != FAIL) {
+ if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) {
tv_clear(&rettv);
}
emsg_skip--;
+ clear_evalarg(&evalarg, eap);
return;
}
@@ -3065,14 +3095,13 @@ void ex_call(exarg_T *eap)
funcexe.fe_evaluate = true;
funcexe.fe_partial = partial;
funcexe.fe_selfdict = fudi.fd_dict;
- if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
+ if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL) {
failed = true;
break;
}
// Handle a function returning a Funcref, Dictionary or List.
- if (handle_subscript((const char **)&arg, &rettv, true, true)
- == FAIL) {
+ if (handle_subscript((const char **)&arg, &rettv, &EVALARG_EVALUATE, true) == FAIL) {
failed = true;
break;
}
@@ -3104,6 +3133,7 @@ void ex_call(exarg_T *eap)
eap->nextcmd = check_nextcmd(arg);
}
}
+ clear_evalarg(&evalarg, eap);
end:
tv_dict_unref(fudi.fd_dict);
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index c8583f232c..f0e1f5dca0 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 701f190a06..b8a8f39437 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -175,25 +175,16 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
/// ":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)
+void ex_let(exarg_T *eap)
{
+ const bool is_const = eap->cmdidx == CMD_const;
char *arg = eap->arg;
char *expr = NULL;
typval_T rettv;
- int i;
int var_count = 0;
int semicolon = 0;
char op[2];
@@ -208,8 +199,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
argend--;
}
expr = skipwhite(argend);
- if (*expr != '=' && !((vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
- && expr[1] == '=') || strncmp(expr, "..=", 3) == 0)) {
+ bool concat = strncmp(expr, "..=", 3) == 0;
+ bool has_assign = *expr == '=' || (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
+ && expr[1] == '=');
+ if (!has_assign && !concat) {
// ":let" without "=": list variables
if (*arg == '[') {
emsg(_(e_invarg));
@@ -227,7 +220,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
list_vim_vars(&first);
}
eap->nextcmd = check_nextcmd(arg);
- } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
+ return;
+ }
+
+ if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
// HERE document
list_T *l = heredoc_get(eap, expr + 3);
if (l != NULL) {
@@ -239,36 +235,43 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
}
tv_clear(&rettv);
}
- } else {
- op[0] = '=';
- op[1] = NUL;
- if (*expr != '=') {
- if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
- op[0] = *expr; // +=, -=, *=, /=, %= or .=
- if (expr[0] == '.' && expr[1] == '.') { // ..=
- expr++;
- }
- }
- expr += 2;
- } else {
- expr += 1;
- }
+ return;
+ }
- expr = skipwhite(expr);
+ rettv.v_type = VAR_UNKNOWN;
- if (eap->skip) {
- emsg_skip++;
- }
- i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
- if (eap->skip) {
- if (i != FAIL) {
- tv_clear(&rettv);
+ op[0] = '=';
+ op[1] = NUL;
+ if (*expr != '=') {
+ if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
+ op[0] = *expr; // +=, -=, *=, /=, %= or .=
+ if (expr[0] == '.' && expr[1] == '.') { // ..=
+ expr++;
}
- emsg_skip--;
- } else if (i != FAIL) {
- (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
- tv_clear(&rettv);
}
+ expr += 2;
+ } else {
+ expr += 1;
+ }
+
+ expr = skipwhite(expr);
+
+ if (eap->skip) {
+ emsg_skip++;
+ }
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
+ int eval_res = eval0(expr, &rettv, eap, &evalarg);
+ if (eap->skip) {
+ emsg_skip--;
+ }
+ clear_evalarg(&evalarg, eap);
+
+ if (!eap->skip && eval_res != FAIL) {
+ (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
+ }
+ if (eval_res != FAIL) {
+ tv_clear(&rettv);
}
}
@@ -500,13 +503,12 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
if (tofree != NULL) {
name = tofree;
}
- if (get_var_tv(name, len, &tv, NULL, true, false)
- == FAIL) {
+ if (eval_variable(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) == FAIL) {
+ if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, true) == FAIL) {
error = true;
} else {
if (arg == arg_subsc && len == 2 && name[1] == ':') {
@@ -1073,8 +1075,8 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap
/// @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 eval_variable(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose,
+ bool no_autoload)
{
int ret = OK;
typval_T *tv = NULL;
@@ -1149,7 +1151,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val)
}
}
hash_clear(ht);
- ht->ht_used = 0;
+ hash_init(ht);
}
/// Delete a variable from hashtab "ht" at item "hi".
@@ -1561,7 +1563,7 @@ static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv,
tv_dict_set_ret(rettv, opts);
done = true;
}
- } else if (get_option_tv(&varname, rettv, true) == OK) {
+ } else if (eval_option(&varname, rettv, true) == OK) {
// Local option
done = true;
}
@@ -1710,10 +1712,10 @@ bool var_exists(const char *var)
if (tofree != NULL) {
name = tofree;
}
- n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
+ n = eval_variable(name, len, &tv, NULL, false, true) == OK;
if (n) {
// Handle d.key, l[idx], f(expr).
- n = handle_subscript(&var, &tv, true, false) == OK;
+ n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK;
if (n) {
tv_clear(&tv);
}
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 2fd50a18d3..be6299db0e 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -197,12 +197,6 @@ module.cmds = {
func='ex_bunload',
},
{
- command='behave',
- flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
- addr_type='ADDR_NONE',
- func='ex_behave',
- },
- {
command='belowright',
flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM),
addr_type='ADDR_NONE',
@@ -640,7 +634,7 @@ module.cmds = {
command='const',
flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
- func='ex_const',
+ func='ex_let',
},
{
command='copen',
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 629aaf14cf..7932649114 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -185,6 +185,7 @@ struct exarg {
char *nextcmd; ///< next command (NULL if none)
char *cmd; ///< the name of the command (except for :make)
char **cmdlinep; ///< pointer to pointer of allocated cmdline
+ char *cmdline_tofree; ///< free later
cmdidx_T cmdidx; ///< the index for the command
uint32_t argt; ///< flags for the command
int skip; ///< don't execute the command, only parse it
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 699c1da3a7..8e55672615 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -484,24 +484,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
}
- if (cstack.cs_looplevel > 0) {
- // Inside a while/for loop we need to store the lines and use them
- // again. Pass a different "fgetline" function to do_one_cmd()
- // below, so that it stores lines in or reads them from
- // "lines_ga". Makes it possible to define a function inside a
- // while/for loop.
- cmd_getline = get_loop_line;
- cmd_cookie = (void *)&cmd_loop_cookie;
- cmd_loop_cookie.lines_gap = &lines_ga;
- cmd_loop_cookie.current_line = current_line;
- cmd_loop_cookie.getline = fgetline;
- cmd_loop_cookie.cookie = cookie;
- cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
- } else {
- cmd_getline = fgetline;
- cmd_cookie = cookie;
- }
-
// 2. If no line given, get an allocated line with fgetline().
if (next_cmdline == NULL) {
// Need to set msg_didout for the first line after an ":if",
@@ -540,15 +522,37 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
cmdline_copy = next_cmdline;
- // Save the current line when inside a ":while" or ":for", and when
- // the command looks like a ":while" or ":for", because we may need it
- // later. When there is a '|' and another command, it is stored
- // separately, because we need to be able to jump back to it from an
+ int current_line_before = 0;
+ // Inside a while/for loop, and when the command looks like a ":while"
+ // or ":for", the line is stored, because we may need it later when
+ // looping.
+ //
+ // When there is a '|' and another command, it is stored separately,
+ // because we need to be able to jump back to it from an
// :endwhile/:endfor.
- if (current_line == lines_ga.ga_len
- && (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) {
- store_loop_line(&lines_ga, next_cmdline);
+ //
+ // Pass a different "fgetline" function to do_one_cmd() below,
+ // that it stores lines in or reads them from "lines_ga". Makes it
+ // possible to define a function inside a while/for loop.
+ if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline))) {
+ cmd_getline = get_loop_line;
+ cmd_cookie = (void *)&cmd_loop_cookie;
+ cmd_loop_cookie.lines_gap = &lines_ga;
+ cmd_loop_cookie.current_line = current_line;
+ cmd_loop_cookie.getline = fgetline;
+ cmd_loop_cookie.cookie = cookie;
+ cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
+
+ // Save the current line when encountering it the first time.
+ if (current_line == lines_ga.ga_len) {
+ store_loop_line(&lines_ga, next_cmdline);
+ }
+ current_line_before = current_line;
+ } else {
+ cmd_getline = fgetline;
+ cmd_cookie = cookie;
}
+
did_endif = false;
if (count++ == 0) {
@@ -651,7 +655,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
} else if (cstack.cs_lflags & CSL_HAD_LOOP) {
// For a ":while" or ":for" we need to remember the line number.
cstack.cs_lflags &= ~CSL_HAD_LOOP;
- cstack.cs_line[cstack.cs_idx] = current_line - 1;
+ cstack.cs_line[cstack.cs_idx] = current_line_before;
}
}
@@ -2328,6 +2332,7 @@ doend:
}
ex_nesting_level--;
+ xfree(ea.cmdline_tofree);
return ea.nextcmd;
}
@@ -3750,7 +3755,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
// Skip over `=expr`, wildcards in it are not expanded.
if (p[0] == '`' && p[1] == '=') {
p += 2;
- (void)skip_expr(&p);
+ (void)skip_expr(&p, NULL);
if (*p == '`') {
p++;
}
@@ -3969,7 +3974,7 @@ void separate_nextcmd(exarg_T *eap)
} else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) {
// Skip over `=expr` when wildcards are expanded.
p += 2;
- (void)skip_expr(&p);
+ (void)skip_expr(&p, NULL);
if (*p == NUL) { // stop at NUL after CTRL-V
break;
}
@@ -4432,7 +4437,7 @@ static void ex_colorscheme(exarg_T *eap)
char *expr = xstrdup("g:colors_name");
emsg_off++;
- char *p = eval_to_string(expr, NULL, false);
+ char *p = eval_to_string(expr, false);
emsg_off--;
xfree(expr);
@@ -7058,24 +7063,6 @@ void dialog_msg(char *buff, char *format, char *fname)
vim_snprintf(buff, DIALOG_MSG_SIZE, format, fname);
}
-/// ":behave {mswin,xterm}"
-static void ex_behave(exarg_T *eap)
-{
- if (strcmp(eap->arg, "mswin") == 0) {
- set_option_value_give_err("selection", 0L, "exclusive", 0);
- set_option_value_give_err("selectmode", 0L, "mouse,key", 0);
- set_option_value_give_err("mousemodel", 0L, "popup", 0);
- set_option_value_give_err("keymodel", 0L, "startsel,stopsel", 0);
- } else if (strcmp(eap->arg, "xterm") == 0) {
- set_option_value_give_err("selection", 0L, "inclusive", 0);
- set_option_value_give_err("selectmode", 0L, "", 0);
- set_option_value_give_err("mousemodel", 0L, "extend", 0);
- set_option_value_give_err("keymodel", 0L, "", 0);
- } else {
- semsg(_(e_invarg2), eap->arg);
- }
-}
-
static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index fed8f549e6..5404ae6731 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -792,10 +792,15 @@ void report_discard_pending(int pending, void *value)
void ex_eval(exarg_T *eap)
{
typval_T tv;
+ evalarg_T evalarg;
- if (eval0(eap->arg, &tv, &eap->nextcmd, !eap->skip) == OK) {
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
+
+ if (eval0(eap->arg, &tv, eap, &evalarg) == OK) {
tv_clear(&tv);
}
+
+ clear_evalarg(&evalarg, eap);
}
/// Handle ":if".
@@ -812,7 +817,7 @@ void ex_if(exarg_T *eap)
int skip = CHECK_SKIP;
bool error;
- int result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ int result = eval_to_bool(eap->arg, &error, eap, skip);
if (!skip && !error) {
if (result) {
@@ -907,7 +912,7 @@ void ex_else(exarg_T *eap)
if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) {
semsg(_(e_invexpr2), eap->arg);
} else {
- result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ result = eval_to_bool(eap->arg, &error, eap, skip);
}
// When throwing error exceptions, we want to throw always the first
@@ -952,13 +957,12 @@ void ex_while(exarg_T *eap)
eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR;
int skip = CHECK_SKIP;
- if (eap->cmdidx == CMD_while) {
- // ":while bool-expr"
- result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
- } else {
+ if (eap->cmdidx == CMD_while) { // ":while bool-expr"
+ result = eval_to_bool(eap->arg, &error, eap, skip);
+ } else { // ":for var in list-expr"
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, skip);
void *fi;
-
- // ":for var in list-expr"
if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) {
// Jumping here from a ":continue" or ":endfor": use the
// previously evaluated list.
@@ -966,7 +970,7 @@ void ex_while(exarg_T *eap)
error = false;
} else {
// Evaluate the argument and get the info in a structure.
- fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip);
+ fi = eval_for_line(eap->arg, &error, eap, &evalarg);
cstack->cs_forinfo[cstack->cs_idx] = fi;
}
@@ -981,6 +985,7 @@ void ex_while(exarg_T *eap)
free_for_info(fi);
cstack->cs_forinfo[cstack->cs_idx] = NULL;
}
+ clear_evalarg(&evalarg, eap);
}
// If this cstack entry was just initialised and is active, set the
@@ -1125,12 +1130,11 @@ void ex_endwhile(exarg_T *eap)
/// Handle ":throw expr"
void ex_throw(exarg_T *eap)
{
- const char *arg = eap->arg;
+ char *arg = eap->arg;
char *value;
if (*arg != NUL && *arg != '|' && *arg != '\n') {
- value = eval_to_string_skip(arg, (const char **)&eap->nextcmd,
- (bool)eap->skip);
+ value = eval_to_string_skip(arg, eap, eap->skip);
} else {
emsg(_(e_argreq));
value = NULL;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index ba403e3dd9..f3afbdcaaf 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -4209,7 +4209,8 @@ void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
- rettv->vval.v_number = set_cmdline_str(argvars[0].vval.v_string, pos);
+ // Use tv_get_string() to handle a NULL string like an empty string.
+ rettv->vval.v_number = set_cmdline_str(tv_get_string(&argvars[0]), pos);
}
/// "setcmdpos()" function
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 71984e806d..a0869b54c9 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1749,7 +1749,8 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
curbuf = wp->w_buffer;
emsg_silent++; // handle exceptions, but don't display errors
- text = eval_to_string_safe(wp->w_p_fdt, NULL, was_set_insecurely(wp, "foldtext", OPT_LOCAL));
+ text = eval_to_string_safe(wp->w_p_fdt,
+ was_set_insecurely(wp, "foldtext", OPT_LOCAL));
emsg_silent--;
if (text == NULL || did_emsg) {
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 6493408a58..3ab8a3bb88 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -1530,6 +1530,10 @@ static bool hlgroup2dict(Dictionary *hl, NS ns_id, int hl_id, Arena *arena)
if (link == -1) {
return false;
}
+ if (ns_id == 0 && sgp->sg_cleared && sgp->sg_set == 0) {
+ // table entry was created but not ever set
+ return false;
+ }
HlAttrs attr =
syn_attr2entry(ns_id == 0 ? sgp->sg_attr : ns_get_hl(&ns_id, hl_id, false, sgp->sg_set));
*hl = arena_dict(arena, HLATTRS_DICT_SIZE + 1);
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 19a2aca75e..c1565a84f5 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -1632,7 +1632,7 @@ char *eval_map_expr(mapblock_T *mp, int c)
api_clear_error(&err);
}
} else {
- p = eval_to_string(expr, NULL, false);
+ p = eval_to_string(expr, false);
xfree(expr);
}
expr_map_lock--;
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 855fcb33ae..59ff3026b3 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -84,7 +84,7 @@ void clear_fmark(fmark_T *fm)
FUNC_ATTR_NONNULL_ALL
{
free_fmark(*fm);
- CLEAR_POINTER(fm);
+ *fm = (fmark_T)INIT_FMARK;
}
// Set named mark "c" to position "pos".
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index af0abba864..8c72579d0f 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -36,7 +36,7 @@
/// Clear given fmark
#define CLEAR_FMARK(fmarkp_) \
- RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T) { 0 }))
+ RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T)INIT_FMARKV))
/// Set given extended mark (regular mark + file name)
#define SET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index b11d5a3c32..718c51deb0 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1173,7 +1173,7 @@ static int normal_execute(VimState *state, int key)
msg_col = 0;
}
- s->old_pos = curwin->w_cursor; // remember where cursor was
+ s->old_pos = curwin->w_cursor; // remember where the cursor was
// When 'keymodel' contains "startsel" some keys start Select/Visual
// mode.
@@ -1997,13 +1997,21 @@ static void display_showcmd(void)
showcmd_is_clear = (len == 0);
if (*p_sloc == 's') {
- win_redr_status(curwin);
- setcursor(); // put cursor back where it belongs
+ if (showcmd_is_clear) {
+ curwin->w_redr_status = true;
+ } else {
+ win_redr_status(curwin);
+ setcursor(); // put cursor back where it belongs
+ }
return;
}
if (*p_sloc == 't') {
- draw_tabline();
- setcursor(); // put cursor back where it belongs
+ if (showcmd_is_clear) {
+ redraw_tabline = true;
+ } else {
+ draw_tabline();
+ setcursor(); // put cursor back where it belongs
+ }
return;
}
// 'showcmdloc' is "last" or empty
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 9aacfcad30..b2c0dd6c01 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -763,7 +763,7 @@ char *get_expr_line(void)
}
nested++;
- rv = eval_to_string(expr_copy, NULL, true);
+ rv = eval_to_string(expr_copy, true);
nested--;
xfree(expr_copy);
return rv;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 386c6d88d9..6d4e7de1a3 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5266,7 +5266,7 @@ int option_set_callback_func(char *optval, Callback *optcb)
|| (strncmp(optval, "function(", 9) == 0)
|| (strncmp(optval, "funcref(", 8) == 0)) {
// Lambda expression or a funcref
- tv = eval_expr(optval);
+ tv = eval_expr(optval, NULL);
if (tv == NULL) {
return FAIL;
}
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 0b06877f3c..26707fd6ca 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -603,7 +603,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es
if (src[0] == '`' && src[1] == '=') {
var = src;
src += 2;
- (void)skip_expr(&src);
+ (void)skip_expr(&src, NULL);
if (*src == '`') {
src++;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 368f3feb27..8bd3303166 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1371,7 +1371,7 @@ static int expand_backtick(garray_T *gap, char *pat, int flags)
char *cmd = xstrnsave(pat + 1, strlen(pat) - 2);
if (*cmd == '=') { // `={expr}`: Expand expression
- buffer = eval_to_string(cmd + 1, &p, true);
+ buffer = eval_to_string(cmd + 1, true);
} else {
buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
}
@@ -1662,7 +1662,7 @@ void simplify_filename(char *filename)
static char *eval_includeexpr(const char *const ptr, const size_t len)
{
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
- char *res = eval_to_string_safe(curbuf->b_p_inex, NULL,
+ char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, "includeexpr", OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index c8db9a64b5..ab6acf685f 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -5540,7 +5540,7 @@ msgstr "tekijät Bram Moolenaar et al."
#~ msgstr "kirjoita :help iccf<Enter> lisätietoa varten "
#, fuzzy
-#~ msgid "type :CheckHealth<Enter> to optimize Nvim"
+#~ msgid "type :checkhealth<Enter> to optimize Nvim"
#~ msgstr "kirjoita :help iccf<Enter> lisätietoa varten "
msgid "type :q<Enter> to exit "
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 262d87ba61..62eb14342c 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -6957,15 +6957,15 @@ void ex_cexpr(exarg_T *eap)
// Evaluate the expression. When the result is a string or a list we can
// use it to fill the errorlist.
- typval_T tv;
- if (eval0(eap->arg, &tv, &eap->nextcmd, true) == FAIL) {
+ typval_T *tv = eval_expr(eap->arg, eap);
+ if (tv == NULL) {
return;
}
- if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
- || tv.v_type == VAR_LIST) {
+ if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
+ || tv->v_type == VAR_LIST) {
incr_quickfix_busy();
- int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
+ int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
(eap->cmdidx != CMD_caddexpr
&& eap->cmdidx != CMD_laddexpr),
(linenr_T)0, (linenr_T)0,
@@ -6996,7 +6996,7 @@ void ex_cexpr(exarg_T *eap)
emsg(_("E777: String or List expected"));
}
cleanup:
- tv_clear(&tv);
+ tv_free(tv);
}
// Get the location list for ":lhelpgrep"
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 94796f0ed3..3ed32bf8af 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1812,7 +1812,7 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
}
tv_clear(&rettv);
} else {
- eval_result[nested] = eval_to_string(source + 2, NULL, true);
+ eval_result[nested] = eval_to_string(source + 2, true);
}
nesting--;
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 2d5eee7eec..78499922bf 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1307,6 +1307,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
.mark = cur_entry.data.filemark.mark,
.fnum = (buf == NULL ? 0 : buf->b_fnum),
.timestamp = cur_entry.timestamp,
+ .view = INIT_FMARKV,
.additional_data = cur_entry.data.filemark.additional_data,
},
};
@@ -1388,6 +1389,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
.mark = cur_entry.data.filemark.mark,
.fnum = 0,
.timestamp = cur_entry.timestamp,
+ .view = INIT_FMARKV,
.additional_data = cur_entry.data.filemark.additional_data,
};
if (cur_entry.type == kSDItemLocalMark) {
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 6c698f45be..05649e9b7f 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -985,7 +985,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
};
set_var(S_LEN("g:statusline_winid"), &tv, false);
- usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
+ usefmt = eval_to_string_safe(fmt + 2, use_sandbox);
if (usefmt == NULL) {
usefmt = fmt;
}
@@ -1457,7 +1457,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
}
// Note: The result stored in `t` is unused.
- str = eval_to_string_safe(out_p, &t, use_sandbox);
+ str = eval_to_string_safe(out_p, use_sandbox);
curwin = save_curwin;
curbuf = save_curbuf;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 401b43204e..cebc7d5d89 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -3322,11 +3322,10 @@ int get_tags(list_T *list, char *pat, char *buf_fname)
}
for (i = 0; i < num_matches; i++) {
- int parse_result = parse_match(matches[i], &tp);
-
- // Avoid an unused variable warning in release builds.
- (void)parse_result;
- assert(parse_result == OK);
+ if (parse_match(matches[i], &tp) == FAIL) {
+ xfree(matches[i]);
+ continue;
+ }
bool is_static = test_for_static(&tp);
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 4cd58bb91b..e859a96552 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -56,7 +56,6 @@ static const char e_no_such_user_defined_command_in_current_buffer_str[]
static const char *command_complete[] = {
[EXPAND_ARGLIST] = "arglist",
[EXPAND_AUGROUP] = "augroup",
- [EXPAND_BEHAVE] = "behave",
[EXPAND_BUFFERS] = "buffer",
[EXPAND_CHECKHEALTH] = "checkhealth",
[EXPAND_COLORS] = "color",
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 00e28e3e68..44e3e9d86a 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -140,7 +140,6 @@ enum {
EXPAND_SHELLCMD,
EXPAND_SIGN,
EXPAND_PROFILE,
- EXPAND_BEHAVE,
EXPAND_FILETYPE,
EXPAND_FILES_IN_PATH,
EXPAND_OWNSYNTAX,
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index a4bd574a56..a6e9f9a42b 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -575,4 +575,25 @@ describe('API: get highlight', function()
meths.set_hl(0, 'Foo', hl)
eq(hl, meths.get_hl(0, { name = 'Foo', link = true }))
end)
+
+ it("doesn't contain unset groups", function()
+ local id = meths.get_hl_id_by_name "@foobar.hubbabubba"
+ ok(id > 0)
+
+ local data = meths.get_hl(0, {})
+ eq(nil, data["@foobar.hubbabubba"])
+ eq(nil, data["@foobar"])
+
+ command 'hi @foobar.hubbabubba gui=bold'
+ data = meths.get_hl(0, {})
+ eq({bold = true}, data["@foobar.hubbabubba"])
+ eq(nil, data["@foobar"])
+
+ -- @foobar.hubbabubba was explicitly cleared and thus shows up
+ -- but @foobar was never touched, and thus doesn't
+ command 'hi clear @foobar.hubbabubba'
+ data = meths.get_hl(0, {})
+ eq({}, data["@foobar.hubbabubba"])
+ eq(nil, data["@foobar"])
+ end)
end)
diff --git a/test/functional/autocmd/autocmd_oldtest_spec.lua b/test/functional/autocmd/autocmd_oldtest_spec.lua
index 29d589a296..29a6171269 100644
--- a/test/functional/autocmd/autocmd_oldtest_spec.lua
+++ b/test/functional/autocmd/autocmd_oldtest_spec.lua
@@ -50,6 +50,7 @@ describe('oldtests', function()
it('should fire on unload buf', function()
funcs.writefile({'Test file Xxx1'}, 'Xxx1')
funcs.writefile({'Test file Xxx2'}, 'Xxx2')
+ local fname = 'Xtest_functional_autocmd_unload'
local content = [[
func UnloadAllBufs()
@@ -69,15 +70,15 @@ describe('oldtests', function()
q
]]
- funcs.writefile(funcs.split(content, "\n"), 'Xtest')
+ funcs.writefile(funcs.split(content, "\n"), fname)
funcs.delete('Xout')
- funcs.system(meths.get_vvar('progpath') .. ' -u NORC -i NONE -N -S Xtest')
+ funcs.system(string.format('%s -u NORC -i NONE -N -S %s', meths.get_vvar('progpath'), fname))
eq(1, funcs.filereadable('Xout'))
funcs.delete('Xxx1')
funcs.delete('Xxx2')
- funcs.delete('Xtest')
+ funcs.delete(fname)
funcs.delete('Xout')
end)
end)
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index cc94623df3..b8a3c1dcd5 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -28,6 +28,9 @@ local meths = helpers.meths
local alter_slashes = helpers.alter_slashes
local is_os = helpers.is_os
local dedent = helpers.dedent
+local tbl_map = helpers.tbl_map
+local tbl_filter = helpers.tbl_filter
+local endswith = helpers.endswith
local testfile = 'Xtest_startuptime'
after_each(function()
@@ -202,12 +205,12 @@ describe('startup', function()
end)
it('disables swapfile/shada/config/plugins', function()
- assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scriptnames=1',
+ assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scripts=1',
nil,
nil,
'-',
- [[print(('updatecount=%d shadafile=%s loadplugins=%s scriptnames=%d'):format(
- vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.split(vim.fn.execute('scriptnames'),'\n'))))]])
+ [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format(
+ vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]])
end)
end)
@@ -398,13 +401,13 @@ describe('startup', function()
for _,arg in ipairs({'-es', '-Es'}) do
local out = funcs.system({nvim_prog, arg,
'+set swapfile? updatecount? shadafile?',
- "+put =execute('scriptnames')", '+%print'})
+ "+put =map(getscriptinfo(), {-> v:val.name})", '+%print'})
local line1 = string.match(out, '^.-\n')
-- updatecount=0 means swapfile was disabled.
eq(" swapfile updatecount=0 shadafile=\n", line1)
-- Standard plugins were loaded, but not user config.
- eq('health.vim', string.match(out, 'health.vim'))
- eq(nil, string.match(out, 'init.vim'))
+ ok(string.find(out, 'man.lua') ~= nil)
+ ok(string.find(out, 'init.vim') == nil)
end
end)
@@ -865,6 +868,10 @@ describe('runtime:', function()
local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'},
pathsep)
local profiler_file = 'test_startuptime.log'
+ finally(function()
+ os.remove(profiler_file)
+ rmdir(plugin_path)
+ end)
mkdir_p(plugin_folder_path)
write_file(plugin_file_path, [[vim.g.lua_plugin = 2]])
@@ -872,18 +879,15 @@ describe('runtime:', function()
clear{ args_rm={'-u'}, args={'--startuptime', profiler_file}, env=xenv }
eq(2, eval('g:lua_plugin'))
- -- Check if plugin_file_path is listed in :scriptname
- local scripts = exec_capture('scriptnames')
- assert(scripts:find(plugin_file_path))
+ -- Check if plugin_file_path is listed in getscriptinfo()
+ local scripts = tbl_map(function(s) return s.name end, funcs.getscriptinfo())
+ ok(#tbl_filter(function(s) return endswith(s, plugin_file_path) end, scripts) > 0)
-- Check if plugin_file_path is listed in startup profile
local profile_reader = io.open(profiler_file, 'r')
local profile_log = profile_reader:read('*a')
profile_reader:close()
- assert(profile_log:find(plugin_file_path))
-
- os.remove(profiler_file)
- rmdir(plugin_path)
+ ok(profile_log:find(plugin_file_path) ~= nil)
end)
it('loads plugin/*.lua from site packages', function()
diff --git a/test/functional/editor/mark_spec.lua b/test/functional/editor/mark_spec.lua
index 365f8527a0..a6e4b0c5eb 100644
--- a/test/functional/editor/mark_spec.lua
+++ b/test/functional/editor/mark_spec.lua
@@ -417,4 +417,48 @@ describe('named marks view', function()
|
]])
end)
+
+ it('fallback to standard behavior when mark is loaded from shada', function()
+ local screen = Screen.new(10, 6)
+ screen:attach()
+ command('edit ' .. file1)
+ feed('G')
+ feed('mA')
+ screen:expect([[
+ 26 line |
+ 27 line |
+ 28 line |
+ 29 line |
+ ^30 line |
+ |
+ ]])
+ command('set shadafile=Xtestfile-functional-editor-marks-shada')
+ finally(function()
+ command('set shadafile=NONE')
+ os.remove('Xtestfile-functional-editor-marks-shada')
+ end)
+ command('wshada!')
+ command('bwipe!')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ command('rshada!')
+ command('edit ' .. file1)
+ feed('`"')
+ screen:expect([[
+ 26 line |
+ 27 line |
+ 28 line |
+ 29 line |
+ ^30 line |
+ |
+ ]])
+ feed('`A')
+ screen:expect_unchanged()
+ end)
end)
diff --git a/test/functional/legacy/061_undo_tree_spec.lua b/test/functional/legacy/061_undo_tree_spec.lua
index 1a8ef067d0..b5af8f7d52 100644
--- a/test/functional/legacy/061_undo_tree_spec.lua
+++ b/test/functional/legacy/061_undo_tree_spec.lua
@@ -22,29 +22,30 @@ end
describe('undo tree:', function()
before_each(clear)
+ local fname = 'Xtest_functional_legacy_undotree'
teardown(function()
- os.remove('Xtest.source')
+ os.remove(fname .. '.source')
end)
describe(':earlier and :later', function()
before_each(function()
- os.remove('Xtest')
+ os.remove(fname)
end)
teardown(function()
- os.remove('Xtest')
+ os.remove(fname)
end)
it('time specifications, g- g+', function()
-- We write the test text to a file in order to prevent nvim to record
-- the inserting of the text into the undo history.
- write_file('Xtest', '\n123456789\n')
+ write_file(fname, '\n123456789\n')
-- `:earlier` and `:later` are (obviously) time-sensitive, so this test
-- sometimes fails if the system is under load. It is wrapped in a local
-- function to allow multiple attempts.
local function test_earlier_later()
clear()
- feed_command('e Xtest')
+ feed_command('e ' .. fname)
-- Assert that no undo history is present.
eq({}, eval('undotree().entries'))
-- Delete three characters and undo.
@@ -103,7 +104,7 @@ describe('undo tree:', function()
it('file-write specifications', function()
feed('ione one one<esc>')
- feed_command('w Xtest')
+ feed_command('w ' .. fname)
feed('otwo<esc>')
feed('otwo<esc>')
feed_command('w')
@@ -187,7 +188,7 @@ describe('undo tree:', function()
it('undo an expression-register', function()
local normal_commands = 'o1\027a2\018=string(123)\n\027'
- write_file('Xtest.source', normal_commands)
+ write_file(fname .. '.source', normal_commands)
feed('oa<esc>')
feed('ob<esc>')
@@ -221,7 +222,7 @@ describe('undo tree:', function()
c
12]])
feed('od<esc>')
- feed_command('so! Xtest.source')
+ feed_command('so! ' .. fname .. '.source')
expect([[
a
diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua
index 6c72cde855..5c3f8a6f8c 100644
--- a/test/functional/legacy/prompt_buffer_spec.lua
+++ b/test/functional/legacy/prompt_buffer_spec.lua
@@ -247,6 +247,7 @@ describe('prompt buffer', function()
func DoAppend()
call appendbufline('prompt', '$', 'Test')
+ return ''
endfunc
]])
feed('asomething<CR>')
@@ -254,7 +255,12 @@ describe('prompt buffer', function()
neq(prev_win, meths.get_current_win())
feed('exit<CR>')
eq(prev_win, meths.get_current_win())
+ eq({ mode = 'n', blocking = false }, meths.get_mode())
command('call DoAppend()')
eq({ mode = 'n', blocking = false }, meths.get_mode())
+ feed('i')
+ eq({ mode = 'i', blocking = false }, meths.get_mode())
+ command('call DoAppend()')
+ eq({ mode = 'i', blocking = false }, meths.get_mode())
end)
end)
diff --git a/test/functional/lua/loader_spec.lua b/test/functional/lua/loader_spec.lua
new file mode 100644
index 0000000000..e2958d1592
--- /dev/null
+++ b/test/functional/lua/loader_spec.lua
@@ -0,0 +1,36 @@
+-- Test suite for testing interactions with API bindings
+local helpers = require('test.functional.helpers')(after_each)
+
+local exec_lua = helpers.exec_lua
+local command = helpers.command
+local eq = helpers.eq
+
+describe('vim.loader', function()
+ before_each(helpers.clear)
+
+ it('handles changing files (#23027)', function()
+ exec_lua[[
+ vim.loader.enable()
+ ]]
+
+ local tmp = helpers.tmpname()
+ command('edit ' .. tmp)
+
+ eq(1, exec_lua([[
+ vim.api.nvim_buf_set_lines(0, 0, -1, true, {'_G.TEST=1'})
+ vim.cmd.write()
+ loadfile(...)()
+ return _G.TEST
+ ]], tmp))
+
+ -- fs latency
+ helpers.sleep(10)
+
+ eq(2, exec_lua([[
+ vim.api.nvim_buf_set_lines(0, 0, -1, true, {'_G.TEST=2'})
+ vim.cmd.write()
+ loadfile(...)()
+ return _G.TEST
+ ]], tmp))
+ end)
+end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 45d9263c0c..1ee1a13fd5 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -461,6 +461,22 @@ describe('lua stdlib', function()
pcall_err(exec_lua, [[return vim.pesc(2)]]))
end)
+ it('vim.list_contains', function()
+ eq(true, exec_lua("return vim.list_contains({'a','b','c'}, 'c')"))
+ eq(false, exec_lua("return vim.list_contains({'a','b','c'}, 'd')"))
+ end)
+
+ it('vim.tbl_contains', function()
+ eq(true, exec_lua("return vim.tbl_contains({'a','b','c'}, 'c')"))
+ eq(false, exec_lua("return vim.tbl_contains({'a','b','c'}, 'd')"))
+ eq(true, exec_lua("return vim.tbl_contains({[2]='a',foo='b',[5] = 'c'}, 'c')"))
+ eq(true, exec_lua([[
+ return vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
+ return vim.deep_equal(v, { 'b', 'c' })
+ end, { predicate = true })
+ ]]))
+ end)
+
it('vim.tbl_keys', function()
eq({}, exec_lua("return vim.tbl_keys({})"))
for _, v in pairs(exec_lua("return vim.tbl_keys({'a', 'b', 'c'})")) do
@@ -505,6 +521,19 @@ describe('lua stdlib', function()
]]))
end)
+ it('vim.tbl_isarray', function()
+ eq(true, exec_lua("return vim.tbl_isarray({})"))
+ eq(false, exec_lua("return vim.tbl_isarray(vim.empty_dict())"))
+ eq(true, exec_lua("return vim.tbl_isarray({'a', 'b', 'c'})"))
+ eq(false, exec_lua("return vim.tbl_isarray({'a', '32', a='hello', b='baz'})"))
+ eq(false, exec_lua("return vim.tbl_isarray({1, a='hello', b='baz'})"))
+ eq(false, exec_lua("return vim.tbl_isarray({a='hello', b='baz', 1})"))
+ eq(false, exec_lua("return vim.tbl_isarray({1, 2, nil, a='hello'})"))
+ eq(true, exec_lua("return vim.tbl_isarray({1, 2, nil, 4})"))
+ eq(true, exec_lua("return vim.tbl_isarray({nil, 2, 3, 4})"))
+ eq(false, exec_lua("return vim.tbl_isarray({1, [1.5]=2, [3]=3})"))
+ end)
+
it('vim.tbl_islist', function()
eq(true, exec_lua("return vim.tbl_islist({})"))
eq(false, exec_lua("return vim.tbl_islist(vim.empty_dict())"))
@@ -513,6 +542,9 @@ describe('lua stdlib', function()
eq(false, exec_lua("return vim.tbl_islist({1, a='hello', b='baz'})"))
eq(false, exec_lua("return vim.tbl_islist({a='hello', b='baz', 1})"))
eq(false, exec_lua("return vim.tbl_islist({1, 2, nil, a='hello'})"))
+ eq(false, exec_lua("return vim.tbl_islist({1, 2, nil, 4})"))
+ eq(false, exec_lua("return vim.tbl_islist({nil, 2, 3, 4})"))
+ eq(false, exec_lua("return vim.tbl_islist({1, [1.5]=2, [3]=3})"))
end)
it('vim.tbl_isempty', function()
@@ -2297,6 +2329,10 @@ describe('lua stdlib', function()
insert([[αα]])
eq({0,5}, exec_lua[[ return vim.region(0,{0,0},{0,4},'3',true)[0] ]])
end)
+ it('getpos() input', function()
+ insert('getpos')
+ eq({0,6}, exec_lua[[ return vim.region(0,{0,0},'.','v',true)[0] ]])
+ end)
end)
describe('vim.on_key', function()
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index da05b09593..5ba0706208 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -323,15 +323,13 @@ describe('LSP', function()
local client
test_rpc_server {
test_name = "set_defaults_all_capabilities";
- on_setup = function()
+ on_init = function(_client)
+ client = _client
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
+ lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
]]
end;
- on_init = function(_client)
- client = _client
- exec_lua("lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)")
- end;
on_handler = function(_, _, ctx)
if ctx.method == 'test' then
eq('v:lua.vim.lsp.tagfunc', get_buf_option("tagfunc"))
@@ -352,7 +350,8 @@ describe('LSP', function()
local client
test_rpc_server {
test_name = "set_defaults_all_capabilities";
- on_setup = function()
+ on_init = function(_client)
+ client = _client
exec_lua [[
vim.api.nvim_command('filetype plugin on')
BUFFER_1 = vim.api.nvim_create_buf(false, true)
@@ -360,14 +359,16 @@ describe('LSP', function()
vim.api.nvim_buf_set_option(BUFFER_1, 'filetype', 'man')
vim.api.nvim_buf_set_option(BUFFER_2, 'filetype', 'xml')
]]
+
+ -- Sanity check to ensure that some values are set after setting filetype.
eq('v:lua.require\'man\'.goto_tag', get_buf_option("tagfunc", "BUFFER_1"))
eq('xmlcomplete#CompleteTags', get_buf_option("omnifunc", "BUFFER_2"))
eq('xmlformat#Format()', get_buf_option("formatexpr", "BUFFER_2"))
- end;
- on_init = function(_client)
- client = _client
- exec_lua("lsp.buf_attach_client(BUFFER_1, TEST_RPC_CLIENT_ID)")
- exec_lua("lsp.buf_attach_client(BUFFER_2, TEST_RPC_CLIENT_ID)")
+
+ exec_lua [[
+ lsp.buf_attach_client(BUFFER_1, TEST_RPC_CLIENT_ID)
+ lsp.buf_attach_client(BUFFER_2, TEST_RPC_CLIENT_ID)
+ ]]
end;
on_handler = function(_, _, ctx)
if ctx.method == 'test' then
@@ -389,18 +390,16 @@ describe('LSP', function()
local client
test_rpc_server {
test_name = "set_defaults_all_capabilities";
- on_setup = function()
+ on_init = function(_client)
+ client = _client
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(BUFFER, 'tagfunc', 'tfu')
vim.api.nvim_buf_set_option(BUFFER, 'omnifunc', 'ofu')
vim.api.nvim_buf_set_option(BUFFER, 'formatexpr', 'fex')
+ lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
]]
end;
- on_init = function(_client)
- client = _client
- exec_lua("lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)")
- end;
on_handler = function(_, _, ctx)
if ctx.method == 'test' then
eq('tfu', get_buf_option("tagfunc"))
diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua
index da2fbbe029..dad7aa851d 100644
--- a/test/functional/shada/merging_spec.lua
+++ b/test/functional/shada/merging_spec.lua
@@ -1115,5 +1115,3 @@ describe('ShaDa changes support code', function()
eq(found, 100)
end)
end)
-
--- vim: ts=2 sw=2
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index f88954c16b..3203b187cc 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -432,6 +432,25 @@ describe('float window', function()
assert_alive()
end)
+ it("should re-apply 'style' when present", function()
+ local float_opts = {style = 'minimal', 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, 'number', true)
+ float_opts.row = 2
+ meths.win_set_config(float_win, float_opts)
+ eq(false, meths.win_get_option(float_win, 'number'))
+ end)
+
+ it("should not re-apply 'style' when missing", function()
+ local float_opts = {style = 'minimal', 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, 'number', true)
+ float_opts.row = 2
+ float_opts.style = nil
+ meths.win_set_config(float_win, float_opts)
+ eq(true, meths.win_get_option(float_win, 'number'))
+ end)
+
it("'scroll' is computed correctly when opening float with splitkeep=screen #20684", function()
meths.set_option('splitkeep', 'screen')
local float_opts = {relative = 'editor', row = 1, col = 1, width = 10, height = 10}
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index fedfaca7ba..89b503141b 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -1411,10 +1411,10 @@ describe('ColorColumn highlight', function()
[3] = {foreground = Screen.colors.Brown}, -- LineNr
[4] = {foreground = Screen.colors.Brown, bold = true}, -- CursorLineNr
[5] = {foreground = Screen.colors.Blue, bold = true}, -- NonText
- -- NonText and ColorColumn
[6] = {foreground = Screen.colors.Blue, background = Screen.colors.LightRed, bold = true},
[7] = {reverse = true, bold = true}, -- StatusLine
[8] = {reverse = true}, -- StatusLineNC
+ [9] = {background = Screen.colors.Grey90, foreground = Screen.colors.Red},
})
screen:attach()
end)
@@ -1500,6 +1500,25 @@ describe('ColorColumn highlight', function()
|
]])
end)
+
+ it('is combined with low-priority CursorLine highlight #23016', function()
+ screen:try_resize(40, 2)
+ command('set colorcolumn=30 cursorline')
+ screen:expect([[
+ {2:^ }{1: }{2: }|
+ |
+ ]])
+ command('hi clear ColorColumn')
+ screen:expect([[
+ {2:^ }|
+ |
+ ]])
+ command('hi ColorColumn guifg=Red')
+ screen:expect([[
+ {2:^ }{9: }{2: }|
+ |
+ ]])
+ end)
end)
describe("MsgSeparator highlight and msgsep fillchar", function()
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 5220f3fa89..1a7fe26d26 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -22,6 +22,7 @@ local skip = helpers.skip
describe('ui/ext_messages', function()
local screen
+ local fname = 'Xtest_functional_ui_messages_spec'
before_each(function()
clear()
@@ -41,7 +42,7 @@ describe('ui/ext_messages', function()
})
end)
after_each(function()
- os.remove('Xtest')
+ os.remove(fname)
end)
it('msg_clear follows msg_show kind of confirm', function()
@@ -126,7 +127,7 @@ describe('ui/ext_messages', function()
feed('nq')
-- kind=wmsg (editing readonly file)
- command('write Xtest')
+ command('write ' .. fname)
command('set readonly nohls')
feed('G$x')
screen:expect{grid=[[
@@ -912,9 +913,9 @@ stack traceback:
end)
it('does not truncate messages', function()
- command('write Xtest')
+ command('write '.. fname)
screen:expect({messages={
- {content = { { '"Xtest" [New] 0L, 0B written' } }, kind = "" }
+ {content = { { string.format('"%s" [New] 0L, 0B written', fname) } }, kind = "" }
}})
end)
end)
diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua
index 0a253455ad..a2fe875e65 100644
--- a/test/functional/ui/statuscolumn_spec.lua
+++ b/test/functional/ui/statuscolumn_spec.lua
@@ -397,6 +397,29 @@ describe('statuscolumn', function()
{0:~ }|
|
]])
+ -- Also test virt_lines when 'cpoptions' includes "n"
+ exec_lua([[
+ vim.opt.cpoptions:append("n")
+ local ns = vim.api.nvim_create_namespace("ns")
+ vim.api.nvim_buf_set_extmark(0, ns, 14, 0, { virt_lines = {{{"virt_line1", ""}}} })
+ vim.api.nvim_buf_set_extmark(0, ns, 14, 0, { virt_lines = {{{"virt_line2", ""}}} })
+ ]])
+ screen:expect([[
+ {1:buffer 0 13}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ aaaaaaaaa |
+ {1:buffer 0 14}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ aaaaaaaaa |
+ {1:buffer 0 15}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ aaaaaaaaa |
+ {1:virtual-2 15}virt_line1 |
+ {1:virtual-2 15}virt_line2 |
+ {1:buffer 0 16}{5:^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {5:aaaaaaaaa }|
+ {1:virtual-1 16}END |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
end)
it("works with 'statuscolumn' clicks", function()
@@ -593,4 +616,17 @@ describe('statuscolumn', function()
|
]])
end)
+
+ it("is only evaluated twice, once to estimate and once to draw", function()
+ command([[
+ let g:stcnr = 0
+ func! Stc()
+ let g:stcnr += 1
+ return '12345'
+ endfunc
+ set stc=%!Stc()
+ norm ggdG
+ ]])
+ eq(2, eval('g:stcnr'))
+ end)
end)
diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua
index ece27ec3ff..970f9c3d76 100644
--- a/test/functional/ui/winbar_spec.lua
+++ b/test/functional/ui/winbar_spec.lua
@@ -114,6 +114,41 @@ describe('winbar', function()
{2:[No Name] [No Name] }|
|
]])
+ -- 'showcmdloc' "statusline" should not interfere with winbar redrawing #23030
+ command('set showcmd showcmdloc=statusline')
+ feed('<C-W>w')
+ feed('<C-W>')
+ screen:expect([[
+ {6:Set Up The Bars }│{6:Set Up The Bars }|
+ │ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{5:Set Up The Bars }|
+ {3:~ }│^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] ^W }|
+ {3:~ }│{6:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ feed('w<C-W>W')
+ screen:expect([[
+ {6:Set Up The Bars }│{6:Set Up The Bars }|
+ │ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{5:Set Up The Bars }|
+ {3:~ }│^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│{6:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
end)
it('works when switching value of \'winbar\'', function()
diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua
index b995aaa5a6..b411b1e379 100644
--- a/test/functional/vimscript/eval_spec.lua
+++ b/test/functional/vimscript/eval_spec.lua
@@ -220,3 +220,38 @@ describe('listing functions using :function', function()
assert_alive()
end)
end)
+
+it('no double-free in garbage collection #16287', function()
+ clear()
+ -- Don't use exec() here as using a named script reproduces the issue better.
+ write_file('Xgarbagecollect.vim', [[
+ func Foo() abort
+ let s:args = [a:000]
+ let foo0 = ""
+ let foo1 = ""
+ let foo2 = ""
+ let foo3 = ""
+ let foo4 = ""
+ let foo5 = ""
+ let foo6 = ""
+ let foo7 = ""
+ let foo8 = ""
+ let foo9 = ""
+ let foo10 = ""
+ let foo11 = ""
+ let foo12 = ""
+ let foo13 = ""
+ let foo14 = ""
+ endfunc
+
+ set updatetime=1
+ call Foo()
+ call Foo()
+ ]])
+ finally(function()
+ os.remove('Xgarbagecollect.vim')
+ end)
+ command('source Xgarbagecollect.vim')
+ sleep(10)
+ assert_alive()
+end)
diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua
index 158dfe86d7..130d5d81fa 100644
--- a/test/functional/vimscript/system_spec.lua
+++ b/test/functional/vimscript/system_spec.lua
@@ -393,7 +393,7 @@ describe('system()', function()
end)
describe('with output containing NULs', function()
- local fname = 'Xtest'
+ local fname = 'Xtest_functional_vimscript_system_nuls'
before_each(create_file_with_nuls(fname))
after_each(delete_file(fname))
@@ -549,7 +549,7 @@ describe('systemlist()', function()
end)
describe('with output containing NULs', function()
- local fname = 'Xtest'
+ local fname = 'Xtest_functional_vimscript_systemlist_nuls'
before_each(function()
command('set ff=unix')
diff --git a/test/helpers.lua b/test/helpers.lua
index 94dcf86c4d..8f06311a3c 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -3,7 +3,6 @@ local shared = vim
local assert = require('luassert')
local busted = require('busted')
local luv = require('luv')
-local relpath = require('pl.path').relpath
local Paths = require('test.cmakeconfig.paths')
assert:set_parameter('TableFormatLevel', 100)
@@ -21,6 +20,16 @@ local module = {
REMOVE_THIS = {},
}
+--- @param p string
+--- @return string
+local function relpath(p)
+ p = vim.fs.normalize(p)
+ local cwd = luv.cwd()
+ return p:gsub("^" .. cwd)
+end
+
+--- @param path string
+--- @return boolean
function module.isdir(path)
if not path then
return false
@@ -32,6 +41,8 @@ function module.isdir(path)
return stat.type == 'directory'
end
+--- @param path string
+--- @return boolean
function module.isfile(path)
if not path then
return false
@@ -43,6 +54,7 @@ function module.isfile(path)
return stat.type == 'file'
end
+--- @return string
function module.argss_to_cmd(...)
local cmd = ''
for i = 1, select('#', ...) do
@@ -457,6 +469,7 @@ function module.check_cores(app, force) -- luacheck: ignore
end
end
+--- @return string?
function module.repeated_read_cmd(...)
for _ = 1, 10 do
local stream = module.popen_r(...)
@@ -556,6 +569,9 @@ function module.concat_tables(...)
return ret
end
+--- @param str string
+--- @param leave_indent? boolean
+--- @return string
function module.dedent(str, leave_indent)
-- find minimum common indent across lines
local indent = nil
diff --git a/test/old/testdir/test_alot.vim b/test/old/testdir/test_alot.vim
index a3d240f27e..4a22315b9f 100644
--- a/test/old/testdir/test_alot.vim
+++ b/test/old/testdir/test_alot.vim
@@ -2,7 +2,6 @@
" This makes testing go faster, since Vim doesn't need to restart.
source test_backup.vim
-source test_behave.vim
source test_compiler.vim
source test_ex_equal.vim
source test_ex_undo.vim
diff --git a/test/old/testdir/test_arglist.vim b/test/old/testdir/test_arglist.vim
index fb8b17cd16..de4e5e33d6 100644
--- a/test/old/testdir/test_arglist.vim
+++ b/test/old/testdir/test_arglist.vim
@@ -183,22 +183,25 @@ func Test_argument()
let save_columns = &columns
let &columns = 79
- exe 'args ' .. join(range(1, 81))
- call assert_equal(join([
- \ '',
- \ '[1] 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 ',
- \ '2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 ',
- \ '3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 ',
- \ '4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 ',
- \ '5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 ',
- \ ], "\n"),
- \ execute('args'))
-
- " No trailing newline with one item per row.
- let long_arg = repeat('X', 81)
- exe 'args ' .. long_arg
- call assert_equal("\n[".long_arg.']', execute('args'))
- let &columns = save_columns
+ try
+ exe 'args ' .. join(range(1, 81))
+ call assert_equal(join([
+ \ '',
+ \ '[1] 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 ',
+ \ '2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 ',
+ \ '3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 ',
+ \ '4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 ',
+ \ '5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 ',
+ \ ], "\n"),
+ \ execute('args'))
+
+ " No trailing newline with one item per row.
+ let long_arg = repeat('X', 81)
+ exe 'args ' .. long_arg
+ call assert_equal("\n[".long_arg.']', execute('args'))
+ finally
+ let &columns = save_columns
+ endtry
" Setting argument list should fail when the current buffer has unsaved
" changes
diff --git a/test/old/testdir/test_behave.vim b/test/old/testdir/test_behave.vim
deleted file mode 100644
index c26bfe7ce3..0000000000
--- a/test/old/testdir/test_behave.vim
+++ /dev/null
@@ -1,29 +0,0 @@
-" Test the :behave command
-
-func Test_behave()
- behave mswin
- call assert_equal('mouse,key', &selectmode)
- call assert_equal('popup', &mousemodel)
- call assert_equal('startsel,stopsel', &keymodel)
- call assert_equal('exclusive', &selection)
-
- behave xterm
- call assert_equal('', &selectmode)
- call assert_equal('extend', &mousemodel)
- call assert_equal('', &keymodel)
- call assert_equal('inclusive', &selection)
-
- set selection&
- set mousemodel&
- set keymodel&
- set selection&
-endfunc
-
-func Test_behave_completion()
- call feedkeys(":behave \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"behave mswin xterm', @:)
-endfunc
-
-func Test_behave_error()
- call assert_fails('behave x', 'E475:')
-endfunc
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
index 1dceb43e5d..74dd7bf3c4 100644
--- a/test/old/testdir/test_cmdline.vim
+++ b/test/old/testdir/test_cmdline.vim
@@ -453,10 +453,10 @@ func Test_getcompletion()
let l = getcompletion('blahblah', 'augroup')
call assert_equal([], l)
- let l = getcompletion('', 'behave')
- call assert_true(index(l, 'mswin') >= 0)
- let l = getcompletion('not', 'behave')
- call assert_equal([], l)
+ " let l = getcompletion('', 'behave')
+ " call assert_true(index(l, 'mswin') >= 0)
+ " let l = getcompletion('not', 'behave')
+ " call assert_equal([], l)
let l = getcompletion('', 'color')
call assert_true(index(l, 'default') >= 0)
@@ -2770,6 +2770,7 @@ endfunc
" :behave suboptions fuzzy completion
func Test_fuzzy_completion_behave()
+ throw 'Skipped: Nvim removed :behave'
set wildoptions&
call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal('"behave xm', @:)
@@ -3532,6 +3533,14 @@ endfunc
func Test_setcmdline()
func SetText(text, pos)
+ call assert_equal(0, setcmdline(v:_null_string))
+ call assert_equal('', getcmdline())
+ call assert_equal(1, getcmdpos())
+
+ call assert_equal(0, setcmdline(''[: -1]))
+ call assert_equal('', getcmdline())
+ call assert_equal(1, getcmdpos())
+
autocmd CmdlineChanged * let g:cmdtype = expand('<afile>')
call assert_equal(0, setcmdline(a:text))
call assert_equal(a:text, getcmdline())
diff --git a/test/old/testdir/test_expr.vim b/test/old/testdir/test_expr.vim
index 292a504df9..1810cf6741 100644
--- a/test/old/testdir/test_expr.vim
+++ b/test/old/testdir/test_expr.vim
@@ -39,6 +39,38 @@ func Test_version()
call assert_false(has('patch-9.9.1'))
endfunc
+func Test_op_trinary()
+ call assert_equal('yes', 1 ? 'yes' : 'no')
+ call assert_equal('no', 0 ? 'yes' : 'no')
+ call assert_equal('no', 'x' ? 'yes' : 'no')
+ call assert_equal('yes', '1x' ? 'yes' : 'no')
+
+ call assert_fails('echo [1] ? "yes" : "no"', 'E745:')
+ call assert_fails('echo {} ? "yes" : "no"', 'E728:')
+endfunc
+
+func Test_op_falsy()
+ call assert_equal(v:true, v:true ?? 456)
+ call assert_equal(123, 123 ?? 456)
+ call assert_equal('yes', 'yes' ?? 456)
+ call assert_equal(0z00, 0z00 ?? 456)
+ call assert_equal([1], [1] ?? 456)
+ call assert_equal(#{one: 1}, #{one: 1} ?? 456)
+ if has('float')
+ call assert_equal(0.1, 0.1 ?? 456)
+ endif
+
+ call assert_equal(456, v:false ?? 456)
+ call assert_equal(456, 0 ?? 456)
+ call assert_equal(456, '' ?? 456)
+ call assert_equal(456, 0z ?? 456)
+ call assert_equal(456, [] ?? 456)
+ call assert_equal(456, {} ?? 456)
+ if has('float')
+ call assert_equal(456, 0.0 ?? 456)
+ endif
+endfunc
+
func Test_dict()
let d = {'': 'empty', 'a': 'a', 0: 'zero'}
call assert_equal('empty', d[''])
diff --git a/test/old/testdir/test_help_tagjump.vim b/test/old/testdir/test_help_tagjump.vim
index eae1a241e3..8a58d2f13c 100644
--- a/test/old/testdir/test_help_tagjump.vim
+++ b/test/old/testdir/test_help_tagjump.vim
@@ -35,9 +35,7 @@ func Test_help_tagjump()
help ??
call assert_equal("help", &filetype)
- " *??* tag needs patch 8.2.1794
- " call assert_true(getline('.') =~ '\*??\*')
- call assert_true(getline('.') =~ '\*g??\*')
+ call assert_true(getline('.') =~ '\*??\*')
helpclose
help :?
diff --git a/test/old/testdir/test_let.vim b/test/old/testdir/test_let.vim
index 35745e9c6a..fe7633cf11 100644
--- a/test/old/testdir/test_let.vim
+++ b/test/old/testdir/test_let.vim
@@ -327,7 +327,7 @@ func Test_let_heredoc_fails()
endfunc
END
call writefile(text, 'XheredocFail')
- call assert_fails('source XheredocFail', 'E126:')
+ call assert_fails('source XheredocFail', 'E1145:')
call delete('XheredocFail')
let text =<< trim CodeEnd
@@ -336,7 +336,7 @@ func Test_let_heredoc_fails()
endfunc
CodeEnd
call writefile(text, 'XheredocWrong')
- call assert_fails('source XheredocWrong', 'E126:')
+ call assert_fails('source XheredocWrong', 'E1145:')
call delete('XheredocWrong')
let text =<< trim TEXTend
diff --git a/test/old/testdir/test_prompt_buffer.vim b/test/old/testdir/test_prompt_buffer.vim
index 43d8bb4789..3dfbbcece6 100644
--- a/test/old/testdir/test_prompt_buffer.vim
+++ b/test/old/testdir/test_prompt_buffer.vim
@@ -271,6 +271,7 @@ func Test_prompt_appending_while_hidden()
func DoAppend()
call appendbufline('prompt', '$', 'Test')
+ return ''
endfunc
END
call writefile(script, 'XpromptBuffer', 'D')
@@ -282,12 +283,18 @@ func Test_prompt_appending_while_hidden()
call TermWait(buf)
call term_sendkeys(buf, "exit\<CR>")
- call TermWait(buf)
+ call WaitForAssert({-> assert_notmatch('-- INSERT --', term_getline(buf, 10))})
call term_sendkeys(buf, ":call DoAppend()\<CR>")
- call TermWait(buf)
- call assert_notmatch('-- INSERT --', term_getline(buf, 10))
+ call WaitForAssert({-> assert_notmatch('-- INSERT --', term_getline(buf, 10))})
+
+ call term_sendkeys(buf, "i")
+ call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 10))})
+ call term_sendkeys(buf, "\<C-R>=DoAppend()\<CR>")
+ call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 10))})
+
+ call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)
endfunc
diff --git a/test/old/testdir/test_usercommands.vim b/test/old/testdir/test_usercommands.vim
index 898f7a59f1..e22f57b6f1 100644
--- a/test/old/testdir/test_usercommands.vim
+++ b/test/old/testdir/test_usercommands.vim
@@ -303,7 +303,7 @@ func Test_CmdErrors()
call assert_fails('com! -complete=xxx DoCmd :', 'E180:')
call assert_fails('com! -complete=custom DoCmd :', 'E467:')
call assert_fails('com! -complete=customlist DoCmd :', 'E467:')
- call assert_fails('com! -complete=behave,CustomComplete DoCmd :', 'E468:')
+ " call assert_fails('com! -complete=behave,CustomComplete DoCmd :', 'E468:')
call assert_fails('com! -complete=file DoCmd :', 'E1208:')
call assert_fails('com! -nargs=0 -complete=file DoCmd :', 'E1208:')
call assert_fails('com! -nargs=x DoCmd :', 'E176:')
@@ -391,9 +391,9 @@ func Test_CmdCompletion()
call feedkeys(":com DoC\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"com DoC', @:)
- com! -nargs=1 -complete=behave DoCmd :
- call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"DoCmd mswin xterm', @:)
+ " com! -nargs=1 -complete=behave DoCmd :
+ " call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
+ " call assert_equal('"DoCmd mswin xterm', @:)
" Test for file name completion
com! -nargs=1 -complete=file DoCmd :
diff --git a/test/old/testdir/test_vimscript.vim b/test/old/testdir/test_vimscript.vim
index b0c4baf7c2..5988d6ed71 100644
--- a/test/old/testdir/test_vimscript.vim
+++ b/test/old/testdir/test_vimscript.vim
@@ -6746,7 +6746,7 @@ func Test_script_lines()
\ ])
call assert_report("Shouldn't be able to define function")
catch
- call assert_exception('Vim(function):E126: Missing :endfunction')
+ call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
endtry
" :change
@@ -6766,7 +6766,7 @@ func Test_script_lines()
\ ])
call assert_report("Shouldn't be able to define function")
catch
- call assert_exception('Vim(function):E126: Missing :endfunction')
+ call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
endtry
" :insert
@@ -6786,7 +6786,7 @@ func Test_script_lines()
\ ])
call assert_report("Shouldn't be able to define function")
catch
- call assert_exception('Vim(function):E126: Missing :endfunction')
+ call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
endtry
endfunc
@@ -7258,6 +7258,30 @@ func Test_typed_script_var()
call StopVimInTerminal(buf)
endfunc
+" Test for issue6776 {{{1
+func Test_trinary_expression()
+ try
+ call eval('0 ? 0')
+ catch
+ endtry
+ " previous failure should not cause next expression to fail
+ call assert_equal(v:false, eval(string(v:false)))
+
+ try
+ call eval('0 ? "burp')
+ catch
+ endtry
+ " previous failure should not cause next expression to fail
+ call assert_equal(v:false, eval(string(v:false)))
+
+ try
+ call eval('1 ? 0 : "burp')
+ catch
+ endtry
+ " previous failure should not cause next expression to fail
+ call assert_equal(v:false, eval(string(v:false)))
+endfunction
+
func Test_for_over_string()
let res = ''
for c in 'aéc̀d'
diff --git a/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim
index 350c69fe4e..d10a946200 100644
--- a/test/old/testdir/test_visual.vim
+++ b/test/old/testdir/test_visual.vim
@@ -485,14 +485,16 @@ endfunc
func Test_visual_block_put_invalid()
enew!
- behave mswin
+ " behave mswin
+ set selection=exclusive
norm yy
norm v)Ps/^/
" this was causing the column to become negative
silent norm ggv)P
bwipe!
- behave xterm
+ " behave xterm
+ set selection&
endfunc
" Visual modes (v V CTRL-V) followed by an operator; count; repeating
diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua
index 9dd6b76de2..883f01bd84 100644
--- a/test/unit/eval/helpers.lua
+++ b/test/unit/eval/helpers.lua
@@ -512,7 +512,8 @@ end
local function eval0(expr)
local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}),
eval.tv_clear)
- if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then
+ local evalarg = ffi.new('evalarg_T', {eval_flags = eval.EVAL_EVALUATE})
+ if eval.eval0(to_cstr(expr), tv, nil, evalarg) == 0 then
return nil
else
return tv
diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua
index 2fd37c599a..c94f0d88f7 100644
--- a/test/unit/formatc.lua
+++ b/test/unit/formatc.lua
@@ -154,6 +154,8 @@ local C_keywords = set { -- luacheck: ignore
--
-- The first one will have a lot of false positives (the line '{' for
-- example), the second one is more unique.
+--- @param string
+--- @return string
local function formatc(str)
local toks = TokeniseC(str)
local result = {}
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index 708929ad9f..10b7594a88 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -14,20 +14,15 @@ local map = global_helpers.tbl_map
local eq = global_helpers.eq
local trim = global_helpers.trim
--- C constants.
-local NULL = ffi.cast('void*', 0)
-
-local OK = 1
-local FAIL = 0
-
-local cimport
-
-- add some standard header locations
for _, p in ipairs(Paths.include_paths) do
Preprocess.add_to_include_path(p)
end
-local child_pid = nil
+local child_pid = nil --- @type integer
+--- @generic F: function
+--- @param func F
+--- @return F
local function only_separate(func)
return function(...)
if child_pid ~= 0 then
@@ -36,9 +31,20 @@ local function only_separate(func)
return func(...)
end
end
-local child_calls_init = {}
-local child_calls_mod = nil
-local child_calls_mod_once = nil
+
+--- @class ChildCall
+--- @field func function
+--- @field args any[]
+
+--- @class ChildCallLog
+--- @field func string
+--- @field args any[]
+--- @field ret any?
+
+local child_calls_init = {} --- @type ChildCall[]
+local child_calls_mod = nil --- @type ChildCall[]
+local child_calls_mod_once = nil --- @type ChildCall[]?
+
local function child_call(func, ret)
return function(...)
local child_calls = child_calls_mod or child_calls_init
@@ -53,16 +59,16 @@ end
-- Run some code at the start of the child process, before running the test
-- itself. Is supposed to be run in `before_each`.
+--- @param func function
local function child_call_once(func, ...)
if child_pid ~= 0 then
- child_calls_mod_once[#child_calls_mod_once + 1] = {
- func=func, args={...}}
+ child_calls_mod_once[#child_calls_mod_once + 1] = { func = func, args = {...} }
else
func(...)
end
end
-local child_cleanups_mod_once = nil
+local child_cleanups_mod_once = nil --- @type ChildCall[]?
-- Run some code at the end of the child process, before exiting. Is supposed to
-- be run in `before_each` because `after_each` is run after child has exited.
@@ -125,8 +131,9 @@ local pragma_pack_id = 1
-- some things are just too complex for the LuaJIT C parser to digest. We
-- usually don't need them anyway.
+--- @param body string
local function filter_complex_blocks(body)
- local result = {}
+ local result = {} --- @type string[]
for line in body:gmatch("[^\r\n]+") do
if not (string.find(line, "(^)", 1, true) ~= nil
@@ -153,18 +160,20 @@ typedef struct { char bytes[16]; } __attribute__((aligned(16))) __uint128_t;
typedef struct { char bytes[16]; } __attribute__((aligned(16))) __float128;
]]
-local preprocess_cache_init = {}
+local preprocess_cache_init = {} --- @type table<string,string>
local previous_defines_mod = ''
-local preprocess_cache_mod = nil
+local preprocess_cache_mod = nil --- @type table<string,string>
local function is_child_cdefs()
- return (os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1')
+ return os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1'
end
-- use this helper to import C files, you can pass multiple paths at once,
-- this helper will return the C namespace of the nvim library.
-cimport = function(...)
- local previous_defines, preprocess_cache, cdefs
+local function cimport(...)
+ local previous_defines --- @type string
+ local preprocess_cache --- @type table<string,string>
+ local cdefs
if is_child_cdefs() and preprocess_cache_mod then
preprocess_cache = preprocess_cache_mod
previous_defines = previous_defines_mod
@@ -180,7 +189,7 @@ cimport = function(...)
path = './' .. path
end
if not preprocess_cache[path] then
- local body
+ local body --- @type string
body, previous_defines = Preprocess.preprocess(previous_defines, path)
-- format it (so that the lines are "unique" statements), also filter out
-- Objective-C blocks
@@ -202,6 +211,7 @@ cimport = function(...)
-- (they are needed in the right order with the struct definitions,
-- otherwise luajit has wrong memory layouts for the sturcts)
if line:match("#pragma%s+pack") then
+ --- @type string
line = line .. " // " .. pragma_pack_id
pragma_pack_id = pragma_pack_id + 1
end
@@ -229,20 +239,21 @@ cimport = function(...)
return lib
end
-local cimport_immediate = function(...)
+local function cimport_immediate(...)
local saved_pid = child_pid
child_pid = 0
local err, emsg = pcall(cimport, ...)
child_pid = saved_pid
if not err then
- emsg = tostring(emsg)
- io.stderr:write(emsg .. '\n')
+ io.stderr:write(tostring(emsg) .. '\n')
assert(false)
else
return lib
end
end
+--- @param preprocess_cache table<string,string[]>
+--- @param path string
local function _cimportstr(preprocess_cache, path)
if imported:contains(path) then
return lib
@@ -265,12 +276,14 @@ end
local function alloc_log_new()
local log = {
- log={},
- lib=cimport('./src/nvim/memory.h'),
- original_functions={},
+ log={}, --- @type ChildCallLog[]
+ lib=cimport('./src/nvim/memory.h'), --- @type table<string,function>
+ original_functions={}, --- @type table<string,function>
null={['\0:is_null']=true},
}
+
local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'}
+
function log:save_original_functions()
for _, funcname in ipairs(allocator_functions) do
if not self.original_functions[funcname] then
@@ -278,13 +291,16 @@ local function alloc_log_new()
end
end
end
+
log.save_original_functions = child_call(log.save_original_functions)
+
function log:set_mocks()
for _, k in ipairs(allocator_functions) do
do
local kk = k
self.lib['mem_' .. k] = function(...)
- local log_entry = {func=kk, args={...}}
+ --- @type ChildCallLog
+ local log_entry = { func = kk, args = {...} }
self.log[#self.log + 1] = log_entry
if kk == 'free' then
self.original_functions[kk](...)
@@ -305,17 +321,21 @@ local function alloc_log_new()
end
end
end
+
log.set_mocks = child_call(log.set_mocks)
+
function log:clear()
self.log = {}
end
+
function log:check(exp)
eq(exp, self.log)
self:clear()
end
+
function log:clear_tmp_allocs(clear_null_frees)
- local toremove = {}
- local allocs = {}
+ local toremove = {} --- @type integer[]
+ local allocs = {} --- @type table<string,integer>
for i, v in ipairs(self.log) do
if v.func == 'malloc' or v.func == 'calloc' then
allocs[tostring(v.ret)] = i
@@ -338,26 +358,20 @@ local function alloc_log_new()
table.remove(self.log, toremove[i])
end
end
- function log:restore_original_functions()
- -- Do nothing: set mocks live in a separate process
- return
- --[[
- [ for k, v in pairs(self.original_functions) do
- [ self.lib['mem_' .. k] = v
- [ end
- ]]
- end
+
function log:setup()
log:save_original_functions()
log:set_mocks()
end
+
function log:before_each()
- return
end
+
function log:after_each()
- log:restore_original_functions()
end
+
log:setup()
+
return log
end
@@ -374,98 +388,109 @@ local function to_cstr(string)
end
cimport_immediate('./test/unit/fixtures/posix.h')
-local sc = {
- fork = function()
- return tonumber(ffi.C.fork())
- end,
- pipe = function()
- local ret = ffi.new('int[2]', {-1, -1})
- ffi.errno(0)
- local res = ffi.C.pipe(ret)
- if (res ~= 0) then
+
+local sc = {}
+
+function sc.fork()
+ return tonumber(ffi.C.fork())
+end
+
+function sc.pipe()
+ local ret = ffi.new('int[2]', {-1, -1})
+ ffi.errno(0)
+ local res = ffi.C.pipe(ret)
+ if (res ~= 0) then
+ local err = ffi.errno(0)
+ assert(res == 0, ("pipe() error: %u: %s"):format(
+ err, ffi.string(ffi.C.strerror(err))))
+ end
+ assert(ret[0] ~= -1 and ret[1] ~= -1)
+ return ret[0], ret[1]
+end
+
+--- @return string
+function sc.read(rd, len)
+ local ret = ffi.new('char[?]', len, {0})
+ local total_bytes_read = 0
+ ffi.errno(0)
+ while total_bytes_read < len do
+ local bytes_read = tonumber(ffi.C.read(
+ rd,
+ ffi.cast('void*', ret + total_bytes_read),
+ len - total_bytes_read))
+ if bytes_read == -1 then
local err = ffi.errno(0)
- assert(res == 0, ("pipe() error: %u: %s"):format(
- err, ffi.string(ffi.C.strerror(err))))
- end
- assert(ret[0] ~= -1 and ret[1] ~= -1)
- return ret[0], ret[1]
- end,
- read = function(rd, len)
- local ret = ffi.new('char[?]', len, {0})
- local total_bytes_read = 0
- ffi.errno(0)
- while total_bytes_read < len do
- local bytes_read = tonumber(ffi.C.read(
- rd,
- ffi.cast('void*', ret + total_bytes_read),
- len - total_bytes_read))
- if bytes_read == -1 then
- local err = ffi.errno(0)
- if err ~= ffi.C.kPOSIXErrnoEINTR then
- assert(false, ("read() error: %u: %s"):format(
- err, ffi.string(ffi.C.strerror(err))))
- end
- elseif bytes_read == 0 then
- break
- else
- total_bytes_read = total_bytes_read + bytes_read
+ if err ~= ffi.C.kPOSIXErrnoEINTR then
+ assert(false, ("read() error: %u: %s"):format(
+ err, ffi.string(ffi.C.strerror(err))))
end
+ elseif bytes_read == 0 then
+ break
+ else
+ total_bytes_read = total_bytes_read + bytes_read
end
- return ffi.string(ret, total_bytes_read)
- end,
- write = function(wr, s)
- local wbuf = to_cstr(s)
- local total_bytes_written = 0
- ffi.errno(0)
- while total_bytes_written < #s do
- local bytes_written = tonumber(ffi.C.write(
- wr,
- ffi.cast('void*', wbuf + total_bytes_written),
- #s - total_bytes_written))
- if bytes_written == -1 then
- local err = ffi.errno(0)
- if err ~= ffi.C.kPOSIXErrnoEINTR then
- assert(false, ("write() error: %u: %s ('%s')"):format(
- err, ffi.string(ffi.C.strerror(err)), s))
- end
- elseif bytes_written == 0 then
- break
- else
- total_bytes_written = total_bytes_written + bytes_written
+ end
+ return ffi.string(ret, total_bytes_read)
+end
+
+function sc.write(wr, s)
+ local wbuf = to_cstr(s)
+ local total_bytes_written = 0
+ ffi.errno(0)
+ while total_bytes_written < #s do
+ local bytes_written = tonumber(ffi.C.write(
+ wr,
+ ffi.cast('void*', wbuf + total_bytes_written),
+ #s - total_bytes_written))
+ if bytes_written == -1 then
+ local err = ffi.errno(0)
+ if err ~= ffi.C.kPOSIXErrnoEINTR then
+ assert(false, ("write() error: %u: %s ('%s')"):format(
+ err, ffi.string(ffi.C.strerror(err)), s))
end
+ elseif bytes_written == 0 then
+ break
+ else
+ total_bytes_written = total_bytes_written + bytes_written
end
- return total_bytes_written
- end,
- close = ffi.C.close,
- wait = function(pid)
- ffi.errno(0)
- local stat_loc = ffi.new('int[1]', {0})
- while true do
- local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED)
- if r == -1 then
- local err = ffi.errno(0)
- if err == ffi.C.kPOSIXErrnoECHILD then
- break
- elseif err ~= ffi.C.kPOSIXErrnoEINTR then
- assert(false, ("waitpid() error: %u: %s"):format(
- err, ffi.string(ffi.C.strerror(err))))
- end
- else
- assert(r == pid)
+ end
+ return total_bytes_written
+end
+
+sc.close = ffi.C.close
+
+--- @param pid integer
+--- @return integer
+function sc.wait(pid)
+ ffi.errno(0)
+ local stat_loc = ffi.new('int[1]', {0})
+ while true do
+ local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED)
+ if r == -1 then
+ local err = ffi.errno(0)
+ if err == ffi.C.kPOSIXErrnoECHILD then
+ break
+ elseif err ~= ffi.C.kPOSIXErrnoEINTR then
+ assert(false, ("waitpid() error: %u: %s"):format(
+ err, ffi.string(ffi.C.strerror(err))))
end
+ else
+ assert(r == pid)
end
- return stat_loc[0]
- end,
- exit = ffi.C._exit,
-}
+ end
+ return stat_loc[0]
+end
+
+sc.exit = ffi.C._exit
+--- @param lst string[]
+--- @return string
local function format_list(lst)
- local ret = ''
+ local ret = {} --- @type string[]
for _, v in ipairs(lst) do
- if ret ~= '' then ret = ret .. ', ' end
- ret = ret .. assert:format({v, n=1})[1]
+ ret[#ret+1] = assert:format({v, n=1})[1]
end
- return ret
+ return table.concat(ret, ', ')
end
if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then
@@ -513,19 +538,26 @@ local tracehelp = dedent([[
]])
local function child_sethook(wr)
- local trace_level = os.getenv('NVIM_TEST_TRACE_LEVEL')
- if not trace_level or trace_level == '' then
- trace_level = 0
- else
- trace_level = tonumber(trace_level)
+ local trace_level_str = os.getenv('NVIM_TEST_TRACE_LEVEL')
+ local trace_level = 0
+ if trace_level_str and trace_level_str ~= '' then
+ --- @type number
+ trace_level = assert(tonumber(trace_level_str))
end
+
if trace_level <= 0 then
return
end
+
local trace_only_c = trace_level <= 1
+ --- @type debuginfo?, string?, integer
local prev_info, prev_reason, prev_lnum
+
+ --- @param reason string
+ --- @param lnum integer
+ --- @param use_prev boolean
local function hook(reason, lnum, use_prev)
- local info = nil
+ local info = nil --- @type debuginfo?
if use_prev then
info = prev_info
elseif reason ~= 'tail return' then -- tail return
@@ -533,6 +565,7 @@ local function child_sethook(wr)
end
if trace_only_c and (not info or info.what ~= 'C') and not use_prev then
+ --- @cast info -nil
if info.source:sub(-9) == '_spec.lua' then
prev_info = info
prev_reason = 'saved'
@@ -573,12 +606,8 @@ local function child_sethook(wr)
end
-- assert(-1 <= lnum and lnum <= 99999)
- local lnum_s
- if lnum == -1 then
- lnum_s = 'nknwn'
- else
- lnum_s = ('%u'):format(lnum)
- end
+ local lnum_s = lnum == -1 and 'nknwn' or ('%u'):format(lnum)
+ --- @type string
local msg = ( -- lua does not support %*
''
.. msgchar
@@ -600,6 +629,7 @@ end
local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2))
+--- @type function
local _debug_log
local debug_log = only_separate(function(...)
@@ -607,6 +637,7 @@ local debug_log = only_separate(function(...)
end)
local function itp_child(wr, func)
+ --- @param s string
_debug_log = function(s)
s = s:sub(1, hook_msglen - 2)
sc.write(wr, '>' .. s .. (' '):rep(hook_msglen - 2 - #s) .. '\n')
@@ -638,7 +669,7 @@ local function itp_child(wr, func)
end
local function check_child_err(rd)
- local trace = {}
+ local trace = {} --- @type string[]
local did_traceline = false
local maxtrace = tonumber(os.getenv('NVIM_TEST_MAXTRACE')) or 1024
while true do
@@ -668,11 +699,14 @@ local function check_child_err(rd)
local len = tonumber(len_s)
neq(0, len)
if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then
+ --- @type string
err = '\nTest failed, trace:\n' .. tracehelp
for _, traceline in ipairs(trace) do
+ --- @type string
err = err .. traceline
end
end
+ --- @type string
err = err .. sc.read(rd, len + 1)
end
local eres = sc.read(rd, 2)
@@ -686,10 +720,12 @@ local function check_child_err(rd)
end
end
if not did_traceline then
+ --- @type string
err = err .. '\nNo end of trace occurred'
end
local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true)
if not cc_err then
+ --- @type string
err = err .. '\ncheck_cores failed: ' .. cc_emsg
end
end
@@ -822,9 +858,9 @@ local module = {
lib = lib,
cstr = cstr,
to_cstr = to_cstr,
- NULL = NULL,
- OK = OK,
- FAIL = FAIL,
+ NULL = ffi.cast('void*', 0),
+ OK = 1,
+ FAIL = 0,
alloc_log_new = alloc_log_new,
gen_itp = gen_itp,
only_separate = only_separate,
diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua
index 88f7a14d1f..c5fb62dce5 100644
--- a/test/unit/preprocess.lua
+++ b/test/unit/preprocess.lua
@@ -7,6 +7,9 @@ local global_helpers = require('test.helpers')
local argss_to_cmd = global_helpers.argss_to_cmd
local repeated_read_cmd = global_helpers.repeated_read_cmd
+--- @alias Compiler {path: string[], type: string}
+
+--- @type Compiler[]
local ccs = {}
local env_cc = os.getenv("CC")
@@ -27,6 +30,8 @@ table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"})
table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"})
-- parse Makefile format dependencies into a Lua table
+--- @param deps string
+--- @return string[]
local function parse_make_deps(deps)
-- remove line breaks and line concatenators
deps = deps:gsub("\n", ""):gsub("\\", "")
@@ -36,7 +41,7 @@ local function parse_make_deps(deps)
deps = deps:gsub(" +", " ")
-- split according to token (space in this case)
- local headers = {}
+ local headers = {} --- @type string[]
for token in deps:gmatch("[^%s]+") do
-- headers[token] = true
headers[#headers + 1] = token
@@ -53,57 +58,58 @@ local function parse_make_deps(deps)
return headers
end
--- will produce a string that represents a meta C header file that includes
--- all the passed in headers. I.e.:
---
--- headerize({"stdio.h", "math.h"}, true)
--- produces:
--- #include <stdio.h>
--- #include <math.h>
---
--- headerize({"vim.h", "memory.h"}, false)
--- produces:
--- #include "vim.h"
--- #include "memory.h"
+--- will produce a string that represents a meta C header file that includes
+--- all the passed in headers. I.e.:
+---
+--- headerize({"stdio.h", "math.h"}, true)
+--- produces:
+--- #include <stdio.h>
+--- #include <math.h>
+---
+--- headerize({"vim.h", "memory.h"}, false)
+--- produces:
+--- #include "vim.h"
+--- #include "memory.h"
+--- @param headers string[]
+--- @param global? boolean
+--- @return string
local function headerize(headers, global)
- local pre = '"'
- local post = pre
- if global then
- pre = "<"
- post = ">"
- end
-
- local formatted = {}
+ local fmt = global and '#include <%s>' or '#include "%s"'
+ local formatted = {} --- @type string[]
for _, hdr in ipairs(headers) do
- formatted[#formatted + 1] = "#include " ..
- tostring(pre) ..
- tostring(hdr) ..
- tostring(post)
+ formatted[#formatted + 1] = string.format(fmt, hdr)
end
return table.concat(formatted, "\n")
end
+--- @class Gcc
+--- @field path string
+--- @field preprocessor_extra_flags string[]
+--- @field get_defines_extra_flags string[]
+--- @field get_declarations_extra_flags string[]
local Gcc = {
preprocessor_extra_flags = {},
get_defines_extra_flags = {'-std=c99', '-dM', '-E'},
get_declarations_extra_flags = {'-std=c99', '-P', '-E'},
}
+--- @param name string
+--- @param args string[]?
+--- @param val string?
function Gcc:define(name, args, val)
- local define = '-D' .. name
- if args ~= nil then
- define = define .. '(' .. table.concat(args, ',') .. ')'
+ local define = string.format('-D%s', name)
+ if args then
+ define = string.format('%s(%s)', define, table.concat(args, ','))
end
- if val ~= nil then
- define = define .. '=' .. val
+ if val then
+ define = string.format('%s=%s', define, val)
end
self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = define
end
function Gcc:undefine(name)
- self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = (
- '-U' .. name)
+ self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = '-U' .. name
end
function Gcc:init_defines()
@@ -128,6 +134,8 @@ function Gcc:init_defines()
self:undefine('__BLOCKS__')
end
+--- @param obj? Compiler
+--- @return Gcc
function Gcc:new(obj)
obj = obj or {}
setmetatable(obj, self)
@@ -136,6 +144,7 @@ function Gcc:new(obj)
return obj
end
+--- @param ... string
function Gcc:add_to_include_path(...)
for i = 1, select('#', ...) do
local path = select(i, ...)
@@ -145,110 +154,115 @@ function Gcc:add_to_include_path(...)
end
-- returns a list of the headers files upon which this file relies
+--- @param hdr string
+--- @return string[]?
function Gcc:dependencies(hdr)
+ --- @type string
local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1'
- local out = io.popen(cmd)
+ local out = assert(io.popen(cmd))
local deps = out:read("*a")
out:close()
if deps then
return parse_make_deps(deps)
- else
- return nil
end
end
+--- @param defines string
+--- @return string
function Gcc:filter_standard_defines(defines)
if not self.standard_defines then
local pseudoheader_fname = 'tmp_empty_pseudoheader.h'
- local pseudoheader_file = io.open(pseudoheader_fname, 'w')
+ local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w'))
pseudoheader_file:close()
- local standard_defines = repeated_read_cmd(self.path,
- self.preprocessor_extra_flags,
- self.get_defines_extra_flags,
- {pseudoheader_fname})
+ local standard_defines = assert(repeated_read_cmd(self.path,
+ self.preprocessor_extra_flags,
+ self.get_defines_extra_flags,
+ {pseudoheader_fname}))
os.remove(pseudoheader_fname)
- self.standard_defines = {}
+ self.standard_defines = {} --- @type table<string,true>
for line in standard_defines:gmatch('[^\n]+') do
self.standard_defines[line] = true
end
end
- local ret = {}
+
+ local ret = {} --- @type string[]
for line in defines:gmatch('[^\n]+') do
if not self.standard_defines[line] then
ret[#ret + 1] = line
end
end
+
return table.concat(ret, "\n")
end
--- returns a stream representing a preprocessed form of the passed-in headers.
--- Don't forget to close the stream by calling the close() method on it.
+--- returns a stream representing a preprocessed form of the passed-in headers.
+--- Don't forget to close the stream by calling the close() method on it.
+--- @param previous_defines string
+--- @param ... string
+--- @return string, string
function Gcc:preprocess(previous_defines, ...)
-- create pseudo-header
local pseudoheader = headerize({...}, false)
local pseudoheader_fname = 'tmp_pseudoheader.h'
- local pseudoheader_file = io.open(pseudoheader_fname, 'w')
+ local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w'))
pseudoheader_file:write(previous_defines)
pseudoheader_file:write("\n")
pseudoheader_file:write(pseudoheader)
pseudoheader_file:flush()
pseudoheader_file:close()
- local defines = repeated_read_cmd(self.path, self.preprocessor_extra_flags,
- self.get_defines_extra_flags,
- {pseudoheader_fname})
+ local defines = assert(repeated_read_cmd(self.path, self.preprocessor_extra_flags,
+ self.get_defines_extra_flags,
+ {pseudoheader_fname}))
defines = self:filter_standard_defines(defines)
- local declarations = repeated_read_cmd(self.path,
- self.preprocessor_extra_flags,
- self.get_declarations_extra_flags,
- {pseudoheader_fname})
+ local declarations = assert(repeated_read_cmd(self.path,
+ self.preprocessor_extra_flags,
+ self.get_declarations_extra_flags,
+ {pseudoheader_fname}))
os.remove(pseudoheader_fname)
- assert(declarations and defines)
return declarations, defines
end
-local Clang = Gcc:new()
-local Msvc = Gcc:new()
-
-local type_to_class = {
- ["gcc"] = Gcc,
- ["clang"] = Clang,
- ["msvc"] = Msvc
-}
-
-- find the best cc. If os.exec causes problems on windows (like popping up
-- a console window) we might consider using something like this:
-- http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html#exec
+--- @param compilers Compiler[]
+--- @return Gcc?
local function find_best_cc(compilers)
for _, meta in pairs(compilers) do
- local version = io.popen(tostring(meta.path) .. " -v 2>&1")
+ local version = assert(io.popen(tostring(meta.path) .. " -v 2>&1"))
version:close()
if version then
- return type_to_class[meta.type]:new({path = meta.path})
+ return Gcc:new({path = meta.path})
end
end
- return nil
end
-- find the best cc. If os.exec causes problems on windows (like popping up
-- a console window) we might consider using something like this:
-- http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html#exec
-local cc = nil
-if cc == nil then
- cc = find_best_cc(ccs)
-end
-
-return {
- includes = function(hdr)
- return cc:dependencies(hdr)
- end,
- preprocess = function(...)
- return cc:preprocess(...)
- end,
- add_to_include_path = function(...)
- return cc:add_to_include_path(...)
- end
-}
+local cc = assert(find_best_cc(ccs))
+
+local M = {}
+
+--- @param hdr string
+--- @return string[]?
+function M.includes(hdr)
+ return cc:dependencies(hdr)
+end
+
+--- @param ... string
+--- @return string, string
+function M.preprocess(...)
+ return cc:preprocess(...)
+end
+
+--- @param ... string
+function M.add_to_include_path(...)
+ return cc:add_to_include_path(...)
+end
+
+return M
diff --git a/test/unit/set.lua b/test/unit/set.lua
index f3d68c3042..91b7e2dd70 100644
--- a/test/unit/set.lua
+++ b/test/unit/set.lua
@@ -4,10 +4,15 @@
-- other:
-- 1) index => item
-- 2) item => index
+--- @class Set
+--- @field nelem integer
+--- @field items string[]
+--- @field tbl table
local Set = {}
+--- @param items? string[]
function Set:new(items)
- local obj = {}
+ local obj = {} --- @ type Set
setmetatable(obj, self)
self.__index = self
@@ -26,8 +31,9 @@ function Set:new(items)
return obj
end
+--- @return Set
function Set:copy()
- local obj = {}
+ local obj = {} --- @ type Set
obj.nelem = self.nelem
obj.tbl = {}
obj.items = {}
@@ -43,6 +49,7 @@ function Set:copy()
end
-- adds the argument Set to this Set
+--- @param other Set
function Set:union(other)
for e in other:iterator() do
self:add(e)
@@ -57,6 +64,7 @@ function Set:union_table(t)
end
-- subtracts the argument Set from this Set
+--- @param other Set
function Set:diff(other)
if other:size() > self:size() then
-- this set is smaller than the other set
@@ -75,6 +83,7 @@ function Set:diff(other)
end
end
+--- @param it string
function Set:add(it)
if not self:contains(it) then
local idx = #self.tbl + 1
@@ -84,6 +93,7 @@ function Set:add(it)
end
end
+--- @param it string
function Set:remove(it)
if self:contains(it) then
local idx = self.items[it]
@@ -93,10 +103,13 @@ function Set:remove(it)
end
end
+--- @param it string
+--- @return boolean
function Set:contains(it)
return self.items[it] or false
end
+--- @return integer
function Set:size()
return self.nelem
end
@@ -113,29 +126,18 @@ function Set:iterator()
return pairs(self.items)
end
+--- @return string[]
function Set:to_table()
-- there might be gaps in @tbl, so we have to be careful and sort first
- local keys
- do
- local _accum_0 = { }
- local _len_0 = 1
- for idx, _ in pairs(self.tbl) do
- _accum_0[_len_0] = idx
- _len_0 = _len_0 + 1
- end
- keys = _accum_0
+ local keys = {} --- @type string[]
+ for idx, _ in pairs(self.tbl) do
+ keys[#keys+1] = idx
end
+
table.sort(keys)
- local copy
- do
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #keys do
- local idx = keys[_index_0]
- _accum_0[_len_0] = self.tbl[idx]
- _len_0 = _len_0 + 1
- end
- copy = _accum_0
+ local copy = {} --- @type string[]
+ for _, idx in ipairs(keys) do
+ copy[#copy+1] = self.tbl[idx]
end
return copy
end