aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml6
-rwxr-xr-x.github/workflows/env.sh2
-rwxr-xr-xci/before_install.sh5
-rwxr-xr-xci/before_script.sh13
-rw-r--r--ci/common/suite.sh93
-rwxr-xr-xci/install.sh4
-rwxr-xr-xci/run_lint.sh7
-rwxr-xr-xci/script.sh9
-rw-r--r--runtime/doc/api.txt7
-rw-r--r--runtime/doc/change.txt3
-rw-r--r--runtime/doc/filetype.txt4
-rw-r--r--runtime/doc/insert.txt5
-rw-r--r--runtime/doc/lua.txt23
-rw-r--r--runtime/filetype.vim6
-rw-r--r--runtime/lua/vim/diagnostic.lua46
-rw-r--r--runtime/lua/vim/lsp/util.lua4
-rw-r--r--runtime/lua/vim/shared.lua54
-rw-r--r--runtime/nvim.appdata.xml2
-rw-r--r--src/nvim/api/buffer.c4
-rw-r--r--src/nvim/api/keysets.lua2
-rw-r--r--src/nvim/api/private/helpers.c47
-rw-r--r--src/nvim/api/vim.c9
-rw-r--r--src/nvim/buffer_defs.h2
-rw-r--r--src/nvim/edit.c4
-rw-r--r--src/nvim/eval.c17
-rw-r--r--src/nvim/eval/funcs.c11
-rw-r--r--src/nvim/ex_getln.c6
-rw-r--r--src/nvim/getchar.c183
-rw-r--r--src/nvim/getchar.h4
-rw-r--r--src/nvim/keymap.h2
-rw-r--r--src/nvim/lua/spell.c2
-rw-r--r--src/nvim/normal.c16
-rw-r--r--src/nvim/terminal.c4
-rw-r--r--src/nvim/testdir/test_filetype.vim4
-rw-r--r--test/functional/api/keymap_spec.lua213
-rw-r--r--test/functional/lua/diagnostic_spec.lua5
-rw-r--r--test/functional/lua/vim_spec.lua3
-rw-r--r--test/functional/terminal/buffer_spec.lua2
-rw-r--r--test/functional/terminal/tui_spec.lua36
-rw-r--r--third-party/CMakeLists.txt4
40 files changed, 604 insertions, 269 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a7d34e06e0..c01980083a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -62,11 +62,11 @@ jobs:
matrix:
include:
- flavor: asan
- cc: clang-12
+ cc: clang-13
runner: ubuntu-20.04
os: linux
- flavor: tsan
- cc: clang-12
+ cc: clang-13
runner: ubuntu-20.04
os: linux
- cc: clang
@@ -98,7 +98,7 @@ jobs:
run: |
wget https://apt.llvm.org/llvm.sh
chmod a+x llvm.sh
- sudo ./llvm.sh 12
+ sudo ./llvm.sh 13
rm llvm.sh
- name: Install brew packages
diff --git a/.github/workflows/env.sh b/.github/workflows/env.sh
index a30e06ae26..d1bc412399 100755
--- a/.github/workflows/env.sh
+++ b/.github/workflows/env.sh
@@ -34,7 +34,7 @@ case "$FLAVOR" in
BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON"
cat <<EOF >> "$GITHUB_ENV"
CLANG_SANITIZER=ASAN_UBSAN
-SYMBOLIZER=asan_symbolize-12
+SYMBOLIZER=asan_symbolize-13
ASAN_OPTIONS=detect_leaks=1:check_initialization_order=1:log_path=$GITHUB_WORKSPACE/build/log/asan
UBSAN_OPTIONS=print_stacktrace=1 log_path=$GITHUB_WORKSPACE/build/log/ubsan
EOF
diff --git a/ci/before_install.sh b/ci/before_install.sh
index f12f972fe0..f4ea720d09 100755
--- a/ci/before_install.sh
+++ b/ci/before_install.sh
@@ -17,8 +17,7 @@ echo 'Python info:'
pyenv versions
) 2>&1 | sed 's/^/ /' || true
-# Use pyenv, but not for OSX on Travis, where it only has the "system" version.
-if [[ "${TRAVIS_OS_NAME}" != osx ]] && command -v pyenv; then
+if command -v pyenv; then
echo 'Setting Python versions via pyenv'
# Prefer Python 2 over 3 (more conservative).
@@ -38,7 +37,7 @@ fi
echo "Install node (LTS)"
-if [[ "${TRAVIS_OS_NAME}" == osx ]] || [ ! -f ~/.nvm/nvm.sh ]; then
+if [ ! -f ~/.nvm/nvm.sh ]; then
curl -o ~/.nvm/nvm.sh https://raw.githubusercontent.com/creationix/nvm/master/nvm.sh
fi
diff --git a/ci/before_script.sh b/ci/before_script.sh
index 701fe1d9eb..f7216338d4 100755
--- a/ci/before_script.sh
+++ b/ci/before_script.sh
@@ -6,12 +6,6 @@ set -o pipefail
CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${CI_DIR}/common/build.sh"
-# Enable ipv6 on Travis. ref: a39c8b7ce30d
-if test -n "${TRAVIS_OS_NAME}" && ! test "${TRAVIS_OS_NAME}" = osx ; then
- echo "before_script.sh: enable ipv6"
- sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
-fi
-
# Test some of the configuration variables.
if [[ -n "${GCOV}" ]] && [[ ! $(type -P "${GCOV}") ]]; then
echo "\$GCOV: '${GCOV}' is not executable."
@@ -27,13 +21,6 @@ ccache -s
# Reset ccache stats for real results in before_cache.
ccache --zero-stats
-if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
- # Adds user to a dummy group.
- # That allows to test changing the group of the file by `os_fchown`.
- sudo dscl . -create /Groups/chown_test
- sudo dscl . -append /Groups/chown_test GroupMembership "${USER}"
-fi
-
# Compile dependencies.
build_deps
diff --git a/ci/common/suite.sh b/ci/common/suite.sh
index 038b116c5a..2591ea37a5 100644
--- a/ci/common/suite.sh
+++ b/ci/common/suite.sh
@@ -98,99 +98,6 @@ run_test() {
fi
}
-run_test_wd() {
- local hang_ok=
- if test "$1" = "--allow-hang" ; then
- hang_ok=1
- shift
- fi
-
- local timeout="$1"
- test $# -gt 0 && shift
-
- local cmd="$1"
- test $# -gt 0 && shift
-
- local restart_cmd="$1"
- : ${restart_cmd:=true}
- test $# -gt 0 && shift
-
- local test_name="$1"
- : ${test_name:=$cmd}
- test $# -gt 0 && shift
-
- local output_file="$(mktemp)"
- local status_file="$(mktemp)"
- local sid_file="$(mktemp)"
-
- local restarts=5
- local prev_tmpsize=-1
- while test $restarts -gt 0 ; do
- : > "$status_file"
- : > "$sid_file"
- setsid \
- env \
- output_file="$output_file" \
- status_file="$status_file" \
- sid_file="$sid_file" \
- cmd="$cmd" \
- CI_DIR="$CI_DIR" \
- sh -c '
- . "${CI_DIR}/common/test.sh"
- ps -o sid= > "$sid_file"
- (
- ret=0
- if ! eval "$cmd" 2>&1 ; then
- ret=1
- fi
- echo "$ret" > "$status_file"
- ) | tee -a "$output_file"
- '
- while test "$(stat -c "%s" "$status_file")" -eq 0 ; do
- prev_tmpsize=$tmpsize
- sleep $timeout
- tmpsize="$(stat -c "%s" "$output_file")"
- if test $tempsize -eq $prev_temsize ; then
- # no output, assuming either hang or exit
- break
- fi
- done
- restarts=$(( restarts - 1 ))
- if test "$(stat -c "%s" "$status_file")" -eq 0 ; then
- # Status file not updated, assuming hang
-
- # SID not known, this should not ever happen
- if test "$(stat -c "%s" "$sid_file")" -eq 0 ; then
- fail "$test_name" E "Shell did not run"
- break
- fi
-
- # Kill all processes which belong to one session: should get rid of test
- # processes as well as sh itself.
- pkill -KILL -s$(cat "$sid_file")
-
- if test $restarts -eq 0 ; then
- if test -z "$hang_ok" ; then
- fail "$test_name" E "Test hang up"
- fi
- else
- echo "Test ${test_name} hang up, restarting"
- eval "$restart_cmd"
- fi
- else
- local new_failed="$(cat "$status_file")"
- if test "$new_failed" != "0" ; then
- fail "$test_name" F "Test failed in run_test_wd"
- fi
- break
- fi
- done
-
- rm -f "$output_file"
- rm -f "$status_file"
- rm -f "$sid_file"
-}
-
ended_successfully() {
if test -f "${FAIL_SUMMARY_FILE}" ; then
echo 'Test failed, complete summary:'
diff --git a/ci/install.sh b/ci/install.sh
index 1edc1138ee..72e193a353 100755
--- a/ci/install.sh
+++ b/ci/install.sh
@@ -8,10 +8,6 @@ if [[ "${CI_TARGET}" == lint ]]; then
exit
fi
-if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
- export PATH="/usr/local/opt/ccache/libexec:$PATH"
-fi
-
# Use default CC to avoid compilation problems when installing Python modules.
echo "Install neovim module for Python 3."
CC=cc python3 -m pip -q install --user --upgrade pynvim
diff --git a/ci/run_lint.sh b/ci/run_lint.sh
index 8373a3cb36..314976edc2 100755
--- a/ci/run_lint.sh
+++ b/ci/run_lint.sh
@@ -25,12 +25,7 @@ run_test 'make shlint' shlint
exit_suite --continue
enter_suite single-includes
-CLICOLOR_FORCE=1 run_test_wd \
- --allow-hang \
- 10s \
- 'make check-single-includes' \
- 'csi_clean' \
- single-includes
+run_test 'make check-single-includes' single-includes
exit_suite --continue
end_tests
diff --git a/ci/script.sh b/ci/script.sh
index c8025ce34d..74fc4eda6c 100755
--- a/ci/script.sh
+++ b/ci/script.sh
@@ -3,14 +3,7 @@
set -e
set -o pipefail
-# This will pass the environment variables down to a bash process which runs
-# as $USER, while retaining the environment variables defined and belonging
-# to secondary groups given above in usermod.
-if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
- sudo -E su "${USER}" -c "ci/run_${CI_TARGET}.sh"
-else
- ci/run_${CI_TARGET}.sh
-fi
+ci/run_${CI_TARGET}.sh
if [[ -s "${GCOV_ERROR_FILE}" ]]; then
echo '=== Unexpected gcov errors: ==='
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index efffca72ad..e73634c632 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -1580,8 +1580,11 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()*
{rhs} Right-hand-side |{rhs}| of the mapping.
{opts} Optional parameters map. Accepts all
|:map-arguments| as keys excluding |<buffer>| but
- including |noremap|. Values are Booleans. Unknown
- key is an error.
+ including |noremap| and "desc". |desc| can be used
+ to give a description to keymap. When called from
+ Lua, also accepts a "callback" key that takes a
+ Lua function to call when the mapping is executed.
+ Values are Booleans. Unknown key is an error.
nvim_set_option({name}, {value}) *nvim_set_option()*
Sets the global value of an option.
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index ffdd8427f9..953f097a92 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -1595,7 +1595,8 @@ r Automatically insert the current comment leader after hitting
<Enter> in Insert mode.
*fo-o*
o Automatically insert the current comment leader after hitting 'o' or
- 'O' in Normal mode.
+ 'O' in Normal mode. In case comment is unwanted in a specific place
+ use CTRL-U to quickly delete it. |i_CTRL-U|
*fo-q*
q Allow formatting of comments with "gq".
Note that formatting will not change blank lines or lines containing
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index bbbe71ec3a..8ca763b45e 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -314,12 +314,12 @@ define yourself. There are a few ways to avoid this:
You need to define your own mapping before the plugin is loaded (before
editing a file of that type). The plugin will then skip installing the
default mapping.
- *no_mail_maps*
+ *no_mail_maps* *g:no_mail_maps*
3. Disable defining mappings for a specific filetype by setting a variable,
which contains the name of the filetype. For the "mail" filetype this
would be: >
:let no_mail_maps = 1
-< *no_plugin_maps*
+< *no_plugin_maps* *g:no_plugin_maps*
4. Disable defining mappings for all filetypes by setting a variable: >
:let no_plugin_maps = 1
<
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index fd1d0f8ea6..d74d7cafa8 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -76,6 +76,8 @@ CTRL-U Delete all entered characters before the cursor in the current
line. If there are no newly entered characters and
'backspace' is not empty, delete all characters before the
cursor in the current line.
+ If C-indenting is enabled the indent will be adjusted if the
+ line becomes blank.
See |i_backspacing| about joining lines.
*i_CTRL-U-default*
By default, sets a new undo point before deleting.
@@ -1878,6 +1880,9 @@ When 'autoindent' is on, the indent for a new line is obtained from the
previous line. When 'smartindent' or 'cindent' is on, the indent for a line
is automatically adjusted for C programs.
+'formatoptions' can be set to copy the comment leader when opening a new
+line.
+
'textwidth' can be set to the maximum width for a line. When a line becomes
too long when appending characters a line break is automatically inserted.
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 036454702b..eaa25f0961 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1656,16 +1656,25 @@ validate({opt}) *vim.validate()*
=> error('arg1: expected even number, got 3')
<
+ If multiple types are valid they can be given as a list. >
+
+ vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
+ => NOP (success)
+
+ vim.validate{arg1={1, {'string', table'}}}
+ => error('arg1: expected string|table, got number')
+<
+
Parameters: ~
- {opt} Map of parameter names to validations. Each key is
- a parameter name; each value is a tuple in one of
- these forms:
+ {opt} table of parameter names to validations. Each key
+ is a parameter name; each value is a tuple in one
+ of these forms:
1. (arg_value, type_name, optional)
• arg_value: argument value
- • type_name: string type name, one of: ("table",
- "t", "string", "s", "number", "n", "boolean",
- "b", "function", "f", "nil", "thread",
- "userdata")
+ • type_name: string|table type name, one of:
+ ("table", "t", "string", "s", "number", "n",
+ "boolean", "b", "function", "f", "nil",
+ "thread", "userdata") or list of them.
• optional: (optional) boolean, if true, `nil`
is valid
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 104836862f..1f9b90ed78 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -959,9 +959,9 @@ au BufNewFile,BufRead lilo.conf setf lilo
" Lisp (*.el = ELisp, *.cl = Common Lisp)
" *.jl was removed, it's also used for Julia, better skip than guess wrong.
if has("fname_case")
- au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,*.L,.emacs,.sawfishrc setf lisp
+ au BufNewFile,BufRead *.lsp,*.lisp,*.asd,*.el,*.cl,*.L,.emacs,.sawfishrc setf lisp
else
- au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,.emacs,.sawfishrc setf lisp
+ au BufNewFile,BufRead *.lsp,*.lisp,*.asd,*.el,*.cl,.emacs,.sawfishrc setf lisp
endif
" SBCL implementation of Common Lisp
@@ -1665,7 +1665,7 @@ au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh
au BufNewFile,BufRead *.zsh setf zsh
" Scheme
-au BufNewFile,BufRead *.scm,*.ss,*.rkt,*.rktd,*.rktl setf scheme
+au BufNewFile,BufRead *.scm,*.ss,*.sld,*.rkt,*.rktd,*.rktl setf scheme
" Screen RC
au BufNewFile,BufRead .screenrc,screenrc setf screen
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index ddcead6ec5..1d06023a4f 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -644,7 +644,11 @@ function M.set(namespace, bufnr, diagnostics, opts)
vim.validate {
namespace = {namespace, 'n'},
bufnr = {bufnr, 'n'},
- diagnostics = {diagnostics, 't'},
+ diagnostics = {
+ diagnostics,
+ vim.tbl_islist,
+ "a list of diagnostics",
+ },
opts = {opts, 't', true},
}
@@ -810,7 +814,11 @@ M.handlers.signs = {
vim.validate {
namespace = {namespace, 'n'},
bufnr = {bufnr, 'n'},
- diagnostics = {diagnostics, 't'},
+ diagnostics = {
+ diagnostics,
+ vim.tbl_islist,
+ "a list of diagnostics",
+ },
opts = {opts, 't', true},
}
@@ -873,7 +881,11 @@ M.handlers.underline = {
vim.validate {
namespace = {namespace, 'n'},
bufnr = {bufnr, 'n'},
- diagnostics = {diagnostics, 't'},
+ diagnostics = {
+ diagnostics,
+ vim.tbl_islist,
+ "a list of diagnostics",
+ },
opts = {opts, 't', true},
}
@@ -921,7 +933,11 @@ M.handlers.virtual_text = {
vim.validate {
namespace = {namespace, 'n'},
bufnr = {bufnr, 'n'},
- diagnostics = {diagnostics, 't'},
+ diagnostics = {
+ diagnostics,
+ vim.tbl_islist,
+ "a list of diagnostics",
+ },
opts = {opts, 't', true},
}
@@ -1081,7 +1097,11 @@ function M.show(namespace, bufnr, diagnostics, opts)
vim.validate {
namespace = { namespace, 'n', true },
bufnr = { bufnr, 'n', true },
- diagnostics = { diagnostics, 't', true },
+ diagnostics = {
+ diagnostics,
+ function(v) return v == nil or vim.tbl_islist(v) end,
+ "a list of diagnostics",
+ },
opts = { opts, 't', true },
}
@@ -1526,7 +1546,13 @@ local errlist_type_map = {
---@param diagnostics table List of diagnostics |diagnostic-structure|.
---@return array of quickfix list items |setqflist-what|
function M.toqflist(diagnostics)
- vim.validate { diagnostics = {diagnostics, 't'} }
+ vim.validate {
+ diagnostics = {
+ diagnostics,
+ vim.tbl_islist,
+ "a list of diagnostics",
+ },
+ }
local list = {}
for _, v in ipairs(diagnostics) do
@@ -1557,7 +1583,13 @@ end
--- |getloclist()|.
---@return array of diagnostics |diagnostic-structure|
function M.fromqflist(list)
- vim.validate { list = {list, 't'} }
+ vim.validate {
+ list = {
+ list,
+ vim.tbl_islist,
+ "a list of quickfix items",
+ },
+ }
local diagnostics = {}
for _, item in ipairs(list) do
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 5921eb5bf0..ae667c4106 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1000,10 +1000,10 @@ function M.jump_to_location(location)
--- Jump to new location (adjusting for UTF-16 encoding of characters)
api.nvim_set_current_buf(bufnr)
- api.nvim_buf_set_option(0, 'buflisted', true)
+ api.nvim_buf_set_option(bufnr, 'buflisted', true)
local range = location.range or location.targetSelectionRange
local row = range.start.line
- local col = get_line_byte_from_position(0, range.start)
+ local col = get_line_byte_from_position(bufnr, range.start)
api.nvim_win_set_cursor(0, {row + 1, col})
-- Open folds under the cursor
vim.cmd("normal! zv")
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 1cf618725d..e170befa4c 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -526,13 +526,23 @@ end
--- => error('arg1: expected even number, got 3')
--- </pre>
---
----@param opt Map of parameter names to validations. Each key is a parameter
+--- If multiple types are valid they can be given as a list.
+--- <pre>
+--- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
+--- => NOP (success)
+---
+--- vim.validate{arg1={1, {'string', table'}}}
+--- => error('arg1: expected string|table, got number')
+---
+--- </pre>
+---
+---@param opt table of parameter names to validations. Each key is a parameter
--- name; each value is a tuple in one of these forms:
--- 1. (arg_value, type_name, optional)
--- - arg_value: argument value
---- - type_name: string type name, one of: ("table", "t", "string",
+--- - type_name: string|table type name, one of: ("table", "t", "string",
--- "s", "number", "n", "boolean", "b", "function", "f", "nil",
---- "thread", "userdata")
+--- "thread", "userdata") or list of them.
--- - optional: (optional) boolean, if true, `nil` is valid
--- 2. (arg_value, fn, msg)
--- - arg_value: argument value
@@ -571,31 +581,43 @@ do
end
local val = spec[1] -- Argument value.
- local t = spec[2] -- Type name, or callable.
+ local types = spec[2] -- Type name, or callable.
local optional = (true == spec[3])
- if type(t) == 'string' then
- local t_name = type_names[t]
- if not t_name then
- return false, string.format('invalid type name: %s', t)
- end
+ if type(types) == 'string' then
+ types = {types}
+ end
- if (not optional or val ~= nil) and not _is_type(val, t_name) then
- return false, string.format("%s: expected %s, got %s", param_name, t_name, type(val))
- end
- elseif vim.is_callable(t) then
+ if vim.is_callable(types) then
-- Check user-provided validation function.
- local valid, optional_message = t(val)
+ local valid, optional_message = types(val)
if not valid then
- local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val)
+ local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), tostring(val))
if optional_message ~= nil then
error_message = error_message .. string.format(". Info: %s", optional_message)
end
return false, error_message
end
+ elseif type(types) == 'table' then
+ local success = false
+ for i, t in ipairs(types) do
+ local t_name = type_names[t]
+ if not t_name then
+ return false, string.format('invalid type name: %s', t)
+ end
+ types[i] = t_name
+
+ if (optional and val == nil) or _is_type(val, t_name) then
+ success = true
+ break
+ end
+ end
+ if not success then
+ return false, string.format("%s: expected %s, got %s", param_name, table.concat(types, '|'), type(val))
+ end
else
- return false, string.format("invalid type name: %s", tostring(t))
+ return false, string.format("invalid type name: %s", tostring(types))
end
end
diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml
index 225dd79878..4ad656f1a3 100644
--- a/runtime/nvim.appdata.xml
+++ b/runtime/nvim.appdata.xml
@@ -26,7 +26,9 @@
</screenshots>
<releases>
+ <release date="2021-12-31" version="0.6.1"/>
<release date="2021-11-30" version="0.6.0"/>
+ <release date="2021-09-26" version="0.5.1"/>
<release date="2021-07-02" version="0.5.0"/>
<release date="2020-08-04" version="0.4.4"/>
<release date="2019-11-06" version="0.4.3"/>
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index bacb991c4b..2d5403d4b8 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -835,7 +835,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// @param[out] err Error details, if any
/// @returns Array of maparg()-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle.
-ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
+ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, String mode, Error *err)
FUNC_API_SINCE(3)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -844,7 +844,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
return (Array)ARRAY_DICT_INIT;
}
- return keymap_array(mode, buf);
+ return keymap_array(mode, buf, channel_id == LUA_INTERNAL_CALL);
}
/// Sets a buffer-local |mapping| for the given mode.
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index e47dec9eb7..97ee885ff6 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -29,6 +29,8 @@ return {
"script";
"expr";
"unique";
+ "callback";
+ "desc";
};
get_commands = {
"builtin";
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 962fce6952..4884a44cea 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -594,6 +594,7 @@ Array string_to_array(const String input, bool crlf)
void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs,
Dict(keymap) *opts, Error *err)
{
+ LuaRef lua_funcref = LUA_NOREF;
bool global = (buffer == -1);
if (global) {
buffer = 0;
@@ -604,6 +605,10 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
return;
}
+ if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) {
+ lua_funcref = opts->callback.data.luaref;
+ opts->callback.data.luaref = LUA_NOREF;
+ }
MapArguments parsed_args = MAP_ARGUMENTS_INIT;
if (opts) {
#define KEY_TO_BOOL(name) \
@@ -623,9 +628,13 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
parsed_args.buffer = !global;
set_maparg_lhs_rhs((char_u *)lhs.data, lhs.size,
- (char_u *)rhs.data, rhs.size,
+ (char_u *)rhs.data, rhs.size, lua_funcref,
CPO_TO_CPO_FLAGS, &parsed_args);
-
+ if (opts != NULL && opts->desc.type == kObjectTypeString) {
+ parsed_args.desc = xstrdup(opts->desc.data.string.data);
+ } else {
+ parsed_args.desc = NULL;
+ }
if (parsed_args.lhs_len > MAXMAPLEN) {
api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
goto fail_and_free;
@@ -658,7 +667,8 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
bool is_noremap = parsed_args.noremap;
assert(!(is_unmap && is_noremap));
- if (!is_unmap && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
+ if (!is_unmap && lua_funcref == LUA_NOREF
+ && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop>
parsed_args.rhs_is_noop = true;
} else {
@@ -668,9 +678,13 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
api_set_error(err, kErrorTypeValidation, "Parsing of nonempty RHS failed: %s", rhs.data);
goto fail_and_free;
}
- } else if (is_unmap && parsed_args.rhs_len) {
- api_set_error(err, kErrorTypeValidation,
- "Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
+ } else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) {
+ if (parsed_args.rhs_len) {
+ api_set_error(err, kErrorTypeValidation,
+ "Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
+ } else {
+ api_set_error(err, kErrorTypeValidation, "Gave nonempty RHS for unmap");
+ }
goto fail_and_free;
}
@@ -700,9 +714,12 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
goto fail_and_free;
} // switch
+ parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success
fail_and_free:
+ NLUA_CLEAR_REF(parsed_args.rhs_lua);
xfree(parsed_args.rhs);
xfree(parsed_args.orig_rhs);
+ XFREE_CLEAR(parsed_args.desc);
return;
}
@@ -1052,8 +1069,9 @@ void api_set_error(Error *err, ErrorType errType, const char *format, ...)
///
/// @param mode The abbreviation for the mode
/// @param buf The buffer to get the mapping array. NULL for global
+/// @param from_lua Whether it is called from internal lua api.
/// @returns Array of maparg()-like dictionaries describing mappings
-ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
+ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
{
Array mappings = ARRAY_DICT_INIT;
dict_T *const dict = tv_dict_alloc();
@@ -1073,8 +1091,19 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
mapblock_fill_dict(dict, current_maphash, buffer_value, false);
- ADD(mappings, vim_to_object((typval_T[]) { { .v_type = VAR_DICT, .vval.v_dict = dict } }));
-
+ Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT,
+ .vval.v_dict = dict } });
+ if (from_lua) {
+ Dictionary d = api_dict.data.dictionary;
+ for (size_t j = 0; j < d.size; j++) {
+ if (strequal("callback", d.items[j].key.data)) {
+ d.items[j].value.type = kObjectTypeLuaRef;
+ d.items[j].value.data.luaref = api_new_luaref((LuaRef)d.items[j].value.data.integer);
+ break;
+ }
+ }
+ }
+ ADD(mappings, api_dict);
tv_dict_clear(dict);
}
}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index dfc606f927..693d2083e6 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1538,10 +1538,10 @@ Dictionary nvim_get_mode(void)
/// @param mode Mode short-name ("n", "i", "v", ...)
/// @returns Array of maparg()-like dictionaries describing mappings.
/// The "buffer" key is always zero.
-ArrayOf(Dictionary) nvim_get_keymap(String mode)
+ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode)
FUNC_API_SINCE(3)
{
- return keymap_array(mode, NULL);
+ return keymap_array(mode, NULL, channel_id == LUA_INTERNAL_CALL);
}
/// Sets a global |mapping| for the given mode.
@@ -1566,7 +1566,10 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// @param lhs Left-hand-side |{lhs}| of the mapping.
/// @param rhs Right-hand-side |{rhs}| of the mapping.
/// @param opts Optional parameters map. Accepts all |:map-arguments|
-/// as keys excluding |<buffer>| but including |noremap|.
+/// as keys excluding |<buffer>| but including |noremap| and "desc".
+/// |desc| can be used to give a description to keymap.
+/// When called from Lua, also accepts a "callback" key that takes
+/// a Lua function to call when the mapping is executed.
/// Values are Booleans. Unknown key is an error.
/// @param[out] err Error details, if any.
void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err)
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 49e527e98b..63a550c017 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -352,6 +352,7 @@ struct mapblock {
char_u *m_keys; // mapped from, lhs
char_u *m_str; // mapped to, rhs
char_u *m_orig_str; // rhs as entered by the user
+ LuaRef m_luaref; // lua function reference as rhs
int m_keylen; // strlen(m_keys)
int m_mode; // valid mode
int m_noremap; // if non-zero no re-mapping for m_str
@@ -359,6 +360,7 @@ struct mapblock {
char m_nowait; // <nowait> used
char m_expr; // <expr> used, m_str is an expression
sctx_T m_script_ctx; // SCTX where map was defined
+ char *m_desc; // description of keymap
};
/// Used for highlighting in the status line.
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 3517b3244e..5eef4350b7 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1076,6 +1076,10 @@ static int insert_handle_key(InsertState *s)
case K_COMMAND: // some command
do_cmdline(NULL, getcmdkeycmd, NULL, 0);
+ goto check_pum;
+
+ case K_LUA:
+ map_execute_lua();
check_pum:
// TODO(bfredl): Not entirely sure this indirection is necessary
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 86384bc5b2..6dbdc09c3b 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -7299,12 +7299,19 @@ void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buf
noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
}
- if (compatible) {
- tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
+ if (mp->m_luaref != LUA_NOREF) {
+ tv_dict_add_nr(dict, S_LEN("callback"), mp->m_luaref);
} else {
- tv_dict_add_allocated_str(dict, S_LEN("rhs"),
- str2special_save((const char *)mp->m_str, false,
- true));
+ if (compatible) {
+ tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
+ } else {
+ tv_dict_add_allocated_str(dict, S_LEN("rhs"),
+ str2special_save((const char *)mp->m_str, false,
+ true));
+ }
+ }
+ if (mp->m_desc != NULL) {
+ tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc));
}
tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 32026282cf..7701688b49 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3991,7 +3991,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
MotionType reg_type = get_reg_type(regname, &reglen);
format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf));
- rettv->v_type = VAR_STRING;
rettv->vval.v_string = (char_u *)xstrdup(buf);
}
@@ -5980,6 +5979,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
{
char_u *keys_buf = NULL;
char_u *rhs;
+ LuaRef rhs_lua;
int mode;
int abbr = FALSE;
int get_dict = FALSE;
@@ -6016,7 +6016,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
CPO_TO_CPO_FLAGS);
- rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local);
+ rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
xfree(keys_buf);
if (!get_dict) {
@@ -6027,10 +6027,15 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
} else {
rettv->vval.v_string = (char_u *)str2special_save((char *)rhs, false, false);
}
+ } else if (rhs_lua != LUA_NOREF) {
+ size_t msglen = 100;
+ char *msg = (char *)xmalloc(msglen);
+ snprintf(msg, msglen, "<Lua function %d>", mp->m_luaref);
+ rettv->vval.v_string = (char_u *)msg;
}
} else {
tv_dict_alloc_ret(rettv);
- if (rhs != NULL) {
+ if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
// Return a dictionary.
mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true);
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 67fd5a4efc..78b8e43e65 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1024,11 +1024,13 @@ static int command_line_execute(VimState *state, int key)
CommandLineState *s = (CommandLineState *)state;
s->c = key;
- if (s->c == K_EVENT || s->c == K_COMMAND) {
+ if (s->c == K_EVENT || s->c == K_COMMAND || s->c == K_LUA) {
if (s->c == K_EVENT) {
state_handle_k_event();
- } else {
+ } else if (s->c == K_COMMAND) {
do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
+ } else {
+ map_execute_lua();
}
if (!cmdline_was_last_drawn) {
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 424bf758e2..5565e17597 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -15,6 +15,7 @@
#include <stdbool.h>
#include <string.h>
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/buffer_defs.h"
@@ -1902,7 +1903,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// complete match
if (keylen >= 0 && keylen <= typebuf.tb_len) {
- char_u *map_str;
+ char_u *map_str = NULL;
int save_m_expr;
int save_m_noremap;
int save_m_silent;
@@ -1947,6 +1948,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
save_m_silent = mp->m_silent;
char_u *save_m_keys = NULL; // only saved when needed
char_u *save_m_str = NULL; // only saved when needed
+ LuaRef save_m_luaref = mp->m_luaref;
// Handle ":map <expr>": evaluate the {rhs} as an
// expression. Also save and restore the command line
@@ -1959,8 +1961,10 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
may_garbage_collect = false;
save_m_keys = vim_strsave(mp->m_keys);
- save_m_str = vim_strsave(mp->m_str);
- map_str = eval_map_expr(save_m_str, NUL);
+ if (save_m_luaref == LUA_NOREF) {
+ save_m_str = vim_strsave(mp->m_str);
+ }
+ map_str = eval_map_expr(mp, NUL);
vgetc_busy = save_vgetc_busy;
may_garbage_collect = save_may_garbage_collect;
} else {
@@ -2618,11 +2622,13 @@ int fix_input_buffer(char_u *buf, int len)
/// @param[in] orig_lhs Original mapping LHS, with characters to replace.
/// @param[in] orig_lhs_len `strlen` of orig_lhs.
/// @param[in] orig_rhs Original mapping RHS, with characters to replace.
+/// @param[in] rhs_lua Lua reference for Lua maps.
/// @param[in] orig_rhs_len `strlen` of orig_rhs.
/// @param[in] cpo_flags See param docs for @ref replace_termcodes.
/// @param[out] mapargs MapArguments struct holding the replaced strings.
-void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const char_u *orig_rhs,
- const size_t orig_rhs_len, int cpo_flags, MapArguments *mapargs)
+void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len,
+ const char_u *orig_rhs, const size_t orig_rhs_len,
+ LuaRef rhs_lua, int cpo_flags, MapArguments *mapargs)
{
char_u *lhs_buf = NULL;
char_u *rhs_buf = NULL;
@@ -2638,22 +2644,34 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const
true, true, true, cpo_flags);
mapargs->lhs_len = STRLEN(replaced);
STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs));
+ mapargs->rhs_lua = rhs_lua;
- mapargs->orig_rhs_len = orig_rhs_len;
- mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u));
- STRLCPY(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1);
+ if (rhs_lua == LUA_NOREF) {
+ mapargs->orig_rhs_len = orig_rhs_len;
+ mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u));
+ STRLCPY(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1);
- if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing
- mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char
- mapargs->rhs_len = 0;
- mapargs->rhs_is_noop = true;
+ if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing
+ mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char
+ mapargs->rhs_len = 0;
+ mapargs->rhs_is_noop = true;
+ } else {
+ replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf,
+ false, true, true, cpo_flags);
+ mapargs->rhs_len = STRLEN(replaced);
+ mapargs->rhs_is_noop = false;
+ mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u));
+ STRLCPY(mapargs->rhs, replaced, mapargs->rhs_len + 1);
+ }
} else {
- replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf,
- false, true, true, cpo_flags);
- mapargs->rhs_len = STRLEN(replaced);
- mapargs->rhs_is_noop = false;
- mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u));
- STRLCPY(mapargs->rhs, replaced, mapargs->rhs_len + 1);
+ char tmp_buf[64];
+ // stores <lua>ref_no<cr> in map_str
+ mapargs->orig_rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "<LUA>%d<CR>", rhs_lua);
+ mapargs->orig_rhs = vim_strsave((char_u *)tmp_buf);
+ mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL,
+ (char_u)KEY2TERMCAP0(K_LUA), KEY2TERMCAP1(K_LUA),
+ rhs_lua);
+ mapargs->rhs = vim_strsave((char_u *)tmp_buf);
}
xfree(lhs_buf);
@@ -2765,7 +2783,7 @@ int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs)
size_t orig_rhs_len = STRLEN(rhs_start);
set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len,
- rhs_start, orig_rhs_len,
+ rhs_start, orig_rhs_len, LUA_NOREF,
CPO_TO_CPO_FLAGS, &parsed_args);
xfree(lhs_to_replace);
@@ -2827,7 +2845,7 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
validate_maphash();
bool has_lhs = (args->lhs[0] != NUL);
- bool has_rhs = (args->rhs[0] != NUL) || args->rhs_is_noop;
+ bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
// check for :unmap without argument
if (maptype == 1 && !has_lhs) {
@@ -3017,10 +3035,14 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
} else { // new rhs for existing entry
mp->m_mode &= ~mode; // remove mode bits
if (mp->m_mode == 0 && !did_it) { // reuse entry
- xfree(mp->m_str);
+ XFREE_CLEAR(mp->m_str);
+ XFREE_CLEAR(mp->m_orig_str);
+ XFREE_CLEAR(mp->m_desc);
+ NLUA_CLEAR_REF(mp->m_luaref);
+
mp->m_str = vim_strsave(rhs);
- xfree(mp->m_orig_str);
mp->m_orig_str = vim_strsave(orig_rhs);
+ mp->m_luaref = args->rhs_lua;
mp->m_noremap = noremap;
mp->m_nowait = args->nowait;
mp->m_silent = args->silent;
@@ -3028,6 +3050,9 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
mp->m_expr = args->expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ if (args->desc != NULL) {
+ mp->m_desc = xstrdup(args->desc);
+ }
did_it = true;
}
}
@@ -3096,6 +3121,7 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
mp->m_keys = vim_strsave(lhs);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
+ mp->m_luaref = args->rhs_lua;
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = args->nowait;
@@ -3104,6 +3130,10 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
mp->m_expr = args->expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ mp->m_desc = NULL;
+ if (args->desc != NULL) {
+ mp->m_desc = xstrdup(args->desc);
+ }
// add the new entry in front of the abbrlist or maphash[] list
if (is_abbrev) {
@@ -3200,8 +3230,10 @@ static void mapblock_free(mapblock_T **mpp)
mp = *mpp;
xfree(mp->m_keys);
- xfree(mp->m_str);
- xfree(mp->m_orig_str);
+ NLUA_CLEAR_REF(mp->m_luaref);
+ XFREE_CLEAR(mp->m_str);
+ XFREE_CLEAR(mp->m_orig_str);
+ XFREE_CLEAR(mp->m_desc);
*mpp = mp->m_next;
xfree(mp);
}
@@ -3392,7 +3424,8 @@ static void showmap(mapblock_T *mp, bool local)
{
size_t len = 1;
- if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) {
+ if (message_filtered(mp->m_keys)
+ && mp->m_str != NULL && message_filtered(mp->m_str)) {
return;
}
@@ -3437,7 +3470,11 @@ static void showmap(mapblock_T *mp, bool local)
/* Use FALSE below if we only want things like <Up> to show up as such on
* the rhs, and not M-x etc, TRUE gets both -- webb */
- if (*mp->m_str == NUL) {
+ if (mp->m_luaref != LUA_NOREF) {
+ char msg[100];
+ snprintf(msg, sizeof(msg), "<Lua function %d>", mp->m_luaref);
+ msg_puts_attr(msg, HL_ATTR(HLF_8));
+ } else if (mp->m_str == NULL) {
msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
} else {
// Remove escaping of CSI, because "m_str" is in a format to be used
@@ -3447,6 +3484,11 @@ static void showmap(mapblock_T *mp, bool local)
msg_outtrans_special(s, false, 0);
xfree(s);
}
+
+ if (mp->m_desc != NULL) {
+ msg_puts("\n "); // Shift line to same level as rhs.
+ msg_puts(mp->m_desc);
+ }
if (p_verbose > 0) {
last_set_msg(mp->m_script_ctx);
}
@@ -3533,7 +3575,7 @@ int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
}
for (; mp; mp = mp->m_next) {
if ((mp->m_mode & mode)
- && strstr((char *)mp->m_str, rhs) != NULL) {
+ && mp->m_str != NULL && strstr((char *)mp->m_str, rhs) != NULL) {
return true;
}
}
@@ -3879,7 +3921,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
(void)ins_typebuf(tb, 1, 0, true, mp->m_silent);
}
if (mp->m_expr) {
- s = eval_map_expr(mp->m_str, c);
+ s = eval_map_expr(mp, c);
} else {
s = mp->m_str;
}
@@ -3909,11 +3951,11 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
/// special characters.
///
/// @param c NUL or typed character for abbreviation
-static char_u *eval_map_expr(char_u *str, int c)
+static char_u *eval_map_expr(mapblock_T *mp, int c)
{
char_u *res;
- char_u *p;
- char_u *expr;
+ char_u *p = NULL;
+ char_u *expr = NULL;
char_u *save_cmd;
pos_T save_cursor;
int save_msg_col;
@@ -3921,8 +3963,10 @@ static char_u *eval_map_expr(char_u *str, int c)
/* Remove escaping of CSI, because "str" is in a format to be used as
* typeahead. */
- expr = vim_strsave(str);
- vim_unescape_csi(expr);
+ if (mp->m_luaref == LUA_NOREF) {
+ expr = vim_strsave(mp->m_str);
+ vim_unescape_csi(expr);
+ }
save_cmd = save_cmdline_alloc();
@@ -3934,7 +3978,22 @@ static char_u *eval_map_expr(char_u *str, int c)
save_cursor = curwin->w_cursor;
save_msg_col = msg_col;
save_msg_row = msg_row;
- p = eval_to_string(expr, NULL, false);
+ if (mp->m_luaref != LUA_NOREF) {
+ Error err = ERROR_INIT;
+ Array args = ARRAY_DICT_INIT;
+ Object ret = nlua_call_ref(mp->m_luaref, NULL, args, true, &err);
+ if (ret.type == kObjectTypeString) {
+ p = (char_u *)xstrndup(ret.data.string.data, ret.data.string.size);
+ }
+ api_free_object(ret);
+ if (err.type != kErrorTypeNone) {
+ semsg_multiline("E5108: %s", err.msg);
+ api_clear_error(&err);
+ }
+ } else {
+ p = eval_to_string(expr, NULL, false);
+ xfree(expr);
+ }
textlock--;
ex_normal_lock--;
curwin->w_cursor = save_cursor;
@@ -3942,7 +4001,6 @@ static char_u *eval_map_expr(char_u *str, int c)
msg_row = save_msg_row;
restore_cmdline_alloc(save_cmd);
- xfree(expr);
if (p == NULL) {
return NULL;
@@ -4049,8 +4107,11 @@ int makemap(FILE *fd, buf_T *buf)
continue;
}
- // skip mappings that contain a <SNR> (script-local thing),
+ // skip lua mappings and mappings that contain a <SNR> (script-local thing),
// they probably don't work when loaded again
+ if (mp->m_luaref != LUA_NOREF) {
+ continue;
+ }
for (p = mp->m_str; *p != NUL; p++) {
if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
&& p[2] == (int)KE_SNR) {
@@ -4331,10 +4392,11 @@ int put_escstr(FILE *fd, char_u *strstart, int what)
/// @param mp_ptr return: pointer to mapblock or NULL
/// @param local_ptr return: buffer-local mapping or NULL
char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr,
- int *local_ptr)
+ int *local_ptr, int *rhs_lua)
{
int len, minlen;
mapblock_T *mp;
+ *rhs_lua = LUA_NOREF;
validate_maphash();
@@ -4375,7 +4437,8 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb
if (local_ptr != NULL) {
*local_ptr = local;
}
- return mp->m_str;
+ *rhs_lua = mp->m_luaref;
+ return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL;
}
}
}
@@ -4560,3 +4623,47 @@ char_u *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat)
return (char_u *)line_ga.ga_data;
}
+
+bool map_execute_lua(void)
+{
+ garray_T line_ga;
+ int c1 = -1;
+ bool aborted = false;
+
+ ga_init(&line_ga, 1, 32);
+
+ no_mapping++;
+
+ got_int = false;
+ while (c1 != NUL && !aborted) {
+ ga_grow(&line_ga, 32);
+ // Get one character at a time.
+ c1 = vgetorpeek(true);
+ if (got_int) {
+ aborted = true;
+ } else if (c1 == '\r' || c1 == '\n') {
+ c1 = NUL; // end the line
+ } else {
+ ga_append(&line_ga, (char)c1);
+ }
+ }
+
+ no_mapping--;
+
+ if (aborted) {
+ ga_clear(&line_ga);
+ return false;
+ }
+
+ LuaRef ref = (LuaRef)atoi(line_ga.ga_data);
+ Error err = ERROR_INIT;
+ Array args = ARRAY_DICT_INIT;
+ nlua_call_ref(ref, NULL, args, false, &err);
+ if (err.type != kErrorTypeNone) {
+ semsg_multiline("E5108: %s", err.msg);
+ api_clear_error(&err);
+ }
+
+ ga_clear(&line_ga);
+ return true;
+}
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 5950611d3f..be10e150e5 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -50,14 +50,16 @@ struct map_arguments {
char_u *rhs; /// The {rhs} of the mapping.
size_t rhs_len;
+ LuaRef rhs_lua; /// lua function as rhs
bool rhs_is_noop; /// True when the {orig_rhs} is <nop>.
char_u *orig_rhs; /// The original text of the {rhs}.
size_t orig_rhs_len;
+ char *desc; /// map escription
};
typedef struct map_arguments MapArguments;
#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
- { 0 }, 0, NULL, 0, false, NULL, 0 }
+ { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
#define KEYLEN_PART_KEY -1 // keylen value for incomplete key-code
#define KEYLEN_PART_MAP -2 // keylen value for incomplete mapping
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index 5ff5a38614..5a74d1dc00 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -245,6 +245,7 @@ enum key_extra {
KE_MOUSEMOVE = 100, // mouse moved with no button down
// , KE_CANCEL = 101 // return from vgetc
KE_EVENT = 102, // event
+ KE_LUA = 103, // lua special key
KE_COMMAND = 104, // <Cmd> special key
};
@@ -443,6 +444,7 @@ enum key_extra {
#define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT)
#define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND)
+#define K_LUA TERMCAP2KEY(KS_EXTRA, KE_LUA)
// Bits for modifier mask
// 0x01 cannot be used, because the modifier must be 0x02 or higher
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
index b84124bc19..3a63f61200 100644
--- a/src/nvim/lua/spell.c
+++ b/src/nvim/lua/spell.c
@@ -1,3 +1,5 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <lua.h>
#include <lauxlib.h>
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 3246596f16..60bf393085 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -334,6 +334,7 @@ static const struct nv_cmd {
{ K_SELECT, nv_select, 0, 0 },
{ K_EVENT, nv_event, NV_KEEPREG, 0 },
{ K_COMMAND, nv_colon, 0, 0 },
+ { K_LUA, nv_colon, 0, 0 },
};
// Number of commands in nv_cmds[].
@@ -4043,21 +4044,22 @@ static void nv_regreplay(cmdarg_T *cap)
}
}
-/// Handle a ":" command and <Cmd>.
+/// Handle a ":" command and <Cmd> or Lua keymaps.
static void nv_colon(cmdarg_T *cap)
{
int old_p_im;
bool cmd_result;
bool is_cmdkey = cap->cmdchar == K_COMMAND;
+ bool is_lua = cap->cmdchar == K_LUA;
- if (VIsual_active && !is_cmdkey) {
+ if (VIsual_active && !is_cmdkey && !is_lua) {
nv_operator(cap);
} else {
if (cap->oap->op_type != OP_NOP) {
// Using ":" as a movement is charwise exclusive.
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
- } else if (cap->count0 && !is_cmdkey) {
+ } else if (cap->count0 && !is_cmdkey && !is_lua) {
// translate "count:" into ":.,.+(count - 1)"
stuffcharReadbuff('.');
if (cap->count0 > 1) {
@@ -4073,9 +4075,13 @@ static void nv_colon(cmdarg_T *cap)
old_p_im = p_im;
+ if (is_lua) {
+ cmd_result = map_execute_lua();
+ } else {
// get a command line and execute it
- cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
- cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
+ cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
+ cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
+ }
// If 'insertmode' changed, enter or exit Insert mode
if (p_im != old_p_im) {
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index a7b52d8238..70a5c7aa08 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -528,6 +528,10 @@ static int terminal_execute(VimState *state, int key)
do_cmdline(NULL, getcmdkeycmd, NULL, 0);
break;
+ case K_LUA:
+ map_execute_lua();
+ break;
+
case Ctrl_N:
if (s->got_bsl) {
return 0;
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 1ffa1f86dc..694e6faf21 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -286,7 +286,7 @@ let s:filename_checks = {
\ 'lilo': ['lilo.conf', 'lilo.conf-file'],
\ 'limits': ['/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf', '/etc/limits.conf', '/etc/limits.d/file.conf', '/etc/some-limits.conf', '/etc/some-limits.d/file.conf', 'any/etc/limits', 'any/etc/limits.conf', 'any/etc/limits.d/file.conf', 'any/etc/some-limits.conf', 'any/etc/some-limits.d/file.conf'],
\ 'liquid': ['file.liquid'],
- \ 'lisp': ['file.lsp', 'file.lisp', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc'],
+ \ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc'],
\ 'lite': ['file.lite', 'file.lt'],
\ 'litestep': ['/LiteStep/any/file.rc', 'any/LiteStep/any/file.rc'],
\ 'loginaccess': ['/etc/login.access', 'any/etc/login.access'],
@@ -436,7 +436,7 @@ let s:filename_checks = {
\ 'sather': ['file.sa'],
\ 'sbt': ['file.sbt'],
\ 'scala': ['file.scala', 'file.sc'],
- \ 'scheme': ['file.scm', 'file.ss', 'file.rkt', 'file.rktd', 'file.rktl'],
+ \ 'scheme': ['file.scm', 'file.ss', 'file.sld', 'file.rkt', 'file.rktd', 'file.rktl'],
\ 'scilab': ['file.sci', 'file.sce'],
\ 'screen': ['.screenrc', 'screenrc'],
\ 'sexplib': ['file.sexp'],
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index dd8eef7ca0..bc2759ade7 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -5,6 +5,7 @@ local clear = helpers.clear
local command = helpers.command
local curbufmeths = helpers.curbufmeths
local eq, neq = helpers.eq, helpers.neq
+local exec_lua = helpers.exec_lua
local feed = helpers.feed
local funcs = helpers.funcs
local meths = helpers.meths
@@ -316,6 +317,55 @@ describe('nvim_get_keymap', function()
command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>')
eq({space_table}, meths.get_keymap('n'))
end)
+
+ it('can handle lua keymaps', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+ eq(1, exec_lua[[return GlobalCount]])
+
+ eq(2, exec_lua[[
+ vim.api.nvim_get_keymap('n')[1].callback()
+ return GlobalCount
+ ]])
+ local mapargs = meths.get_keymap('n')
+ assert.Truthy(type(mapargs[1].callback) == 'number', 'callback is not luaref number')
+ mapargs[1].callback = nil
+ eq({
+ lhs='asdf',
+ script=0,
+ silent=0,
+ expr=0,
+ sid=0,
+ buffer=0,
+ nowait=0,
+ mode='n',
+ noremap=0,
+ lnum=0,
+ }, mapargs[1])
+ end)
+
+ it ('can handle map descriptions', function()
+ meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
+ eq({
+ lhs='lhs',
+ rhs='rhs',
+ script=0,
+ silent=0,
+ expr=0,
+ sid=0,
+ buffer=0,
+ nowait=0,
+ mode='n',
+ noremap=0,
+ lnum=0,
+ desc='map description'
+ }, meths.get_keymap('n')[1])
+ end)
end)
describe('nvim_set_keymap, nvim_del_keymap', function()
@@ -353,6 +403,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
to_return.sid = not opts.sid and 0 or opts.sid
to_return.buffer = not opts.buffer and 0 or opts.buffer
to_return.lnum = not opts.lnum and 0 or opts.lnum
+ to_return.desc = opts.desc
return to_return
end
@@ -717,6 +768,105 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
end)
end
end
+
+ it('can make lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ end)
+
+ it (':map command shows lua keymap correctly', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
+ ]]
+ assert.truthy(string.match(exec_lua[[return vim.api.nvim_exec(':nmap asdf', true)]],
+ "^\nn asdf <Lua function %d+>"))
+ end)
+
+ it ('mapcheck() returns lua keymap correctly', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
+ ]]
+ assert.truthy(string.match(funcs.mapcheck('asdf', 'n'),
+ "^<Lua function %d+>"))
+ end)
+
+ it ('maparg() returns lua keymap correctly', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
+ ]]
+ assert.truthy(string.match(funcs.maparg('asdf', 'n'),
+ "^<Lua function %d+>"))
+ local mapargs = funcs.maparg('asdf', 'n', false, true)
+ assert.Truthy(type(mapargs.callback) == 'number', 'callback is not luaref number')
+ mapargs.callback = nil
+ eq(generate_mapargs('n', 'asdf', nil, {}), mapargs)
+ end)
+
+ it('can make lua expr mappings', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true })
+ ]]
+
+ feed('aa')
+
+ eq(99, exec_lua[[return SomeValue]])
+ end)
+
+ it('can overwrite lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
+ ]]
+
+ feed('asdf\n')
+
+ eq(0, exec_lua[[return GlobalCount]])
+ end)
+
+ it('can unmap lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_del_keymap('n', 'asdf' )
+ ]]
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
+ end)
+
+ it('can set descriptions on keymaps', function()
+ meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
+ eq(generate_mapargs('n', 'lhs', 'rhs', {desc="map description"}), get_mapargs('n', 'lhs'))
+ eq("\nn lhs rhs\n map description",
+ helpers.exec_capture("nmap lhs"))
+ end)
end)
describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
@@ -814,4 +964,67 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
pcall_err(bufmeths.set_keymap, 100, '', 'lsh', 'irhs<Esc>', {})
helpers.assert_alive()
end)
+
+ it('can make lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ end)
+
+ it('can make lua expr mappings', function()
+ exec_lua [[
+ vim.api.nvim_buf_set_keymap (0, 'n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true })
+ ]]
+
+ feed('aa')
+
+ eq(99, exec_lua[[return SomeValue ]])
+ end)
+
+ it('can overwrite lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
+ ]]
+
+ feed('asdf\n')
+
+ eq(0, exec_lua[[return GlobalCount]])
+ end)
+
+ it('can unmap lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_buf_del_keymap(0, 'n', 'asdf' )
+ ]]
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
+ end)
end)
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index a88da63e90..7d260f2e29 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -1119,6 +1119,11 @@ describe('vim.diagnostic', function()
end)
describe('set()', function()
+ it('validates its arguments', function()
+ matches("expected a list of diagnostics",
+ pcall_err(exec_lua, [[vim.diagnostic.set(1, 0, {lnum = 1, col = 2})]]))
+ end)
+
it('can perform updates after insert_leave', function()
exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]]
nvim("input", "o")
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 317f92fcdc..9bf376b6fe 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -906,6 +906,7 @@ describe('lua stdlib', function()
exec_lua("vim.validate{arg1={nil, 'thread', true }}")
exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}")
exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}")
+ exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}")
matches('expected table, got number',
pcall_err(exec_lua, "vim.validate{ 1, 'x' }"))
@@ -935,6 +936,8 @@ describe('lua stdlib', function()
pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}"))
matches('arg1: expected %?, got 3',
pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}"))
+ matches('arg1: expected number|string, got nil',
+ pcall_err(exec_lua, "vim.validate{ arg1={ nil, {'n', 's'} }}"))
-- Pass an additional message back.
matches('arg1: expected %?, got 3. Info: TEST_MSG',
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index f25cfa2039..1ff451434d 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -350,7 +350,7 @@ describe('on_lines does not emit out-of-bounds line indexes when', function()
end)
it('creating a terminal buffer #16394', function()
- feed_command([[autocmd TermOpen * ++once call v:lua.register_callback(expand("<abuf>"))]])
+ feed_command('autocmd TermOpen * ++once call v:lua.register_callback(str2nr(expand("<abuf>")))')
feed_command('terminal')
sleep(500)
eq('', exec_lua([[return _G.cb_error]]))
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index bf57b135cb..faf44fa01d 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -20,7 +20,6 @@ local nvim_prog = helpers.nvim_prog
local nvim_set = helpers.nvim_set
local ok = helpers.ok
local read_file = helpers.read_file
-local exec_lua = helpers.exec_lua
if helpers.pending_win32(pending) then return end
@@ -581,34 +580,21 @@ describe('TUI', function()
end)
it("paste: 'nomodifiable' buffer", function()
- local has_luajit = exec_lua('return jit ~= nil')
child_session:request('nvim_command', 'set nomodifiable')
child_session:request('nvim_exec_lua', [[
- -- Stack traces for this test are non-deterministic, so disable them
- _G.debug.traceback = function(msg) return msg end
+ -- Truncate the error message to hide the line number
+ _G.debug.traceback = function(msg) return msg:sub(-49) end
]], {})
feed_data('\027[200~fail 1\nfail 2\n\027[201~')
- if has_luajit then
- screen:expect{grid=[[
- |
- {4:~ }|
- {5: }|
- {8:paste: Error executing lua: vim.lua:0: Vim:E21: Ca}|
- {8:nnot make changes, 'modifiable' is off} |
- {10:Press ENTER or type command to continue}{1: } |
- {3:-- TERMINAL --} |
- ]]}
- else
- screen:expect{grid=[[
- |
- {4:~ }|
- {5: }|
- {8:paste: Error executing lua: Vim:E21: Cannot make c}|
- {8:hanges, 'modifiable' is off} |
- {10:Press ENTER or type command to continue}{1: } |
- {3:-- TERMINAL --} |
- ]]}
- end
+ screen:expect{grid=[[
+ |
+ {4:~ }|
+ {5: }|
+ {8:paste: Error executing lua: Vim:E21: Cannot make c}|
+ {8:hanges, 'modifiable' is off} |
+ {10:Press ENTER or type command to continue}{1: } |
+ {3:-- TERMINAL --} |
+ ]]}
feed_data('\n') -- <Enter>
child_session:request('nvim_command', 'set modifiable')
feed_data('\027[200~success 1\nsuccess 2\n\027[201~')
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index b0b91df791..613b39c833 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -203,8 +203,8 @@ set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc891
set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.1.tar.gz)
set(TREESITTER_C_SHA256 ffcc2ef0eded59ad1acec9aec4f9b0c7dd209fc1a85d85f8b0e81298e3dddcc2)
-set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/25f64e1eb66bb1ab3eccd4f0b7da543005f3ba79.tar.gz)
-set(TREESITTER_SHA256 4f43bad474df494d00a779157f1c437638987d0e7896f9c73492cfeef35366b5)
+set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.2.tar.gz)
+set(TREESITTER_SHA256 2a0445f8172bbf83db005aedb4e893d394e2b7b33251badd3c94c2c5cc37c403)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)