Merge branch 'master' into luaviml'/lua

This commit is contained in:
ZyX 2017-04-10 22:21:06 +03:00
commit ab4d13e2fa
235 changed files with 5372 additions and 4678 deletions

View File

@ -25,7 +25,7 @@ env:
- CMAKE_FLAGS="-DTRAVIS_CI_BUILD=ON - CMAKE_FLAGS="-DTRAVIS_CI_BUILD=ON
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_BUILD_TYPE=Debug
-DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX
-DBUSTED_OUTPUT_TYPE=gtest -DBUSTED_OUTPUT_TYPE=nvim
-DDEPS_PREFIX=$DEPS_BUILD_DIR/usr -DDEPS_PREFIX=$DEPS_BUILD_DIR/usr
-DMIN_LOG_LEVEL=2" -DMIN_LOG_LEVEL=2"
- DEPS_CMAKE_FLAGS="-DDEPS_DOWNLOAD_DIR:PATH=$DEPS_DOWNLOAD_DIR" - DEPS_CMAKE_FLAGS="-DDEPS_DOWNLOAD_DIR:PATH=$DEPS_DOWNLOAD_DIR"
@ -43,11 +43,6 @@ env:
# If this file exists, we know that the cache contains compiled # If this file exists, we know that the cache contains compiled
# dependencies and we can use it. # dependencies and we can use it.
- CACHE_MARKER="$HOME/.cache/nvim-deps/.travis_cache_marker" - CACHE_MARKER="$HOME/.cache/nvim-deps/.travis_cache_marker"
# Test success marker. If this file exists, we know that all tests
# were successful. Required because we only want to update the cache
# if the tests were successful, but don't have this information
# available in before_cache (which is run before after_success).
- SUCCESS_MARKER="$BUILD_DIR/.tests_successful"
# default target name for functional tests # default target name for functional tests
- FUNCTIONALTEST=functionaltest - FUNCTIONALTEST=functionaltest
- CI_TARGET=tests - CI_TARGET=tests

View File

@ -275,7 +275,6 @@ else()
endif() endif()
add_definitions(-DINCLUDE_GENERATED_DECLARATIONS) add_definitions(-DINCLUDE_GENERATED_DECLARATIONS)
add_definitions(-DHAVE_CONFIG_H)
if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined")

View File

@ -7,7 +7,7 @@ build_script:
- call ci\build.bat - call ci\build.bat
cache: cache:
- C:\msys64\var\cache\pacman\pkg -> ci\build.bat - C:\msys64\var\cache\pacman\pkg -> ci\build.bat
- .deps -> third-party/CMakeLists.txt - .deps -> third-party/**
artifacts: artifacts:
- path: build/Neovim.zip - path: build/Neovim.zip
- path: build/bin/nvim.exe - path: build/bin/nvim.exe

View File

@ -0,0 +1,305 @@
local s = require 'say'
local pretty = require 'pl.pretty'
local term = require 'term'
local colors
local isWindows = package.config:sub(1,1) == '\\'
if isWindows then
colors = setmetatable({}, {__index = function() return function(s) return s end end})
else
colors = require 'term.colors'
end
return function(options)
local busted = require 'busted'
local handler = require 'busted.outputHandlers.base'()
local c = {
succ = function(s) return colors.bright(colors.green(s)) end,
skip = function(s) return colors.bright(colors.yellow(s)) end,
fail = function(s) return colors.bright(colors.magenta(s)) end,
errr = function(s) return colors.bright(colors.red(s)) end,
test = tostring,
file = colors.cyan,
time = colors.dim,
note = colors.yellow,
sect = function(s) return colors.green(colors.dim(s)) end,
nmbr = colors.bright,
}
local repeatSuiteString = '\nRepeating all tests (run %d of %d) . . .\n\n'
local randomizeString = c.note('Note: Randomizing test order with a seed of %d.\n')
local globalSetup = c.sect('[----------]') .. ' Global test environment setup.\n'
local fileStartString = c.sect('[----------]') .. ' Running tests from ' .. c.file('%s') .. '\n'
local runString = c.sect('[ RUN ]') .. ' ' .. c.test('%s') .. ': '
local successString = c.succ('OK') .. '\n'
local skippedString = c.skip('SKIP') .. '\n'
local failureString = c.fail('FAIL') .. '\n'
local errorString = c.errr('ERR') .. '\n'
local fileEndString = c.sect('[----------]') .. ' '.. c.nmbr('%d') .. ' %s from ' .. c.file('%s') .. ' ' .. c.time('(%.2f ms total)') .. '\n\n'
local globalTeardown = c.sect('[----------]') .. ' Global test environment teardown.\n'
local suiteEndString = c.sect('[==========]') .. ' ' .. c.nmbr('%d') .. ' %s from ' .. c.nmbr('%d') .. ' test %s ran. ' .. c.time('(%.2f ms total)') .. '\n'
local successStatus = c.succ('[ PASSED ]') .. ' ' .. c.nmbr('%d') .. ' %s.\n'
local timeString = c.time('%.2f ms')
local summaryStrings = {
skipped = {
header = c.skip('[ SKIPPED ]') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
test = c.skip('[ SKIPPED ]') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' SKIPPED %s\n',
},
failure = {
header = c.fail('[ FAILED ]') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
test = c.fail('[ FAILED ]') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' FAILED %s\n',
},
error = {
header = c.errr('[ ERROR ]') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
test = c.errr('[ ERROR ]') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' %s\n',
},
}
c = nil
local fileCount = 0
local fileTestCount = 0
local testCount = 0
local successCount = 0
local skippedCount = 0
local failureCount = 0
local errorCount = 0
local pendingDescription = function(pending)
local name = pending.name
local string = ''
if type(pending.message) == 'string' then
string = string .. pending.message .. '\n'
elseif pending.message ~= nil then
string = string .. pretty.write(pending.message) .. '\n'
end
return string
end
local failureDescription = function(failure)
local string = failure.randomseed and ('Random seed: ' .. failure.randomseed .. '\n') or ''
if type(failure.message) == 'string' then
string = string .. failure.message
elseif failure.message == nil then
string = string .. 'Nil error'
else
string = string .. pretty.write(failure.message)
end
string = string .. '\n'
if options.verbose and failure.trace and failure.trace.traceback then
string = string .. failure.trace.traceback .. '\n'
end
return string
end
local getFileLine = function(element)
local fileline = ''
if element.trace or element.trace.short_src then
fileline = colors.cyan(element.trace.short_src) .. ' @ ' ..
colors.cyan(element.trace.currentline) .. ': '
end
return fileline
end
local getTestList = function(status, count, list, getDescription)
local string = ''
local header = summaryStrings[status].header
if count > 0 and header then
local tests = (count == 1 and 'test' or 'tests')
local errors = (count == 1 and 'error' or 'errors')
string = header:format(count, status == 'error' and errors or tests)
local testString = summaryStrings[status].test
if testString then
for _, t in ipairs(list) do
local fullname = getFileLine(t.element) .. colors.bright(t.name)
string = string .. testString:format(fullname)
string = string .. getDescription(t)
end
end
end
return string
end
local getSummary = function(status, count)
local string = ''
local footer = summaryStrings[status].footer
if count > 0 and footer then
local tests = (count == 1 and 'TEST' or 'TESTS')
local errors = (count == 1 and 'ERROR' or 'ERRORS')
string = footer:format(count, status == 'error' and errors or tests)
end
return string
end
local getSummaryString = function()
local tests = (successCount == 1 and 'test' or 'tests')
local string = successStatus:format(successCount, tests)
string = string .. getTestList('skipped', skippedCount, handler.pendings, pendingDescription)
string = string .. getTestList('failure', failureCount, handler.failures, failureDescription)
string = string .. getTestList('error', errorCount, handler.errors, failureDescription)
string = string .. ((skippedCount + failureCount + errorCount) > 0 and '\n' or '')
string = string .. getSummary('skipped', skippedCount)
string = string .. getSummary('failure', failureCount)
string = string .. getSummary('error', errorCount)
return string
end
handler.suiteReset = function()
fileCount = 0
fileTestCount = 0
testCount = 0
successCount = 0
skippedCount = 0
failureCount = 0
errorCount = 0
return nil, true
end
handler.suiteStart = function(suite, count, total, randomseed)
if total > 1 then
io.write(repeatSuiteString:format(count, total))
end
if randomseed then
io.write(randomizeString:format(randomseed))
end
io.write(globalSetup)
io.flush()
return nil, true
end
local function getElapsedTime(tbl)
if tbl.duration then
return tbl.duration * 1000
else
return tonumber('nan')
end
end
handler.suiteEnd = function(suite, count, total)
local elapsedTime_ms = getElapsedTime(suite)
local tests = (testCount == 1 and 'test' or 'tests')
local files = (fileCount == 1 and 'file' or 'files')
io.write(globalTeardown)
io.write(suiteEndString:format(testCount, tests, fileCount, files, elapsedTime_ms))
io.write(getSummaryString())
io.flush()
return nil, true
end
handler.fileStart = function(file)
fileTestCount = 0
io.write(fileStartString:format(file.name))
io.flush()
return nil, true
end
handler.fileEnd = function(file)
local elapsedTime_ms = getElapsedTime(file)
local tests = (fileTestCount == 1 and 'test' or 'tests')
fileCount = fileCount + 1
io.write(fileEndString:format(fileTestCount, tests, file.name, elapsedTime_ms))
io.flush()
return nil, true
end
handler.testStart = function(element, parent)
io.write(runString:format(handler.getFullName(element)))
io.flush()
return nil, true
end
handler.testEnd = function(element, parent, status, debug)
local elapsedTime_ms = getElapsedTime(element)
local string
fileTestCount = fileTestCount + 1
testCount = testCount + 1
if status == 'success' then
successCount = successCount + 1
string = successString
elseif status == 'pending' then
skippedCount = skippedCount + 1
string = skippedString
elseif status == 'failure' then
failureCount = failureCount + 1
string = nil
elseif status == 'error' then
errorCount = errorCount + 1
string = nil
end
if string ~= nil then
if elapsedTime_ms == elapsedTime_ms then
string = timeString:format(elapsedTime_ms) .. ' ' .. string
end
io.write(string)
io.flush()
end
return nil, true
end
handler.testFailure = function(element, parent, message, debug)
io.write(failureString)
io.flush()
io.write(failureDescription(handler.failures[#handler.failures]))
io.flush()
return nil, true
end
handler.testError = function(element, parent, message, debug)
io.write(errorString)
io.flush()
io.write(failureDescription(handler.errors[#handler.errors]))
io.flush()
return nil, true
end
handler.error = function(element, parent, message, debug)
if element.descriptor ~= 'it' then
io.write(failureDescription(handler.errors[#handler.errors]))
io.flush()
errorCount = errorCount + 1
end
return nil, true
end
busted.subscribe({ 'suite', 'reset' }, handler.suiteReset)
busted.subscribe({ 'suite', 'start' }, handler.suiteStart)
busted.subscribe({ 'suite', 'end' }, handler.suiteEnd)
busted.subscribe({ 'file', 'start' }, handler.fileStart)
busted.subscribe({ 'file', 'end' }, handler.fileEnd)
busted.subscribe({ 'test', 'start' }, handler.testStart, { predicate = handler.cancelOnPending })
busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending })
busted.subscribe({ 'failure', 'it' }, handler.testFailure)
busted.subscribe({ 'error', 'it' }, handler.testError)
busted.subscribe({ 'failure' }, handler.error)
busted.subscribe({ 'error' }, handler.error)
return handler
end

View File

@ -3,12 +3,15 @@
set -e set -e
set -o pipefail set -o pipefail
CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${CI_DIR}/common/suite.sh"
# Don't cache pip's log and selfcheck. # Don't cache pip's log and selfcheck.
rm -rf "${HOME}/.cache/pip/log" rm -rf "${HOME}/.cache/pip/log"
rm -f "${HOME}/.cache/pip/selfcheck.json" rm -f "${HOME}/.cache/pip/selfcheck.json"
# Update the third-party dependency cache only if the build was successful. # Update the third-party dependency cache only if the build was successful.
if [[ -f "${SUCCESS_MARKER}" ]]; then if ended_successfully; then
rm -rf "${HOME}/.cache/nvim-deps" rm -rf "${HOME}/.cache/nvim-deps"
mv "${DEPS_BUILD_DIR}" "${HOME}/.cache/nvim-deps" mv "${DEPS_BUILD_DIR}" "${HOME}/.cache/nvim-deps"
touch "${CACHE_MARKER}" touch "${CACHE_MARKER}"

View File

@ -38,7 +38,7 @@ cd ..
:: Build Neovim :: Build Neovim
mkdir build mkdir build
cd build cd build
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DBUSTED_OUTPUT_TYPE=gtest -DGPERF_PRG="C:\msys64\usr\bin\gperf.exe" .. || goto :error cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DBUSTED_OUTPUT_TYPE=nvim -DGPERF_PRG="C:\msys64\usr\bin\gperf.exe" .. || goto :error
mingw32-make VERBOSE=1 || goto :error mingw32-make VERBOSE=1 || goto :error
bin\nvim --version || goto :error bin\nvim --version || goto :error

View File

@ -2,11 +2,18 @@
NL="$(printf '\nE')" NL="$(printf '\nE')"
NL="${NL%E}" NL="${NL%E}"
FAILED=0
FAIL_SUMMARY="" FAIL_SUMMARY=""
# Test success marker. If END_MARKER file exists, we know that all tests
# finished. If FAIL_SUMMARY_FILE exists we know that some tests failed, this
# file will contain information about failed tests. Build is considered
# successful if tests ended without any of them failing.
END_MARKER="$BUILD_DIR/.tests_finished"
FAIL_SUMMARY_FILE="$BUILD_DIR/.test_errors"
enter_suite() { enter_suite() {
FAILED=0
rm -f "${END_MARKER}"
local suite_name="$1" local suite_name="$1"
export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE}/$suite_name" export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE}/$suite_name"
} }
@ -19,17 +26,16 @@ exit_suite() {
export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE%/*}" export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE%/*}"
if test "x$1" != "x--continue" ; then if test "x$1" != "x--continue" ; then
exit $FAILED exit $FAILED
else
local saved_failed=$FAILED
FAILED=0
return $saved_failed
fi fi
} }
fail() { fail() {
local allow_failure=
if test "x$1" = "x--allow-failure" ; then
shift
allow_failure=A
fi
local test_name="$1" local test_name="$1"
local fail_char="$allow_failure$2" local fail_char="$2"
local message="$3" local message="$3"
: ${fail_char:=F} : ${fail_char:=F}
@ -37,10 +43,9 @@ fail() {
local full_msg="$fail_char $NVIM_TEST_CURRENT_SUITE|$test_name :: $message" local full_msg="$fail_char $NVIM_TEST_CURRENT_SUITE|$test_name :: $message"
FAIL_SUMMARY="${FAIL_SUMMARY}${NL}${full_msg}" FAIL_SUMMARY="${FAIL_SUMMARY}${NL}${full_msg}"
echo "${full_msg}" >> "${FAIL_SUMMARY_FILE}"
echo "Failed: $full_msg" echo "Failed: $full_msg"
if test "x$allow_failure" = "x" ; then
FAILED=1 FAILED=1
fi
} }
run_test() { run_test() {
@ -55,6 +60,12 @@ run_test() {
} }
run_test_wd() { run_test_wd() {
local hang_ok=
if test "x$1" = "x--allow-hang" ; then
hang_ok=1
shift
fi
local timeout="$1" local timeout="$1"
test $# -gt 0 && shift test $# -gt 0 && shift
@ -77,14 +88,13 @@ run_test_wd() {
while test $restarts -gt 0 ; do while test $restarts -gt 0 ; do
: > "${status_file}" : > "${status_file}"
( (
FAILED=0
if ! (
set -o pipefail set -o pipefail
eval "$cmd" 2>&1 | tee -a "$output_file" ret=0
) ; then if ! eval "$cmd" 2>&1 | tee -a "$output_file" ; then
fail "${test_name}" "$@" ret=1
fi fi
echo "$FAILED" > "$status_file" echo "$ret" > "$status_file"
exit $ret
) & ) &
local pid=$! local pid=$!
while test "$(stat -c "%s" "$status_file")" -eq 0 ; do while test "$(stat -c "%s" "$status_file")" -eq 0 ; do
@ -101,7 +111,9 @@ run_test_wd() {
# status file not updated, assuming hang # status file not updated, assuming hang
kill -KILL $pid kill -KILL $pid
if test $restarts -eq 0 ; then if test $restarts -eq 0 ; then
if test "x$hang_ok" = "x" ; then
fail "${test_name}" E "Test hang up" fail "${test_name}" E "Test hang up"
fi
else else
echo "Test ${test_name} hang up, restarting" echo "Test ${test_name} hang up, restarting"
eval "$restart_cmd" eval "$restart_cmd"
@ -116,6 +128,20 @@ run_test_wd() {
done done
} }
succeeded() { ended_successfully() {
return $FAILED if [[ -f "${FAIL_SUMMARY_FILE}" ]]; then
echo 'Test failed, complete summary:'
cat "${FAIL_SUMMARY_FILE}"
return 1
fi
if ! [[ -f "${END_MARKER}" ]] ; then
echo 'ended_successfully called before end marker was touched'
return 1
fi
return 0
}
end_tests() {
touch "${END_MARKER}"
ended_successfully
} }

View File

@ -1,4 +1,5 @@
source "${CI_DIR}/common/build.sh" source "${CI_DIR}/common/build.sh"
source "${CI_DIR}/common/suite.sh"
print_core() { print_core() {
local app="$1" local app="$1"
@ -40,10 +41,9 @@ check_core_dumps() {
print_core "$app" "$core" print_core "$app" "$core"
fi fi
done done
if test "$app" = quiet ; then if test "$app" != quiet ; then
return 0 fail 'cores' E 'Core dumps found'
fi fi
exit 1
} }
check_logs() { check_logs() {
@ -62,8 +62,7 @@ check_logs() {
err=1 err=1
done done
if [[ -n "${err}" ]]; then if [[ -n "${err}" ]]; then
echo "Runtime errors detected." fail 'logs' E 'Runtime errors detected.'
exit 1
fi fi
} }
@ -75,50 +74,53 @@ asan_check() {
check_logs "${1}" "*san.*" check_logs "${1}" "*san.*"
} }
run_unittests() { run_unittests() {(
enter_suite unittests
ulimit -c unlimited ulimit -c unlimited
if ! build_make unittest ; then if ! build_make unittest ; then
check_core_dumps "$(which luajit)" fail 'unittests' F 'Unit tests failed'
exit 1
fi fi
check_core_dumps "$(which luajit)" check_core_dumps "$(which luajit)"
} exit_suite
)}
run_functionaltests() { run_functionaltests() {(
enter_suite functionaltests
ulimit -c unlimited ulimit -c unlimited
if ! build_make ${FUNCTIONALTEST}; then if ! build_make ${FUNCTIONALTEST}; then
asan_check "${LOG_DIR}" fail 'functionaltests' F 'Functional tests failed'
valgrind_check "${LOG_DIR}"
check_core_dumps
exit 1
fi fi
asan_check "${LOG_DIR}" asan_check "${LOG_DIR}"
valgrind_check "${LOG_DIR}" valgrind_check "${LOG_DIR}"
check_core_dumps check_core_dumps
} exit_suite
)}
run_oldtests() { run_oldtests() {(
enter_suite oldtests
ulimit -c unlimited ulimit -c unlimited
if ! make -C "${TRAVIS_BUILD_DIR}/src/nvim/testdir"; then if ! make -C "${TRAVIS_BUILD_DIR}/src/nvim/testdir"; then
reset reset
asan_check "${LOG_DIR}" fail 'oldtests' F 'Legacy tests failed'
valgrind_check "${LOG_DIR}"
check_core_dumps
exit 1
fi fi
asan_check "${LOG_DIR}" asan_check "${LOG_DIR}"
valgrind_check "${LOG_DIR}" valgrind_check "${LOG_DIR}"
check_core_dumps check_core_dumps
} exit_suite
)}
install_nvim() { install_nvim() {(
build_make install enter_suite 'install_nvim'
if ! build_make install ; then
fail 'install' E 'make install failed'
exit_suite
fi
"${INSTALL_PREFIX}/bin/nvim" --version "${INSTALL_PREFIX}/bin/nvim" --version
"${INSTALL_PREFIX}/bin/nvim" -u NONE -e -c ':help' -c ':qall' || { "${INSTALL_PREFIX}/bin/nvim" -u NONE -e -c ':help' -c ':qall' || {
echo "Running ':help' in the installed nvim failed." echo "Running ':help' in the installed nvim failed."
echo "Maybe the helptags have not been generated properly." echo "Maybe the helptags have not been generated properly."
exit 1 fail 'help' F 'Failed running :help'
} }
local genvimsynf=syntax/vim/generated.vim local genvimsynf=syntax/vim/generated.vim
@ -127,24 +129,22 @@ install_nvim() {
cd runtime ; git ls-files | grep -e '.vim$' -e '.ps$' -e '.dict$' -e '.py$' -e '.tutor$' cd runtime ; git ls-files | grep -e '.vim$' -e '.ps$' -e '.dict$' -e '.py$' -e '.tutor$'
) ; do ) ; do
if ! test -e "${INSTALL_PREFIX}/share/nvim/runtime/$file" ; then if ! test -e "${INSTALL_PREFIX}/share/nvim/runtime/$file" ; then
echo "It appears that $file is not installed." fail 'runtime-install' F "It appears that $file is not installed."
exit 1
fi fi
done done
# Check that generated syntax file has function names, #5060. # Check that generated syntax file has function names, #5060.
local gpat='syn keyword vimFuncName .*eval' local gpat='syn keyword vimFuncName .*eval'
if ! grep -q "$gpat" "${INSTALL_PREFIX}/share/nvim/runtime/$genvimsynf"; then if ! grep -q "$gpat" "${INSTALL_PREFIX}/share/nvim/runtime/$genvimsynf"; then
echo "It appears that $genvimsynf does not contain $gpat." fail 'funcnames' F "It appears that $genvimsynf does not contain $gpat."
exit 1
fi fi
for file in $( for file in $(
cd runtime ; git ls-files | grep -e '.awk$' -e '.sh$' -e '.bat$' cd runtime ; git ls-files | grep -e '.awk$' -e '.sh$' -e '.bat$'
) ; do ) ; do
if ! test -x "${INSTALL_PREFIX}/share/nvim/runtime/$file" ; then if ! test -x "${INSTALL_PREFIX}/share/nvim/runtime/$file" ; then
echo "It appears that $file is not installed or is not executable." fail 'not-exe' F "It appears that $file is not installed or is not executable."
exit 1
fi fi
done done
} exit_suite
)}

View File

@ -20,9 +20,10 @@ csi_clean() {
run_test 'top_make clint-full' clint run_test 'top_make clint-full' clint
run_test 'top_make testlint' testlint run_test 'top_make testlint' testlint
CLICOLOR_FORCE=1 run_test_wd \ CLICOLOR_FORCE=1 run_test_wd \
5s \ --allow-hang \
10s \
'top_make check-single-includes' \ 'top_make check-single-includes' \
'csi_clean' \ 'csi_clean' \
single-includes single-includes
exit_suite end_tests

View File

@ -27,8 +27,4 @@ run_test run_oldtests
run_test install_nvim run_test install_nvim
if succeeded ; then end_tests
touch "${SUCCESS_MARKER}"
fi
exit_suite

View File

@ -0,0 +1,2 @@
file(GLOB_RECURSE JSON_FILES *.json)
file(COPY ${JSON_FILES} DESTINATION "${TARGET}")

View File

@ -2292,7 +2292,7 @@ tabpagebuflist([{arg}]) List list of buffer numbers in tab page
tabpagenr([{arg}]) Number number of current or last tab page tabpagenr([{arg}]) Number number of current or last tab page
tabpagewinnr({tabarg}[, {arg}]) tabpagewinnr({tabarg}[, {arg}])
Number number of current window in tab page Number number of current window in tab page
taglist({expr}) List list of tags matching {expr} taglist({expr}[, {filename}]) List list of tags matching {expr}
tagfiles() List tags files used tagfiles() List tags files used
tan({expr}) Float tangent of {expr} tan({expr}) Float tangent of {expr}
tanh({expr}) Float hyperbolic tangent of {expr} tanh({expr}) Float hyperbolic tangent of {expr}
@ -4945,8 +4945,8 @@ json_decode({expr}) *json_decode()*
json_encode({expr}) *json_encode()* json_encode({expr}) *json_encode()*
Convert {expr} into a JSON string. Accepts Convert {expr} into a JSON string. Accepts
|msgpack-special-dict| as the input. Will not convert |Funcref|s, |msgpack-special-dict| as the input. Will not convert
mappings with non-string keys (can be created as |Funcref|s, mappings with non-string keys (can be created as
|msgpack-special-dict|), values with self-referencing |msgpack-special-dict|), values with self-referencing
containers, strings which contain non-UTF-8 characters, containers, strings which contain non-UTF-8 characters,
pseudo-UTF-8 strings which contain codepoints reserved for pseudo-UTF-8 strings which contain codepoints reserved for
@ -7431,8 +7431,13 @@ tagfiles() Returns a |List| with the file names used to search for tags
for the current buffer. This is the 'tags' option expanded. for the current buffer. This is the 'tags' option expanded.
taglist({expr}) *taglist()* taglist({expr}[, {filename}]) *taglist()*
Returns a list of tags matching the regular expression {expr}. Returns a list of tags matching the regular expression {expr}.
If {filename} is passed it is used to prioritize the results
in the same way that |:tselect| does. See |tag-priority|.
{filename} should be the full path of the file.
Each list item is a dictionary with at least the following Each list item is a dictionary with at least the following
entries: entries:
name Name of the tag. name Name of the tag.

View File

@ -5151,8 +5151,8 @@ A jump table for the options with a short description can be found at |Q_op|.
saved. When not included, the value of 'history' is used. saved. When not included, the value of 'history' is used.
*shada-c* *shada-c*
c Dummy option, kept for compatibility reasons. Has no actual c Dummy option, kept for compatibility reasons. Has no actual
effect. Current encoding state is described in effect: ShaDa always uses UTF-8 and 'encoding' value is fixed
|shada-encoding|. to UTF-8 as well.
*shada-f* *shada-f*
f Whether file marks need to be stored. If zero, file marks ('0 f Whether file marks need to be stored. If zero, file marks ('0
to '9, 'A to 'Z) are not stored. When not present or when to '9, 'A to 'Z) are not stored. When not present or when

View File

@ -1097,23 +1097,6 @@ SHADA FILE NAME *shada-file-name*
default and the name given with 'shada' or "-i" (unless it's NONE). default and the name given with 'shada' or "-i" (unless it's NONE).
CHARACTER ENCODING *shada-encoding*
The text in the ShaDa file is UTF-8-encoded. Normally you will always work
with the same 'encoding' value, and this works just fine. However, if you
read the ShaDa file with value for 'encoding' different from utf-8 and
'encoding' used when writing ShaDa file, some of the text (non-ASCII
characters) may be invalid as Neovim always attempts to convert the text in
the ShaDa file from the UTF-8 to the current 'encoding' value. Filenames are
never converted, affected elements are:
- history strings;
- variable values;
- register values;
- last used search and substitute patterns;
- last used substitute replacement string.
MANUALLY READING AND WRITING *shada-read-write* MANUALLY READING AND WRITING *shada-read-write*
Two commands can be used to read and write the ShaDa file manually. This Two commands can be used to read and write the ShaDa file manually. This
@ -1221,8 +1204,11 @@ exactly four MessagePack objects:
3. Third goes the length of the fourth entry. Unsigned integer as well, used 3. Third goes the length of the fourth entry. Unsigned integer as well, used
for fast skipping without parsing. for fast skipping without parsing.
4. Fourth is actual entry data. All currently used ShaDa entries use 4. Fourth is actual entry data. All currently used ShaDa entries use
containers to hold data: either map or array. Exact format depends on the containers to hold data: either map or array. All string values in those
entry type: containers are either binary (applies to filenames) or UTF-8, yet parser
needs to expect that invalid bytes may be present in a UTF-8 string.
Exact format depends on the entry type:
Entry type (name) Entry data ~ Entry type (name) Entry data ~
1 (Header) Map containing data that describes the generator 1 (Header) Map containing data that describes the generator

View File

@ -42,7 +42,10 @@ set(LINT_SUPPRESS_URL "${LINT_SUPPRESS_URL_BASE}/errors.json")
set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py) set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py)
set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake) set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake)
set(LINT_SUPPRESSES_ROOT ${PROJECT_BINARY_DIR}/errors) set(LINT_SUPPRESSES_ROOT ${PROJECT_BINARY_DIR}/errors)
set(LINT_SUPPRESSES_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.tar.gz") set(LINT_SUPPRESSES_URL "${LINT_SUPPRESS_URL_BASE}/errors.tar.gz")
set(LINT_SUPPRESSES_ARCHIVE ${LINT_SUPPRESSES_ROOT}/errors.tar.gz)
set(LINT_SUPPRESSES_TOUCH_FILE "${TOUCHES_DIR}/unpacked-clint-errors-archive")
set(LINT_SUPPRESSES_INSTALL_SCRIPT "${PROJECT_SOURCE_DIR}/cmake/InstallClintErrors.cmake")
file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt)
file(GLOB API_HEADERS api/*.h) file(GLOB API_HEADERS api/*.h)
@ -55,6 +58,8 @@ include_directories(${GENERATED_INCLUDES_DIR})
file(MAKE_DIRECTORY ${TOUCHES_DIR}) file(MAKE_DIRECTORY ${TOUCHES_DIR})
file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR})
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT})
file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src)
file(GLOB NVIM_SOURCES *.c) file(GLOB NVIM_SOURCES *.c)
file(GLOB NVIM_HEADERS *.h) file(GLOB NVIM_HEADERS *.h)
@ -495,26 +500,10 @@ function(get_test_target prefix sfile relative_path_var target_var)
endfunction() endfunction()
set(NO_SINGLE_CHECK_HEADERS set(NO_SINGLE_CHECK_HEADERS
cursor_shape.h
digraph.h
ex_cmds.h
ex_getln.h
file_search.h
fold.h
getchar.h getchar.h
hardcopy.h
if_cscope.h
if_cscope_defs.h if_cscope_defs.h
mark.h
mbyte.h
memfile_defs.h
memline.h
memline_defs.h
menu.h
misc2.h misc2.h
move.h
msgpack_rpc/server.h msgpack_rpc/server.h
ops.h
option.h option.h
os/shell.h os/shell.h
os_unix.h os_unix.h
@ -573,6 +562,21 @@ function(add_download output url allow_failure)
) )
endfunction() endfunction()
add_download(${LINT_SUPPRESSES_ARCHIVE} ${LINT_SUPPRESSES_URL} off)
add_custom_command(
OUTPUT ${LINT_SUPPRESSES_TOUCH_FILE}
WORKING_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src
COMMAND ${CMAKE_COMMAND} -E tar xfz ${LINT_SUPPRESSES_ARCHIVE}
COMMAND
${CMAKE_COMMAND}
-DTARGET=${LINT_SUPPRESSES_ROOT}
-P ${LINT_SUPPRESSES_INSTALL_SCRIPT}
COMMAND ${CMAKE_COMMAND} -E touch ${LINT_SUPPRESSES_TOUCH_FILE}
DEPENDS
${LINT_SUPPRESSES_ARCHIVE} ${LINT_SUPPRESSES_INSTALL_SCRIPT}
)
add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off) add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off)
set(LINT_NVIM_REL_SOURCES) set(LINT_NVIM_REL_SOURCES)
@ -581,14 +585,13 @@ foreach(sfile ${LINT_NVIM_SOURCES})
set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json)
set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json")
set(rsfile src/nvim/${r}) set(rsfile src/nvim/${r})
add_download(${suppress_file} ${suppress_url} on)
set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}")
add_custom_command( add_custom_command(
OUTPUT ${touch_file} OUTPUT ${touch_file}
COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile} COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
DEPENDS ${LINT_PRG} ${sfile} ${suppress_file} DEPENDS ${LINT_PRG} ${sfile} ${LINT_SUPPRESSES_TOUCH_FILE}
) )
list(APPEND LINT_TARGETS ${touch_file}) list(APPEND LINT_TARGETS ${touch_file})
list(APPEND LINT_NVIM_REL_SOURCES ${rsfile}) list(APPEND LINT_NVIM_REL_SOURCES ${rsfile})

View File

@ -212,8 +212,8 @@ int buf_init_chartab(buf_T *buf, int global)
// work properly when 'encoding' is "latin1" and the locale is // work properly when 'encoding' is "latin1" and the locale is
// "C". // "C".
if (!do_isalpha if (!do_isalpha
|| vim_islower(c) || mb_islower(c)
|| vim_isupper(c) || mb_isupper(c)
|| (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) { || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) {
if (i == 0) { if (i == 0) {
// (re)set ID flag // (re)set ID flag
@ -417,11 +417,11 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
while (STR_CHAR(i) != NUL) { while (STR_CHAR(i) != NUL) {
int c = utf_ptr2char(STR_PTR(i)); int c = utf_ptr2char(STR_PTR(i));
int olen = utf_ptr2len(STR_PTR(i)); int olen = utf_ptr2len(STR_PTR(i));
int lc = utf_tolower(c); int lc = mb_tolower(c);
// Only replace the character when it is not an invalid // Only replace the character when it is not an invalid
// sequence (ASCII character or more than one byte) and // sequence (ASCII character or more than one byte) and
// utf_tolower() doesn't return the original character. // mb_tolower() doesn't return the original character.
if (((c < 0x80) || (olen > 1)) && (c != lc)) { if (((c < 0x80) || (olen > 1)) && (c != lc)) {
int nlen = utf_char2len(lc); int nlen = utf_char2len(lc);
@ -1506,67 +1506,6 @@ char_u* skiptohex(char_u *q)
return p; return p;
} }
// Vim's own character class functions. These exist because many library
// islower()/toupper() etc. do not work properly: they crash when used with
// invalid values or can't handle latin1 when the locale is C.
// Speed is most important here.
/// Check that the character is lower-case
///
/// @param c character to check
bool vim_islower(int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (c <= '@') {
return false;
}
if (c >= 0x80) {
return utf_islower(c);
}
return islower(c);
}
/// Check that the character is upper-case
///
/// @param c character to check
bool vim_isupper(int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (c <= '@') {
return false;
}
if (c >= 0x80) {
return utf_isupper(c);
}
return isupper(c);
}
int vim_toupper(int c)
{
if (c <= '@') {
return c;
}
if (c >= 0x80) {
return utf_toupper(c);
}
return TOUPPER_LOC(c);
}
int vim_tolower(int c)
{
if (c <= '@') {
return c;
}
if (c >= 0x80) {
return utf_tolower(c);
}
return TOLOWER_LOC(c);
}
/// Skip over text until ' ' or '\t' or NUL /// Skip over text until ' ' or '\t' or NUL
/// ///
/// @param[in] p Text to skip over. /// @param[in] p Text to skip over.

View File

@ -1,6 +1,9 @@
#ifndef NVIM_CURSOR_SHAPE_H #ifndef NVIM_CURSOR_SHAPE_H
#define NVIM_CURSOR_SHAPE_H #define NVIM_CURSOR_SHAPE_H
#include "nvim/types.h"
#include "nvim/api/private/defs.h"
/// struct to store values from 'guicursor' and 'mouseshape' /// struct to store values from 'guicursor' and 'mouseshape'
/// Indexes in shape_table[] /// Indexes in shape_table[]
typedef enum { typedef enum {

View File

@ -1,6 +1,9 @@
#ifndef NVIM_DIGRAPH_H #ifndef NVIM_DIGRAPH_H
#define NVIM_DIGRAPH_H #define NVIM_DIGRAPH_H
#include "nvim/types.h"
#include "nvim/ex_cmds_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "digraph.h.generated.h" # include "digraph.h.generated.h"
#endif #endif

View File

@ -2037,12 +2037,12 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int
} else { } else {
c = *(p++); c = *(p++);
} }
if (vim_islower(c)) { if (mb_islower(c)) {
has_lower = true; has_lower = true;
if (vim_isupper(wca[i])) { if (mb_isupper(wca[i])) {
// Rule 1 is satisfied. // Rule 1 is satisfied.
for (i = actual_compl_length; i < actual_len; i++) { for (i = actual_compl_length; i < actual_len; i++) {
wca[i] = vim_tolower(wca[i]); wca[i] = mb_tolower(wca[i]);
} }
break; break;
} }
@ -2062,14 +2062,14 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int
} else { } else {
c = *(p++); c = *(p++);
} }
if (was_letter && vim_isupper(c) && vim_islower(wca[i])) { if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
// Rule 2 is satisfied. // Rule 2 is satisfied.
for (i = actual_compl_length; i < actual_len; i++) { for (i = actual_compl_length; i < actual_len; i++) {
wca[i] = vim_toupper(wca[i]); wca[i] = mb_toupper(wca[i]);
} }
break; break;
} }
was_letter = vim_islower(c) || vim_isupper(c); was_letter = mb_islower(c) || mb_isupper(c);
} }
} }
@ -2082,10 +2082,10 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int
} else { } else {
c = *(p++); c = *(p++);
} }
if (vim_islower(c)) { if (mb_islower(c)) {
wca[i] = vim_tolower(wca[i]); wca[i] = mb_tolower(wca[i]);
} else if (vim_isupper(c)) { } else if (mb_isupper(c)) {
wca[i] = vim_toupper(wca[i]); wca[i] = mb_toupper(wca[i]);
} }
} }
} }
@ -2302,9 +2302,10 @@ static void ins_compl_longest_match(compl_T *match)
c1 = *p; c1 = *p;
c2 = *s; c2 = *s;
} }
if (match->cp_icase ? (vim_tolower(c1) != vim_tolower(c2)) if (match->cp_icase ? (mb_tolower(c1) != mb_tolower(c2))
: (c1 != c2)) : (c1 != c2)) {
break; break;
}
if (has_mbyte) { if (has_mbyte) {
mb_ptr_adv(p); mb_ptr_adv(p);
mb_ptr_adv(s); mb_ptr_adv(s);

View File

@ -8729,10 +8729,10 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
fold_count = foldedCount(curwin, lnum, &foldinfo); fold_count = foldedCount(curwin, lnum, &foldinfo);
if (fold_count > 0) { if (fold_count > 0) {
text = get_foldtext(curwin, lnum, lnum + fold_count - 1, text = get_foldtext(curwin, lnum, lnum + fold_count - 1, &foldinfo, buf);
&foldinfo, buf); if (text == buf) {
if (text == buf)
text = vim_strsave(text); text = vim_strsave(text);
}
rettv->vval.v_string = text; rettv->vval.v_string = text;
} }
} }
@ -16451,7 +16451,12 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; return;
} }
(void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern); const char *fname = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) {
fname = tv_get_string(&argvars[1]);
}
(void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern,
(char_u *)fname);
} }
/* /*
@ -16801,30 +16806,9 @@ void timer_teardown(void)
*/ */
static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
char_u *p = (char_u *)xstrdup(tv_get_string(&argvars[0]));
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
rettv->vval.v_string = p; rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
false);
while (*p != NUL) {
int l;
if (enc_utf8) {
int c, lc;
c = utf_ptr2char(p);
lc = utf_tolower(c);
l = utf_ptr2len(p);
/* TODO: reallocate string when byte count changes. */
if (utf_char2len(lc) == l)
utf_char2bytes(lc, p);
p += l;
} else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
p += l; /* skip multi-byte character */
else {
*p = TOLOWER_LOC(*p); /* note that tolower() can be a macro */
++p;
}
}
} }
/* /*
@ -16833,7 +16817,8 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
rettv->vval.v_string = (char_u *)strup_save(tv_get_string(&argvars[0])); rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
true);
} }
/* /*

View File

@ -301,7 +301,7 @@ return {
tabpagenr={args={0, 1}}, tabpagenr={args={0, 1}},
tabpagewinnr={args={1, 2}}, tabpagewinnr={args={1, 2}},
tagfiles={}, tagfiles={},
taglist={args=1}, taglist={args={1, 2}},
tan={args=1, func="float_op_wrapper", data="&tan"}, tan={args=1, func="float_op_wrapper", data="&tan"},
tanh={args=1, func="float_op_wrapper", data="&tanh"}, tanh={args=1, func="float_op_wrapper", data="&tanh"},
tempname={}, tempname={},

View File

@ -295,8 +295,6 @@ typval_T decode_string(const char *const s, const size_t len,
/// Parse JSON double-quoted string /// Parse JSON double-quoted string
/// ///
/// @param[in] conv Defines conversion necessary to convert UTF-8 string to
/// &encoding.
/// @param[in] buf Buffer being converted. /// @param[in] buf Buffer being converted.
/// @param[in] buf_len Length of the buffer. /// @param[in] buf_len Length of the buffer.
/// @param[in,out] pp Pointer to the start of the string. Must point to '"'. /// @param[in,out] pp Pointer to the start of the string. Must point to '"'.
@ -313,8 +311,7 @@ typval_T decode_string(const char *const s, const size_t len,
/// value when decoder is restarted, otherwise unused. /// value when decoder is restarted, otherwise unused.
/// ///
/// @return OK in case of success, FAIL in case of error. /// @return OK in case of success, FAIL in case of error.
static inline int parse_json_string(vimconv_T *const conv, static inline int parse_json_string(const char *const buf, const size_t buf_len,
const char *const buf, const size_t buf_len,
const char **const pp, const char **const pp,
ValuesStack *const stack, ValuesStack *const stack,
ContainerStack *const container_stack, ContainerStack *const container_stack,
@ -489,20 +486,6 @@ static inline int parse_json_string(vimconv_T *const conv,
} }
PUT_FST_IN_PAIR(fst_in_pair, str_end); PUT_FST_IN_PAIR(fst_in_pair, str_end);
#undef PUT_FST_IN_PAIR #undef PUT_FST_IN_PAIR
if (conv->vc_type != CONV_NONE) {
size_t str_len = (size_t) (str_end - str);
char *const new_str = (char *) string_convert(conv, (char_u *) str,
&str_len);
if (new_str == NULL) {
emsgf(_("E474: Failed to convert string \"%.*s\" from UTF-8"),
(int) str_len, str);
xfree(str);
goto parse_json_string_fail;
}
xfree(str);
str = new_str;
str_end = new_str + str_len;
}
*str_end = NUL; *str_end = NUL;
typval_T obj = decode_string( typval_T obj = decode_string(
str, (size_t)(str_end - str), hasnul ? kTrue : kFalse, false, true); str, (size_t)(str_end - str), hasnul ? kTrue : kFalse, false, true);
@ -683,9 +666,6 @@ int json_decode_string(const char *const buf, const size_t buf_len,
EMSG(_("E474: Attempt to decode a blank string")); EMSG(_("E474: Attempt to decode a blank string"));
return FAIL; return FAIL;
} }
vimconv_T conv = { .vc_type = CONV_NONE };
convert_setup(&conv, (char_u *) "utf-8", p_enc);
conv.vc_fail = true;
int ret = OK; int ret = OK;
ValuesStack stack = KV_INITIAL_VALUE; ValuesStack stack = KV_INITIAL_VALUE;
ContainerStack container_stack = KV_INITIAL_VALUE; ContainerStack container_stack = KV_INITIAL_VALUE;
@ -831,7 +811,7 @@ json_decode_string_cycle_start:
break; break;
} }
case '"': { case '"': {
if (parse_json_string(&conv, buf, buf_len, &p, &stack, &container_stack, if (parse_json_string(buf, buf_len, &p, &stack, &container_stack,
&next_map_special, &didcomma, &didcolon) &next_map_special, &didcomma, &didcolon)
== FAIL) { == FAIL) {
// Error message was already given // Error message was already given

View File

@ -11,7 +11,7 @@
#include <math.h> #include <math.h>
#include "nvim/eval/encode.h" #include "nvim/eval/encode.h"
#include "nvim/buffer_defs.h" // vimconv_T #include "nvim/buffer_defs.h"
#include "nvim/eval.h" #include "nvim/eval.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/garray.h" #include "nvim/garray.h"
@ -29,10 +29,6 @@
#define utf_ptr2char(b) utf_ptr2char((char_u *)b) #define utf_ptr2char(b) utf_ptr2char((char_u *)b)
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) #define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
#define utf_char2len(b) ((size_t)utf_char2len(b)) #define utf_char2len(b) ((size_t)utf_char2len(b))
#define string_convert(a, b, c) \
((char *)string_convert((vimconv_T *)a, (char_u *)b, c))
#define convert_setup(vcp, from, to) \
(convert_setup(vcp, (char_u *)from, (char_u *)to))
const char *const encode_special_var_names[] = { const char *const encode_special_var_names[] = {
[kSpecialVarNull] = "null", [kSpecialVarNull] = "null",
@ -537,17 +533,6 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \ } \
} while (0) } while (0)
/// Last used p_enc value
///
/// Generic pointer: it is not used as a string, only pointer comparisons are
/// performed. Must not be freed.
static const void *last_p_enc = NULL;
/// Conversion setup for converting from last_p_enc to UTF-8
static vimconv_T p_enc_conv = {
.vc_type = CONV_NONE,
};
/// Escape sequences used in JSON /// Escape sequences used in JSON
static const char escapes[][3] = { static const char escapes[][3] = {
[BS] = "\\b", [BS] = "\\b",
@ -579,33 +564,15 @@ static inline int convert_to_json_string(garray_T *const gap,
} else { } else {
size_t utf_len = len; size_t utf_len = len;
char *tofree = NULL; char *tofree = NULL;
if (last_p_enc != (const void *) p_enc) {
p_enc_conv.vc_type = CONV_NONE;
convert_setup(&p_enc_conv, p_enc, "utf-8");
p_enc_conv.vc_fail = true;
last_p_enc = p_enc;
}
if (p_enc_conv.vc_type != CONV_NONE) {
tofree = string_convert(&p_enc_conv, buf, &utf_len);
if (tofree == NULL) {
emsgf(_("E474: Failed to convert string \"%.*s\" to UTF-8"),
utf_len, utf_buf);
return FAIL;
}
utf_buf = tofree;
}
size_t str_len = 0; size_t str_len = 0;
// Encode character as \u0000 if // Encode character as \uNNNN if
// 1. It is an ASCII control character (0x0 .. 0x1F, 0x7F). // 1. It is an ASCII control character (0x0 .. 0x1F; 0x7F not
// 2. &encoding is not UTF-8 and code point is above 0x7F. // utf_printable and thus not checked specially).
// 3. &encoding is UTF-8 and code point is not printable according to // 2. Code point is not printable according to utf_printable().
// utf_printable(). // This is done to make resulting values displayable on screen also not from
// This is done to make it possible to :echo values when &encoding is not // Neovim.
// UTF-8. #define ENCODE_RAW(ch) \
#define ENCODE_RAW(p_enc_conv, ch) \ (ch >= 0x20 && utf_printable(ch))
(ch >= 0x20 && (p_enc_conv.vc_type == CONV_NONE \
? utf_printable(ch) \
: ch < 0x7F))
for (size_t i = 0; i < utf_len;) { for (size_t i = 0; i < utf_len;) {
const int ch = utf_ptr2char(utf_buf + i); const int ch = utf_ptr2char(utf_buf + i);
const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i)); const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i));
@ -636,7 +603,7 @@ static inline int convert_to_json_string(garray_T *const gap,
utf_len - (i - shift), utf_buf + i - shift); utf_len - (i - shift), utf_buf + i - shift);
xfree(tofree); xfree(tofree);
return FAIL; return FAIL;
} else if (ENCODE_RAW(p_enc_conv, ch)) { } else if (ENCODE_RAW(ch)) {
str_len += shift; str_len += shift;
} else { } else {
str_len += ((sizeof("\\u1234") - 1) str_len += ((sizeof("\\u1234") - 1)
@ -666,7 +633,7 @@ static inline int convert_to_json_string(garray_T *const gap,
break; break;
} }
default: { default: {
if (ENCODE_RAW(p_enc_conv, ch)) { if (ENCODE_RAW(ch)) {
ga_concat_len(gap, utf_buf + i, shift); ga_concat_len(gap, utf_buf + i, shift);
} else if (ch < SURROGATE_FIRST_CHAR) { } else if (ch < SURROGATE_FIRST_CHAR) {
ga_concat_len(gap, ((const char[]) { ga_concat_len(gap, ((const char[]) {

View File

@ -6,6 +6,8 @@
#include "nvim/os/time.h" #include "nvim/os/time.h"
#include "nvim/pos.h" #include "nvim/pos.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
// flags for do_ecmd() // flags for do_ecmd()
#define ECMD_HIDE 0x01 // don't free the current buffer #define ECMD_HIDE 0x01 // don't free the current buffer

View File

@ -1231,7 +1231,7 @@ static int command_line_handle_key(CommandLineState *s)
// command line has no uppercase characters, convert // command line has no uppercase characters, convert
// the character to lowercase // the character to lowercase
if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff)) { if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff)) {
s->c = vim_tolower(s->c); s->c = mb_tolower(s->c);
} }
if (s->c != NUL) { if (s->c != NUL) {
@ -3018,7 +3018,7 @@ ExpandOne (
|| xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_SHELLCMD
|| xp->xp_context == EXPAND_BUFFERS)) { || xp->xp_context == EXPAND_BUFFERS)) {
if (vim_tolower(c0) != vim_tolower(ci)) { if (mb_tolower(c0) != mb_tolower(ci)) {
break; break;
} }
} else if (c0 != ci) { } else if (c0 != ci) {

View File

@ -3,6 +3,9 @@
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h" #include "nvim/ex_cmds.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/os/time.h"
#include "nvim/regexp_defs.h"
/* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */ /* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */
#define WILD_FREE 1 #define WILD_FREE 1

View File

@ -1057,7 +1057,7 @@ static bool ff_wc_equal(char_u *s1, char_u *s2)
c1 = PTR2CHAR(s1 + i); c1 = PTR2CHAR(s1 + i);
c2 = PTR2CHAR(s2 + j); c2 = PTR2CHAR(s2 + j);
if ((p_fic ? vim_tolower(c1) != vim_tolower(c2) : c1 != c2) if ((p_fic ? mb_tolower(c1) != mb_tolower(c2) : c1 != c2)
&& (prev1 != '*' || prev2 != '*')) { && (prev1 != '*' || prev2 != '*')) {
return false; return false;
} }

View File

@ -1,6 +1,11 @@
#ifndef NVIM_FILE_SEARCH_H #ifndef NVIM_FILE_SEARCH_H
#define NVIM_FILE_SEARCH_H #define NVIM_FILE_SEARCH_H
#include <stdlib.h> // for size_t
#include "nvim/types.h" // for char_u
#include "nvim/globals.h" // for CdScope
/* Flags for find_file_*() functions. */ /* Flags for find_file_*() functions. */
#define FINDFILE_FILE 0 /* only files */ #define FINDFILE_FILE 0 /* only files */
#define FINDFILE_DIR 1 /* only directories */ #define FINDFILE_DIR 1 /* only directories */

View File

@ -1,7 +1,12 @@
#ifndef NVIM_FOLD_H #ifndef NVIM_FOLD_H
#define NVIM_FOLD_H #define NVIM_FOLD_H
#include <stdio.h>
#include "nvim/pos.h" #include "nvim/pos.h"
#include "nvim/garray.h"
#include "nvim/types.h"
#include "nvim/buffer_defs.h"
/* /*
* Info used to pass info about a fold from the fold-detection code to the * Info used to pass info about a fold from the fold-detection code to the

View File

@ -2,6 +2,10 @@
#define NVIM_HARDCOPY_H #define NVIM_HARDCOPY_H
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> // for size_t
#include "nvim/types.h" // for char_u
#include "nvim/ex_cmds_defs.h" // for exarg_T
/* /*
* Structure to hold printing color and font attributes. * Structure to hold printing color and font attributes.

View File

@ -10,9 +10,7 @@
// USE_ICONV, or to put the USE_ICONV definition in config.h.in directly. As // USE_ICONV, or to put the USE_ICONV definition in config.h.in directly. As
// it stands, globals.h needs to be included alongside iconv.h. // it stands, globals.h needs to be included alongside iconv.h.
#ifdef HAVE_CONFIG_H
#include "auto/config.h" #include "auto/config.h"
#endif
// Use iconv() when it's available, either by linking to the library at // Use iconv() when it's available, either by linking to the library at
// compile time or by loading it at runtime. // compile time or by loading it at runtime.

View File

@ -1,6 +1,9 @@
#ifndef NVIM_IF_CSCOPE_H #ifndef NVIM_IF_CSCOPE_H
#define NVIM_IF_CSCOPE_H #define NVIM_IF_CSCOPE_H
#include "nvim/types.h" // for char_u and expand_T
#include "nvim/ex_cmds_defs.h" // for exarg_T
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "if_cscope.h.generated.h" # include "if_cscope.h.generated.h"
#endif #endif

View File

@ -62,7 +62,7 @@
* toupper() and tolower() that use the current locale. * toupper() and tolower() that use the current locale.
* Careful: Only call TOUPPER_LOC() and TOLOWER_LOC() with a character in the * Careful: Only call TOUPPER_LOC() and TOLOWER_LOC() with a character in the
* range 0 - 255. toupper()/tolower() on some systems can't handle others. * range 0 - 255. toupper()/tolower() on some systems can't handle others.
* Note: It is often better to use vim_tolower() and vim_toupper(), because many * Note: It is often better to use mb_tolower() and mb_toupper(), because many
* toupper() and tolower() implementations only work for ASCII. * toupper() and tolower() implementations only work for ASCII.
*/ */
#define TOUPPER_LOC toupper #define TOUPPER_LOC toupper

View File

@ -8,6 +8,7 @@
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/pos.h" #include "nvim/pos.h"
#include "nvim/os/time.h" #include "nvim/os/time.h"
#include "nvim/ex_cmds_defs.h" // for exarg_T
/// Set fmark using given value /// Set fmark using given value
#define SET_FMARK(fmarkp_, mark_, fnum_) \ #define SET_FMARK(fmarkp_, mark_, fnum_) \

View File

@ -1174,11 +1174,14 @@ int utf_fold(int a)
return utf_convert(a, foldCase, ARRAY_SIZE(foldCase)); return utf_convert(a, foldCase, ARRAY_SIZE(foldCase));
} }
/* // Vim's own character class functions. These exist because many library
* Return the upper-case equivalent of "a", which is a UCS-4 character. Use // islower()/toupper() etc. do not work properly: they crash when used with
* simple case folding. // invalid values or can't handle latin1 when the locale is C.
*/ // Speed is most important here.
int utf_toupper(int a)
/// Return the upper-case equivalent of "a", which is a UCS-4 character. Use
/// simple case folding.
int mb_toupper(int a)
{ {
/* If 'casemap' contains "keepascii" use ASCII style toupper(). */ /* If 'casemap' contains "keepascii" use ASCII style toupper(). */
if (a < 128 && (cmp_flags & CMP_KEEPASCII)) if (a < 128 && (cmp_flags & CMP_KEEPASCII))
@ -1198,17 +1201,15 @@ int utf_toupper(int a)
return utf_convert(a, toUpper, ARRAY_SIZE(toUpper)); return utf_convert(a, toUpper, ARRAY_SIZE(toUpper));
} }
bool utf_islower(int a) bool mb_islower(int a)
{ {
/* German sharp s is lower case but has no upper case equivalent. */ // German sharp s is lower case but has no upper case equivalent.
return (utf_toupper(a) != a) || a == 0xdf; return (mb_toupper(a) != a) || a == 0xdf;
} }
/* /// Return the lower-case equivalent of "a", which is a UCS-4 character. Use
* Return the lower-case equivalent of "a", which is a UCS-4 character. Use /// simple case folding.
* simple case folding. int mb_tolower(int a)
*/
int utf_tolower(int a)
{ {
/* If 'casemap' contains "keepascii" use ASCII style tolower(). */ /* If 'casemap' contains "keepascii" use ASCII style tolower(). */
if (a < 128 && (cmp_flags & CMP_KEEPASCII)) if (a < 128 && (cmp_flags & CMP_KEEPASCII))
@ -1228,9 +1229,9 @@ int utf_tolower(int a)
return utf_convert(a, toLower, ARRAY_SIZE(toLower)); return utf_convert(a, toLower, ARRAY_SIZE(toLower));
} }
bool utf_isupper(int a) bool mb_isupper(int a)
{ {
return utf_tolower(a) != a; return mb_tolower(a) != a;
} }
static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1,

View File

@ -7,6 +7,7 @@
#include "nvim/iconv.h" #include "nvim/iconv.h"
#include "nvim/func_attr.h" #include "nvim/func_attr.h"
#include "nvim/os/os_defs.h" // For WCHAR, indirect #include "nvim/os/os_defs.h" // For WCHAR, indirect
#include "nvim/types.h" // for char_u
/* /*
* Return byte length of character that starts with byte "b". * Return byte length of character that starts with byte "b".

View File

@ -3,8 +3,10 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include "nvim/types.h" #include "nvim/types.h"
#include "nvim/pos.h"
/// A block number. /// A block number.
/// ///

View File

@ -2,6 +2,8 @@
#define NVIM_MEMLINE_H #define NVIM_MEMLINE_H
#include "nvim/types.h" #include "nvim/types.h"
#include "nvim/pos.h" // for pos_T, linenr_T, colnr_T
#include "nvim/buffer_defs.h" // for buf_T
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "memline.h.generated.h" # include "memline.h.generated.h"

View File

@ -1,6 +1,11 @@
#ifndef NVIM_MENU_H #ifndef NVIM_MENU_H
#define NVIM_MENU_H #define NVIM_MENU_H
#include <stdbool.h> // for bool
#include "nvim/types.h" // for char_u and expand_T
#include "nvim/ex_cmds_defs.h" // for exarg_T
/* Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode */ /* Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode */
#define MENU_INDEX_INVALID -1 #define MENU_INDEX_INVALID -1
#define MENU_INDEX_NORMAL 0 #define MENU_INDEX_NORMAL 0

View File

@ -2730,8 +2730,8 @@ do_dialog (
break; break;
} }
/* Make the character lowercase, as chars in "hotkeys" are. */ // Make the character lowercase, as chars in "hotkeys" are.
c = vim_tolower(c); c = mb_tolower(c);
retval = 1; retval = 1;
for (i = 0; hotkeys[i]; ++i) { for (i = 0; hotkeys[i]; ++i) {
if (has_mbyte) { if (has_mbyte) {
@ -2777,7 +2777,7 @@ copy_char (
if (has_mbyte) { if (has_mbyte) {
if (lowercase) { if (lowercase) {
c = vim_tolower((*mb_ptr2char)(from)); c = mb_tolower((*mb_ptr2char)(from));
return (*mb_char2bytes)(c, to); return (*mb_char2bytes)(c, to);
} else { } else {
len = (*mb_ptr2len)(from); len = (*mb_ptr2len)(from);

View File

@ -2,6 +2,8 @@
#define NVIM_MOVE_H #define NVIM_MOVE_H
#include <stdbool.h> #include <stdbool.h>
#include "nvim/buffer_defs.h"
#include "nvim/pos.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "move.h.generated.h" # include "move.h.generated.h"

View File

@ -1956,16 +1956,18 @@ int swapchar(int op_type, pos_T *pos)
if (enc_dbcs != 0 && c >= 0x100) /* No lower/uppercase letter */ if (enc_dbcs != 0 && c >= 0x100) /* No lower/uppercase letter */
return FALSE; return FALSE;
nc = c; nc = c;
if (vim_islower(c)) { if (mb_islower(c)) {
if (op_type == OP_ROT13) if (op_type == OP_ROT13) {
nc = ROT13(c, 'a'); nc = ROT13(c, 'a');
else if (op_type != OP_LOWER) } else if (op_type != OP_LOWER) {
nc = vim_toupper(c); nc = mb_toupper(c);
} else if (vim_isupper(c)) { }
if (op_type == OP_ROT13) } else if (mb_isupper(c)) {
if (op_type == OP_ROT13) {
nc = ROT13(c, 'A'); nc = ROT13(c, 'A');
else if (op_type != OP_UPPER) } else if (op_type != OP_UPPER) {
nc = vim_tolower(c); nc = mb_tolower(c);
}
} }
if (nc != c) { if (nc != c) {
if (enc_utf8 && (c >= 0x80 || nc >= 0x80)) { if (enc_utf8 && (c >= 0x80 || nc >= 0x80)) {
@ -3327,10 +3329,11 @@ void ex_display(exarg_T *eap)
get_clipboard(name, &yb, true); get_clipboard(name, &yb, true);
if (name == vim_tolower(redir_reg) if (name == mb_tolower(redir_reg)
|| (redir_reg == '"' && yb == y_previous)) || (redir_reg == '"' && yb == y_previous)) {
continue; /* do not list register being written to, the continue; // do not list register being written to, the
* pointer can be freed */ // pointer can be freed
}
if (yb->y_array != NULL) { if (yb->y_array != NULL) {
msg_putchar('\n'); msg_putchar('\n');

View File

@ -8,6 +8,8 @@
#include "nvim/types.h" #include "nvim/types.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/os/time.h" #include "nvim/os/time.h"
#include "nvim/normal.h" // for MotionType and oparg_T
#include "nvim/ex_cmds_defs.h" // for exarg_T
typedef int (*Indenter)(void); typedef int (*Indenter)(void);

View File

@ -1853,7 +1853,7 @@ int pathcmp(const char *p, const char *q, int maxlen)
break; break;
} }
if ((p_fic ? vim_toupper(c1) != vim_toupper(c2) : c1 != c2) if ((p_fic ? mb_toupper(c1) != mb_toupper(c2) : c1 != c2)
#ifdef BACKSLASH_IN_FILENAME #ifdef BACKSLASH_IN_FILENAME
/* consider '/' and '\\' to be equal */ /* consider '/' and '\\' to be equal */
&& !((c1 == '/' && c2 == '\\') && !((c1 == '/' && c2 == '\\')
@ -1864,8 +1864,8 @@ int pathcmp(const char *p, const char *q, int maxlen)
return -1; return -1;
if (vim_ispathsep(c2)) if (vim_ispathsep(c2))
return 1; return 1;
return p_fic ? vim_toupper(c1) - vim_toupper(c2) return p_fic ? mb_toupper(c1) - mb_toupper(c2)
: c1 - c2; /* no match */ : c1 - c2; // no match
} }
i += MB_PTR2LEN((char_u *)p + i); i += MB_PTR2LEN((char_u *)p + i);

View File

@ -2350,7 +2350,7 @@ collection:
break; break;
case CLASS_LOWER: case CLASS_LOWER:
for (cu = 1; cu <= 255; cu++) { for (cu = 1; cu <= 255; cu++) {
if (vim_islower(cu) && cu != 170 && cu != 186) { if (mb_islower(cu) && cu != 170 && cu != 186) {
regmbc(cu); regmbc(cu);
} }
} }
@ -2376,7 +2376,7 @@ collection:
break; break;
case CLASS_UPPER: case CLASS_UPPER:
for (cu = 1; cu <= 255; cu++) { for (cu = 1; cu <= 255; cu++) {
if (vim_isupper(cu)) { if (mb_isupper(cu)) {
regmbc(cu); regmbc(cu);
} }
} }
@ -3474,7 +3474,7 @@ static long bt_regexec_both(char_u *line,
|| (ireg_ic || (ireg_ic
&& (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c))) && (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c)))
|| (c < 255 && prog->regstart < 255 || (c < 255 && prog->regstart < 255
&& vim_tolower(prog->regstart) == vim_tolower(c))))) { && mb_tolower(prog->regstart) == mb_tolower(c))))) {
retval = regtry(prog, col); retval = regtry(prog, col);
} else { } else {
retval = 0; retval = 0;
@ -4155,7 +4155,7 @@ regmatch (
if (*opnd != *reginput if (*opnd != *reginput
&& (!ireg_ic && (!ireg_ic
|| (!enc_utf8 || (!enc_utf8
&& vim_tolower(*opnd) != vim_tolower(*reginput)))) { && mb_tolower(*opnd) != mb_tolower(*reginput)))) {
status = RA_NOMATCH; status = RA_NOMATCH;
} else if (*opnd == NUL) { } else if (*opnd == NUL) {
// match empty string always works; happens when "~" is // match empty string always works; happens when "~" is
@ -4573,12 +4573,14 @@ regmatch (
if (OP(next) == EXACTLY) { if (OP(next) == EXACTLY) {
rst.nextb = *OPERAND(next); rst.nextb = *OPERAND(next);
if (ireg_ic) { if (ireg_ic) {
if (vim_isupper(rst.nextb)) if (mb_isupper(rst.nextb)) {
rst.nextb_ic = vim_tolower(rst.nextb); rst.nextb_ic = mb_tolower(rst.nextb);
else } else {
rst.nextb_ic = vim_toupper(rst.nextb); rst.nextb_ic = mb_toupper(rst.nextb);
} else }
} else {
rst.nextb_ic = rst.nextb; rst.nextb_ic = rst.nextb;
}
} else { } else {
rst.nextb = NUL; rst.nextb = NUL;
rst.nextb_ic = NUL; rst.nextb_ic = NUL;
@ -5339,8 +5341,8 @@ do_class:
* would have been used for it. It does handle single-byte * would have been used for it. It does handle single-byte
* characters, such as latin1. */ * characters, such as latin1. */
if (ireg_ic) { if (ireg_ic) {
cu = vim_toupper(*opnd); cu = mb_toupper(*opnd);
cl = vim_tolower(*opnd); cl = mb_tolower(*opnd);
while (count < maxcount && (*scan == cu || *scan == cl)) { while (count < maxcount && (*scan == cu || *scan == cl)) {
count++; count++;
scan++; scan++;
@ -6312,14 +6314,15 @@ static char_u *cstrchr(char_u *s, int c)
/* tolower() and toupper() can be slow, comparing twice should be a lot /* tolower() and toupper() can be slow, comparing twice should be a lot
* faster (esp. when using MS Visual C++!). * faster (esp. when using MS Visual C++!).
* For UTF-8 need to use folded case. */ * For UTF-8 need to use folded case. */
if (enc_utf8 && c > 0x80) if (c > 0x80) {
cc = utf_fold(c); cc = utf_fold(c);
else if (vim_isupper(c)) } else if (mb_isupper(c)) {
cc = vim_tolower(c); cc = mb_tolower(c);
else if (vim_islower(c)) } else if (mb_islower(c)) {
cc = vim_toupper(c); cc = mb_toupper(c);
else } else {
return vim_strchr(s, c); return vim_strchr(s, c);
}
if (has_mbyte) { if (has_mbyte) {
for (p = s; *p != NUL; p += (*mb_ptr2len)(p)) { for (p = s; *p != NUL; p += (*mb_ptr2len)(p)) {
@ -6348,28 +6351,28 @@ static char_u *cstrchr(char_u *s, int c)
static fptr_T do_upper(int *d, int c) static fptr_T do_upper(int *d, int c)
{ {
*d = vim_toupper(c); *d = mb_toupper(c);
return (fptr_T)NULL; return (fptr_T)NULL;
} }
static fptr_T do_Upper(int *d, int c) static fptr_T do_Upper(int *d, int c)
{ {
*d = vim_toupper(c); *d = mb_toupper(c);
return (fptr_T)do_Upper; return (fptr_T)do_Upper;
} }
static fptr_T do_lower(int *d, int c) static fptr_T do_lower(int *d, int c)
{ {
*d = vim_tolower(c); *d = mb_tolower(c);
return (fptr_T)NULL; return (fptr_T)NULL;
} }
static fptr_T do_Lower(int *d, int c) static fptr_T do_Lower(int *d, int c)
{ {
*d = vim_tolower(c); *d = mb_tolower(c);
return (fptr_T)do_Lower; return (fptr_T)do_Lower;
} }

View File

@ -4373,7 +4373,7 @@ static int check_char_class(int class, int c)
return OK; return OK;
break; break;
case NFA_CLASS_LOWER: case NFA_CLASS_LOWER:
if (vim_islower(c) && c != 170 && c != 186) { if (mb_islower(c) && c != 170 && c != 186) {
return OK; return OK;
} }
break; break;
@ -4391,8 +4391,9 @@ static int check_char_class(int class, int c)
return OK; return OK;
break; break;
case NFA_CLASS_UPPER: case NFA_CLASS_UPPER:
if (vim_isupper(c)) if (mb_isupper(c)) {
return OK; return OK;
}
break; break;
case NFA_CLASS_XDIGIT: case NFA_CLASS_XDIGIT:
if (ascii_isxdigit(c)) if (ascii_isxdigit(c))
@ -4892,7 +4893,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
int c2_len = PTR2LEN(s2); int c2_len = PTR2LEN(s2);
int c2 = PTR2CHAR(s2); int c2 = PTR2CHAR(s2);
if ((c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2))) if ((c1 != c2 && (!ireg_ic || mb_tolower(c1) != mb_tolower(c2)))
|| c1_len != c2_len) { || c1_len != c2_len) {
match = false; match = false;
break; break;
@ -5585,22 +5586,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
break; break;
} }
if (ireg_ic) { if (ireg_ic) {
int curc_low = vim_tolower(curc); int curc_low = mb_tolower(curc);
int done = FALSE; int done = false;
for (; c1 <= c2; ++c1) for (; c1 <= c2; c1++) {
if (vim_tolower(c1) == curc_low) { if (mb_tolower(c1) == curc_low) {
result = result_if_matched; result = result_if_matched;
done = TRUE; done = TRUE;
break; break;
} }
if (done) }
if (done) {
break; break;
} }
}
} else if (state->c < 0 ? check_char_class(state->c, curc) } else if (state->c < 0 ? check_char_class(state->c, curc)
: (curc == state->c : (curc == state->c
|| (ireg_ic && vim_tolower(curc) || (ireg_ic && mb_tolower(curc)
== vim_tolower(state->c)))) { == mb_tolower(state->c)))) {
result = result_if_matched; result = result_if_matched;
break; break;
} }
@ -6003,8 +6006,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
#endif #endif
result = (c == curc); result = (c == curc);
if (!result && ireg_ic) if (!result && ireg_ic) {
result = vim_tolower(c) == vim_tolower(curc); result = mb_tolower(c) == mb_tolower(curc);
}
// If ireg_icombine is not set only skip over the character // If ireg_icombine is not set only skip over the character
// itself. When it is set skip over composing characters. // itself. When it is set skip over composing characters.
@ -6152,8 +6156,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// Checking if the required start character matches is // Checking if the required start character matches is
// cheaper than adding a state that won't match. // cheaper than adding a state that won't match.
c = PTR2CHAR(reginput + clen); c = PTR2CHAR(reginput + clen);
if (c != prog->regstart && (!ireg_ic || vim_tolower(c) if (c != prog->regstart && (!ireg_ic || mb_tolower(c)
!= vim_tolower(prog->regstart))) { != mb_tolower(prog->regstart))) {
#ifdef REGEXP_DEBUG #ifdef REGEXP_DEBUG
fprintf(log_fd, fprintf(log_fd,
" Skipping start state, regstart does not match\n"); " Skipping start state, regstart does not match\n");

View File

@ -335,23 +335,26 @@ int pat_has_uppercase(char_u *pat)
while (*p != NUL) { while (*p != NUL) {
int l; int l;
if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) { if ((l = mb_ptr2len(p)) > 1) {
if (enc_utf8 && utf_isupper(utf_ptr2char(p))) if (mb_isupper(utf_ptr2char(p))) {
return TRUE; return true;
}
p += l; p += l;
} else if (*p == '\\') { } else if (*p == '\\') {
if (p[1] == '_' && p[2] != NUL) /* skip "\_X" */ if (p[1] == '_' && p[2] != NUL) { // skip "\_X"
p += 3; p += 3;
else if (p[1] == '%' && p[2] != NUL) /* skip "\%X" */ } else if (p[1] == '%' && p[2] != NUL) { // skip "\%X"
p += 3; p += 3;
else if (p[1] != NUL) /* skip "\X" */ } else if (p[1] != NUL) { // skip "\X"
p += 2; p += 2;
else } else {
p += 1; p += 1;
} else if (vim_isupper(*p)) }
return TRUE; } else if (mb_isupper(*p)) {
else return true;
++p; } else {
p++;
}
} }
return FALSE; return FALSE;
} }

View File

@ -73,15 +73,10 @@ KHASH_SET_INIT_STR(strset)
(vim_rename((char_u *)a, (char_u *)b)) (vim_rename((char_u *)a, (char_u *)b))
#define mb_strnicmp(a, b, c) \ #define mb_strnicmp(a, b, c) \
(mb_strnicmp((char_u *)a, (char_u *)b, c)) (mb_strnicmp((char_u *)a, (char_u *)b, c))
#define has_non_ascii(a) (has_non_ascii((char_u *)a))
#define string_convert(a, b, c) \
((char *)string_convert((vimconv_T *)a, (char_u *)b, c))
#define path_shorten_fname_if_possible(b) \ #define path_shorten_fname_if_possible(b) \
((char *)path_shorten_fname_if_possible((char_u *)b)) ((char *)path_shorten_fname_if_possible((char_u *)b))
#define buflist_new(ffname, sfname, ...) \ #define buflist_new(ffname, sfname, ...) \
(buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__))
#define convert_setup(vcp, from, to) \
(convert_setup(vcp, (char_u *)from, (char_u *)to))
#define os_isdir(f) (os_isdir((char_u *) f)) #define os_isdir(f) (os_isdir((char_u *) f))
#define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) #define regtilde(s, m) ((char *) regtilde((char_u *) s, m))
#define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) #define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f))
@ -413,8 +408,6 @@ typedef struct sd_read_def {
const char *error; ///< Error message in case of error. const char *error; ///< Error message in case of error.
uintmax_t fpos; ///< Current position (amount of bytes read since uintmax_t fpos; ///< Current position (amount of bytes read since
///< reader structure initialization). May overflow. ///< reader structure initialization). May overflow.
vimconv_T sd_conv; ///< Structure used for converting encodings of some
///< items.
} ShaDaReadDef; } ShaDaReadDef;
struct sd_write_def; struct sd_write_def;
@ -435,8 +428,6 @@ typedef struct sd_write_def {
ShaDaWriteCloser close; ///< Close function. ShaDaWriteCloser close; ///< Close function.
void *cookie; ///< Data describing object written to. void *cookie; ///< Data describing object written to.
const char *error; ///< Error message in case of error. const char *error; ///< Error message in case of error.
vimconv_T sd_conv; ///< Structure used for converting encodings of some
///< items.
} ShaDaWriteDef; } ShaDaWriteDef;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
@ -803,7 +794,7 @@ static int open_shada_file_for_reading(const char *const fname,
return error; return error;
} }
convert_setup(&sd_reader->sd_conv, "utf-8", p_enc); assert(STRCMP(p_enc, "utf-8") == 0);
return 0; return 0;
} }
@ -1899,127 +1890,24 @@ shada_pack_entry_error:
} }
#undef PACK_STRING #undef PACK_STRING
/// Write single ShaDa entry, converting it if needed /// Write single ShaDa entry and free it afterwards
/// ///
/// @warning Frees entry after packing. /// Will not free if entry could not be freed.
/// ///
/// @param[in] packer Packer used to write entry. /// @param[in] packer Packer used to write entry.
/// @param[in] sd_conv Conversion definitions. /// @param[in] entry Entry written.
/// @param[in] entry Entry written. If entry.can_free_entry is false then
/// it assumes that entry was not converted, otherwise it
/// is assumed that entry was already converted.
/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
/// restrictions. /// restrictions.
static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, static inline ShaDaWriteResult shada_pack_pfreed_entry(
const vimconv_T *const sd_conv, msgpack_packer *const packer, PossiblyFreedShadaEntry entry,
PossiblyFreedShadaEntry entry,
const size_t max_kbyte) const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
{ {
ShaDaWriteResult ret = kSDWriteSuccessfull; ShaDaWriteResult ret = kSDWriteSuccessfull;
ret = shada_pack_entry(packer, entry.data, max_kbyte);
if (entry.can_free_entry) { if (entry.can_free_entry) {
ret = shada_pack_entry(packer, entry.data, max_kbyte);
shada_free_shada_entry(&entry.data); shada_free_shada_entry(&entry.data);
return ret;
} }
#define RUN_WITH_CONVERTED_STRING(cstr, code) \
do { \
bool did_convert = false; \
if (sd_conv->vc_type != CONV_NONE && has_non_ascii((cstr))) { \
char *const converted_string = string_convert(sd_conv, (cstr), NULL); \
if (converted_string != NULL) { \
(cstr) = converted_string; \
did_convert = true; \
} \
} \
code \
if (did_convert) { \
xfree((cstr)); \
} \
} while (0)
switch (entry.data.type) {
case kSDItemUnknown:
case kSDItemMissing: {
assert(false);
}
case kSDItemSearchPattern: {
RUN_WITH_CONVERTED_STRING(entry.data.data.search_pattern.pat, {
ret = shada_pack_entry(packer, entry.data, max_kbyte);
});
break;
}
case kSDItemHistoryEntry: {
RUN_WITH_CONVERTED_STRING(entry.data.data.history_item.string, {
ret = shada_pack_entry(packer, entry.data, max_kbyte);
});
break;
}
case kSDItemSubString: {
RUN_WITH_CONVERTED_STRING(entry.data.data.sub_string.sub, {
ret = shada_pack_entry(packer, entry.data, max_kbyte);
});
break;
}
case kSDItemVariable: {
if (sd_conv->vc_type != CONV_NONE) {
typval_T tgttv;
var_item_copy(sd_conv, &entry.data.data.global_var.value, &tgttv,
true, 0);
tv_clear(&entry.data.data.global_var.value);
entry.data.data.global_var.value = tgttv;
}
ret = shada_pack_entry(packer, entry.data, max_kbyte);
break;
}
case kSDItemRegister: {
bool did_convert = false;
if (sd_conv->vc_type != CONV_NONE) {
size_t first_non_ascii = 0;
for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) {
if (has_non_ascii(entry.data.data.reg.contents[i])) {
first_non_ascii = i;
did_convert = true;
break;
}
}
if (did_convert) {
entry.data.data.reg.contents =
xmemdup(entry.data.data.reg.contents,
(entry.data.data.reg.contents_size
* sizeof(entry.data.data.reg.contents[0])));
for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) {
if (i >= first_non_ascii) {
entry.data.data.reg.contents[i] = get_converted_string(
sd_conv,
entry.data.data.reg.contents[i],
strlen(entry.data.data.reg.contents[i]));
} else {
entry.data.data.reg.contents[i] =
xstrdup(entry.data.data.reg.contents[i]);
}
}
}
}
ret = shada_pack_entry(packer, entry.data, max_kbyte);
if (did_convert) {
for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) {
xfree(entry.data.data.reg.contents[i]);
}
xfree(entry.data.data.reg.contents);
}
break;
}
case kSDItemHeader:
case kSDItemGlobalMark:
case kSDItemJump:
case kSDItemBufferList:
case kSDItemLocalMark:
case kSDItemChange: {
ret = shada_pack_entry(packer, entry.data, max_kbyte);
break;
}
}
#undef RUN_WITH_CONVERTED_STRING
return ret; return ret;
} }
@ -2556,11 +2444,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
break; break;
} }
typval_T tgttv; typval_T tgttv;
if (sd_writer->sd_conv.vc_type != CONV_NONE) {
var_item_copy(&sd_writer->sd_conv, &vartv, &tgttv, true, 0);
} else {
tv_copy(&vartv, &tgttv); tv_copy(&vartv, &tgttv);
}
ShaDaWriteResult spe_ret; ShaDaWriteResult spe_ret;
if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) {
.type = kSDItemVariable, .type = kSDItemVariable,
@ -2811,9 +2695,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
do { \ do { \
for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \
if (wms_array[i_].data.type != kSDItemMissing) { \ if (wms_array[i_].data.type != kSDItemMissing) { \
if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \ if (shada_pack_pfreed_entry(packer, wms_array[i_], max_kbyte) \
wms_array[i_], \ == kSDWriteFailed) { \
max_kbyte) == kSDWriteFailed) { \
ret = kSDWriteFailed; \ ret = kSDWriteFailed; \
goto shada_write_exit; \ goto shada_write_exit; \
} \ } \
@ -2823,8 +2706,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
PACK_WMS_ARRAY(wms->global_marks); PACK_WMS_ARRAY(wms->global_marks);
PACK_WMS_ARRAY(wms->registers); PACK_WMS_ARRAY(wms->registers);
for (size_t i = 0; i < wms->jumps_size; i++) { for (size_t i = 0; i < wms->jumps_size; i++) {
if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i], if (shada_pack_pfreed_entry(packer, wms->jumps[i], max_kbyte)
max_kbyte) == kSDWriteFailed) { == kSDWriteFailed) {
ret = kSDWriteFailed; ret = kSDWriteFailed;
goto shada_write_exit; goto shada_write_exit;
} }
@ -2832,8 +2715,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
#define PACK_WMS_ENTRY(wms_entry) \ #define PACK_WMS_ENTRY(wms_entry) \
do { \ do { \
if (wms_entry.data.type != kSDItemMissing) { \ if (wms_entry.data.type != kSDItemMissing) { \
if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \ if (shada_pack_pfreed_entry(packer, wms_entry, max_kbyte) \
max_kbyte) == kSDWriteFailed) { \ == kSDWriteFailed) { \
ret = kSDWriteFailed; \ ret = kSDWriteFailed; \
goto shada_write_exit; \ goto shada_write_exit; \
} \ } \
@ -2860,8 +2743,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
for (size_t i = 0; i < file_markss_to_dump; i++) { for (size_t i = 0; i < file_markss_to_dump; i++) {
PACK_WMS_ARRAY(all_file_markss[i]->marks); PACK_WMS_ARRAY(all_file_markss[i]->marks);
for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) { for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) {
if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, if (shada_pack_pfreed_entry(packer, all_file_markss[i]->changes[j],
all_file_markss[i]->changes[j],
max_kbyte) == kSDWriteFailed) { max_kbyte) == kSDWriteFailed) {
ret = kSDWriteFailed; ret = kSDWriteFailed;
goto shada_write_exit; goto shada_write_exit;
@ -2886,8 +2768,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
if (dump_one_history[i]) { if (dump_one_history[i]) {
hms_insert_whole_neovim_history(&wms->hms[i]); hms_insert_whole_neovim_history(&wms->hms[i]);
HMS_ITER(&wms->hms[i], cur_entry, { HMS_ITER(&wms->hms[i], cur_entry, {
if (shada_pack_encoded_entry( if (shada_pack_pfreed_entry(
packer, &sd_writer->sd_conv, (PossiblyFreedShadaEntry) { packer, (PossiblyFreedShadaEntry) {
.data = cur_entry->data, .data = cur_entry->data,
.can_free_entry = cur_entry->can_free_entry, .can_free_entry = cur_entry->can_free_entry,
}, max_kbyte) == kSDWriteFailed) { }, max_kbyte) == kSDWriteFailed) {
@ -3038,8 +2920,6 @@ shada_write_file_nomerge: {}
verbose_leave(); verbose_leave();
} }
convert_setup(&sd_writer.sd_conv, p_enc, "utf-8");
const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge
? NULL ? NULL
: &sd_reader)); : &sd_reader));
@ -3327,29 +3207,6 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader,
return kSDReadStatusSuccess; return kSDReadStatusSuccess;
} }
/// Convert or copy and return a string
///
/// @param[in] sd_conv Conversion definition.
/// @param[in] str String to convert.
/// @param[in] len String length.
///
/// @return [allocated] converted string or copy of the original string.
static inline char *get_converted_string(const vimconv_T *const sd_conv,
const char *const str,
const size_t len)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
if (!has_non_ascii_len(str, len)) {
return xmemdupz(str, len);
}
size_t new_len = len;
char *const new_str = string_convert(sd_conv, str, &new_len);
if (new_str == NULL) {
return xmemdupz(str, len);
}
return new_str;
}
#define READERR(entry_name, error_desc) \ #define READERR(entry_name, error_desc) \
RERR "Error while reading ShaDa file: " \ RERR "Error while reading ShaDa file: " \
entry_name " entry at position %" PRIu64 " " \ entry_name " entry at position %" PRIu64 " " \
@ -3427,10 +3284,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
sizeof(*unpacked.data.via.map.ptr)); \ sizeof(*unpacked.data.via.map.ptr)); \
ad_ga.ga_len++; \ ad_ga.ga_len++; \
} }
#define CONVERTED(str, len) ( \ #define CONVERTED(str, len) (xmemdupz((str), (len)))
sd_reader->sd_conv.vc_type != CONV_NONE \
? get_converted_string(&sd_reader->sd_conv, (str), (len)) \
: xmemdupz((str), (len)))
#define BIN_CONVERTED(b) CONVERTED(b.ptr, b.size) #define BIN_CONVERTED(b) CONVERTED(b.ptr, b.size)
#define SET_ADDITIONAL_DATA(tgt, name) \ #define SET_ADDITIONAL_DATA(tgt, name) \
do { \ do { \
@ -3803,10 +3657,6 @@ shada_read_next_item_start:
(char) unpacked.data.via.array.ptr[2].via.u64; (char) unpacked.data.via.array.ptr[2].via.u64;
} }
size_t strsize; size_t strsize;
if (sd_reader->sd_conv.vc_type == CONV_NONE
|| !has_non_ascii_len(unpacked.data.via.array.ptr[1].via.bin.ptr,
unpacked.data.via.array.ptr[1].via.bin.size)) {
shada_read_next_item_hist_no_conv:
strsize = ( strsize = (
unpacked.data.via.array.ptr[1].via.bin.size unpacked.data.via.array.ptr[1].via.bin.size
+ 1 // Zero byte + 1 // Zero byte
@ -3815,18 +3665,6 @@ shada_read_next_item_hist_no_conv:
memcpy(entry->data.history_item.string, memcpy(entry->data.history_item.string,
unpacked.data.via.array.ptr[1].via.bin.ptr, unpacked.data.via.array.ptr[1].via.bin.ptr,
unpacked.data.via.array.ptr[1].via.bin.size); unpacked.data.via.array.ptr[1].via.bin.size);
} else {
size_t len = unpacked.data.via.array.ptr[1].via.bin.size;
char *const converted = string_convert(
&sd_reader->sd_conv, unpacked.data.via.array.ptr[1].via.bin.ptr,
&len);
if (converted != NULL) {
strsize = len + 2;
entry->data.history_item.string = xrealloc(converted, strsize);
} else {
goto shada_read_next_item_hist_no_conv;
}
}
entry->data.history_item.string[strsize - 2] = 0; entry->data.history_item.string[strsize - 2] = 0;
entry->data.history_item.string[strsize - 1] = entry->data.history_item.string[strsize - 1] =
entry->data.history_item.sep; entry->data.history_item.sep;
@ -3859,16 +3697,6 @@ shada_read_next_item_hist_no_conv:
"be converted to the VimL value")), initial_fpos); "be converted to the VimL value")), initial_fpos);
goto shada_read_next_item_error; goto shada_read_next_item_error;
} }
if (sd_reader->sd_conv.vc_type != CONV_NONE) {
typval_T tgttv;
var_item_copy(&sd_reader->sd_conv,
&entry->data.global_var.value,
&tgttv,
true,
0);
tv_clear(&entry->data.global_var.value);
entry->data.global_var.value = tgttv;
}
SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2,
entry->data.global_var.additional_elements, entry->data.global_var.additional_elements,
"variable"); "variable");

View File

@ -2526,8 +2526,7 @@ void clear_spell_chartab(spelltab_T *sp)
} }
} }
// Init the chartab used for spelling. Only depends on 'encoding'. // Init the chartab used for spelling. Called once while starting up.
// Called once while starting up and when 'encoding' changes.
// The default is to use isalpha(), but the spell file should define the word // The default is to use isalpha(), but the spell file should define the word
// characters to make it possible that 'encoding' differs from the current // characters to make it possible that 'encoding' differs from the current
// locale. For utf-8 we don't use isalpha() but our own functions. // locale. For utf-8 we don't use isalpha() but our own functions.
@ -2537,37 +2536,18 @@ void init_spell_chartab(void)
did_set_spelltab = false; did_set_spelltab = false;
clear_spell_chartab(&spelltab); clear_spell_chartab(&spelltab);
if (enc_dbcs) { for (i = 128; i < 256; i++) {
// DBCS: assume double-wide characters are word characters.
for (i = 128; i <= 255; ++i)
if (MB_BYTE2LEN(i) == 2)
spelltab.st_isw[i] = true;
} else if (enc_utf8) {
for (i = 128; i < 256; ++i) {
int f = utf_fold(i); int f = utf_fold(i);
int u = utf_toupper(i); int u = mb_toupper(i);
spelltab.st_isu[i] = utf_isupper(i); spelltab.st_isu[i] = mb_isupper(i);
spelltab.st_isw[i] = spelltab.st_isu[i] || utf_islower(i); spelltab.st_isw[i] = spelltab.st_isu[i] || mb_islower(i);
// The folded/upper-cased value is different between latin1 and // The folded/upper-cased value is different between latin1 and
// utf8 for 0xb5, causing E763 for no good reason. Use the latin1 // utf8 for 0xb5, causing E763 for no good reason. Use the latin1
// value for utf-8 to avoid this. // value for utf-8 to avoid this.
spelltab.st_fold[i] = (f < 256) ? f : i; spelltab.st_fold[i] = (f < 256) ? f : i;
spelltab.st_upper[i] = (u < 256) ? u : i; spelltab.st_upper[i] = (u < 256) ? u : i;
} }
} else {
// Rough guess: use locale-dependent library functions.
for (i = 128; i < 256; ++i) {
if (vim_isupper(i)) {
spelltab.st_isw[i] = true;
spelltab.st_isu[i] = true;
spelltab.st_fold[i] = vim_tolower(i);
} else if (vim_islower(i)) {
spelltab.st_isw[i] = true;
spelltab.st_upper[i] = vim_toupper(i);
}
}
}
} }
/// Returns true if "p" points to a word character. /// Returns true if "p" points to a word character.

View File

@ -265,11 +265,11 @@ typedef struct trystate_S {
: (c) < \ : (c) < \
256 ? (int)spelltab.st_fold[c] : (int)towlower(c)) 256 ? (int)spelltab.st_fold[c] : (int)towlower(c))
#define SPELL_TOUPPER(c) (enc_utf8 && (c) >= 128 ? utf_toupper(c) \ #define SPELL_TOUPPER(c) (enc_utf8 && (c) >= 128 ? mb_toupper(c) \
: (c) < \ : (c) < \
256 ? (int)spelltab.st_upper[c] : (int)towupper(c)) 256 ? (int)spelltab.st_upper[c] : (int)towupper(c))
#define SPELL_ISUPPER(c) (enc_utf8 && (c) >= 128 ? utf_isupper(c) \ #define SPELL_ISUPPER(c) (enc_utf8 && (c) >= 128 ? mb_isupper(c) \
: (c) < 256 ? spelltab.st_isu[c] : iswupper(c)) : (c) < 256 ? spelltab.st_isu[c] : iswupper(c))
// First language that is loaded, start of the linked list of loaded // First language that is loaded, start of the linked list of loaded

View File

@ -223,7 +223,9 @@
// few bytes as possible, see offset2bytes()) // few bytes as possible, see offset2bytes())
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#include <wctype.h> #include <wctype.h>
#include <strings.h>
#include "nvim/vim.h" #include "nvim/vim.h"
#include "nvim/spell_defs.h" #include "nvim/spell_defs.h"
@ -266,7 +268,7 @@
#define SAL_REM_ACCENTS 4 #define SAL_REM_ACCENTS 4
#define VIMSPELLMAGIC "VIMspell" // string at start of Vim spell file #define VIMSPELLMAGIC "VIMspell" // string at start of Vim spell file
#define VIMSPELLMAGICL 8 #define VIMSPELLMAGICL (sizeof(VIMSPELLMAGIC) - 1)
#define VIMSPELLVERSION 50 #define VIMSPELLVERSION 50
// Section IDs. Only renumber them when VIMSPELLVERSION changes! // Section IDs. Only renumber them when VIMSPELLVERSION changes!
@ -493,6 +495,64 @@ typedef struct spellinfo_S {
# include "spellfile.c.generated.h" # include "spellfile.c.generated.h"
#endif #endif
/// Read n bytes from fd to buf, returning on errors
///
/// @param[out] buf Buffer to read to, must be at least n bytes long.
/// @param[in] n Amount of bytes to read.
/// @param fd FILE* to read from.
/// @param exit_code Code to run before returning.
///
/// @return Allows to proceed if everything is OK, returns SP_TRUNCERROR if
/// there are not enough bytes, returns SP_OTHERERROR if reading failed.
#define SPELL_READ_BYTES(buf, n, fd, exit_code) \
do { \
const size_t n__SPRB = (n); \
FILE *const fd__SPRB = (fd); \
char *const buf__SPRB = (buf); \
const size_t read_bytes__SPRB = fread(buf__SPRB, 1, n__SPRB, fd__SPRB); \
if (read_bytes__SPRB != n__SPRB) { \
exit_code; \
return feof(fd__SPRB) ? SP_TRUNCERROR : SP_OTHERERROR; \
} \
} while (0)
/// Like #SPELL_READ_BYTES, but also error out if NUL byte was read
///
/// @return Allows to proceed if everything is OK, returns SP_TRUNCERROR if
/// there are not enough bytes, returns SP_OTHERERROR if reading failed,
/// returns SP_FORMERROR if read out a NUL byte.
#define SPELL_READ_NONNUL_BYTES(buf, n, fd, exit_code) \
do { \
const size_t n__SPRNB = (n); \
FILE *const fd__SPRNB = (fd); \
char *const buf__SPRNB = (buf); \
SPELL_READ_BYTES(buf__SPRNB, n__SPRNB, fd__SPRNB, exit_code); \
if (memchr(buf__SPRNB, NUL, (size_t)n__SPRNB)) { \
exit_code; \
return SP_FORMERROR; \
} \
} while (0)
/// Check that spell file starts with a magic string
///
/// Does not check for version of the file.
///
/// @param fd File to check.
///
/// @return 0 in case of success, SP_TRUNCERROR if file contains not enough
/// bytes, SP_FORMERROR if it does not match magic string and
/// SP_OTHERERROR if reading file failed.
static inline int spell_check_magic_string(FILE *const fd)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
{
char buf[VIMSPELLMAGICL];
SPELL_READ_BYTES(buf, VIMSPELLMAGICL, fd, ;);
if (memcmp(buf, VIMSPELLMAGIC, VIMSPELLMAGICL) != 0) {
return SP_FORMERROR;
}
return 0;
}
// Load one spell file and store the info into a slang_T. // Load one spell file and store the info into a slang_T.
// //
// This is invoked in three ways: // This is invoked in three ways:
@ -513,9 +573,7 @@ spell_load_file (
) )
{ {
FILE *fd; FILE *fd;
char_u buf[VIMSPELLMAGICL];
char_u *p; char_u *p;
int i;
int n; int n;
int len; int len;
char_u *save_sourcing_name = sourcing_name; char_u *save_sourcing_name = sourcing_name;
@ -557,12 +615,21 @@ spell_load_file (
sourcing_lnum = 0; sourcing_lnum = 0;
// <HEADER>: <fileID> // <HEADER>: <fileID>
for (i = 0; i < VIMSPELLMAGICL; ++i) const int scms_ret = spell_check_magic_string(fd);
buf[i] = getc(fd); // <fileID> switch (scms_ret) {
if (STRNCMP(buf, VIMSPELLMAGIC, VIMSPELLMAGICL) != 0) { case SP_FORMERROR:
EMSG(_("E757: This does not look like a spell file")); case SP_TRUNCERROR: {
emsgf(_("E757: This does not look like a spell file"));
goto endFAIL; goto endFAIL;
} }
case SP_OTHERERROR: {
emsgf(_("E5042: Failed to read spell file %s: %s"),
fname, strerror(ferror(fd)));
}
case 0: {
break;
}
}
c = getc(fd); // <versionnr> c = getc(fd); // <versionnr>
if (c < VIMSPELLVERSION) { if (c < VIMSPELLVERSION) {
EMSG(_("E771: Old spell file, needs to be updated")); EMSG(_("E771: Old spell file, needs to be updated"));
@ -934,12 +1001,10 @@ static char_u *read_cnt_string(FILE *fd, int cnt_bytes, int *cntp)
// Return SP_*ERROR flags. // Return SP_*ERROR flags.
static int read_region_section(FILE *fd, slang_T *lp, int len) static int read_region_section(FILE *fd, slang_T *lp, int len)
{ {
int i; if (len > 16) {
if (len > 16)
return SP_FORMERROR; return SP_FORMERROR;
for (i = 0; i < len; ++i) }
lp->sl_regions[i] = getc(fd); // <regionname> SPELL_READ_NONNUL_BYTES((char *)lp->sl_regions, (size_t)len, fd, ;);
lp->sl_regions[len] = NUL; lp->sl_regions[len] = NUL;
return 0; return 0;
} }
@ -982,35 +1047,30 @@ static int read_charflags_section(FILE *fd)
// Return SP_*ERROR flags. // Return SP_*ERROR flags.
static int read_prefcond_section(FILE *fd, slang_T *lp) static int read_prefcond_section(FILE *fd, slang_T *lp)
{ {
int cnt;
int i;
int n;
char_u *p;
char_u buf[MAXWLEN + 1];
// <prefcondcnt> <prefcond> ... // <prefcondcnt> <prefcond> ...
cnt = get2c(fd); // <prefcondcnt> const int cnt = get2c(fd); // <prefcondcnt>
if (cnt <= 0) if (cnt <= 0) {
return SP_FORMERROR; return SP_FORMERROR;
}
lp->sl_prefprog = xcalloc(cnt, sizeof(regprog_T *)); lp->sl_prefprog = xcalloc(cnt, sizeof(regprog_T *));
lp->sl_prefixcnt = cnt; lp->sl_prefixcnt = cnt;
for (i = 0; i < cnt; ++i) { for (int i = 0; i < cnt; i++) {
// <prefcond> : <condlen> <condstr> // <prefcond> : <condlen> <condstr>
n = getc(fd); // <condlen> const int n = getc(fd); // <condlen>
if (n < 0 || n >= MAXWLEN) if (n < 0 || n >= MAXWLEN) {
return SP_FORMERROR; return SP_FORMERROR;
}
// When <condlen> is zero we have an empty condition. Otherwise // When <condlen> is zero we have an empty condition. Otherwise
// compile the regexp program used to check for the condition. // compile the regexp program used to check for the condition.
if (n > 0) { if (n > 0) {
char buf[MAXWLEN + 1];
buf[0] = '^'; // always match at one position only buf[0] = '^'; // always match at one position only
p = buf + 1; SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd, ;);
while (n-- > 0) buf[n + 1] = NUL;
*p++ = getc(fd); // <condstr> lp->sl_prefprog[i] = vim_regcomp((char_u *)buf, RE_MAGIC | RE_STRING);
*p = NUL;
lp->sl_prefprog[i] = vim_regcomp(buf, RE_MAGIC + RE_STRING);
} }
} }
return 0; return 0;
@ -1063,7 +1123,6 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first)
// Return SP_*ERROR flags. // Return SP_*ERROR flags.
static int read_sal_section(FILE *fd, slang_T *slang) static int read_sal_section(FILE *fd, slang_T *slang)
{ {
int i;
int cnt; int cnt;
garray_T *gap; garray_T *gap;
salitem_T *smp; salitem_T *smp;
@ -1073,13 +1132,16 @@ static int read_sal_section(FILE *fd, slang_T *slang)
slang->sl_sofo = false; slang->sl_sofo = false;
i = getc(fd); // <salflags> const int flags = getc(fd); // <salflags>
if (i & SAL_F0LLOWUP) if (flags & SAL_F0LLOWUP) {
slang->sl_followup = true; slang->sl_followup = true;
if (i & SAL_COLLAPSE) }
if (flags & SAL_COLLAPSE) {
slang->sl_collapse = true; slang->sl_collapse = true;
if (i & SAL_REM_ACCENTS) }
if (flags & SAL_REM_ACCENTS) {
slang->sl_rem_accents = true; slang->sl_rem_accents = true;
}
cnt = get2c(fd); // <salcount> cnt = get2c(fd); // <salcount>
if (cnt < 0) if (cnt < 0)
@ -1099,7 +1161,8 @@ static int read_sal_section(FILE *fd, slang_T *slang)
smp->sm_lead = p; smp->sm_lead = p;
// Read up to the first special char into sm_lead. // Read up to the first special char into sm_lead.
for (i = 0; i < ccnt; ++i) { int i = 0;
for (; i < ccnt; ++i) {
c = getc(fd); // <salfrom> c = getc(fd); // <salfrom>
if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL) if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL)
break; break;
@ -1125,11 +1188,17 @@ static int read_sal_section(FILE *fd, slang_T *slang)
// Any following chars go in sm_rules. // Any following chars go in sm_rules.
smp->sm_rules = p; smp->sm_rules = p;
if (i < ccnt) if (i < ccnt) {
// store the char we got while checking for end of sm_lead // store the char we got while checking for end of sm_lead
*p++ = c; *p++ = c;
for (++i; i < ccnt; ++i) }
*p++ = getc(fd); // <salfrom> i++;
if (i < ccnt) {
SPELL_READ_NONNUL_BYTES( // <salfrom>
(char *)p, (size_t)(ccnt - i), fd, xfree(smp->sm_lead));
p += (ccnt - i);
i = ccnt;
}
*p++ = NUL; *p++ = NUL;
// <saltolen> <salto> // <saltolen> <salto>
@ -1569,9 +1638,14 @@ spell_read_tree (
// The tree size was computed when writing the file, so that we can // The tree size was computed when writing the file, so that we can
// allocate it as one long block. <nodecount> // allocate it as one long block. <nodecount>
int len = get4c(fd); long len = get4c(fd);
if (len < 0) if (len < 0) {
return SP_TRUNCERROR; return SP_TRUNCERROR;
}
if ((size_t)len >= SIZE_MAX / sizeof(int)) {
// Invalid length, multiply with sizeof(int) would overflow.
return SP_FORMERROR;
}
if (len > 0) { if (len > 0) {
// Allocate the byte array. // Allocate the byte array.
bp = xmalloc(len); bp = xmalloc(len);

View File

@ -291,14 +291,15 @@ void vim_strup(char_u *p)
} }
} }
/// Make given string all upper-case /// Make given string all upper-case or all lower-case
/// ///
/// Handels multi-byte characters as good as possible. /// Handles multi-byte characters as good as possible.
/// ///
/// @param[in] orig Input string. /// @param[in] orig Input string.
/// @param[in] upper If true make uppercase, otherwise lowercase
/// ///
/// @return [allocated] upper-cased string. /// @return [allocated] upper-cased string.
char *strup_save(const char *const orig) char *strcase_save(const char *const orig, bool upper)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{ {
char *res = xstrdup(orig); char *res = xstrdup(orig);
@ -307,9 +308,8 @@ char *strup_save(const char *const orig)
while (*p != NUL) { while (*p != NUL) {
int l; int l;
if (enc_utf8) {
int c = utf_ptr2char((const char_u *)p); int c = utf_ptr2char((const char_u *)p);
int uc = utf_toupper(c); int uc = upper ? mb_toupper(c) : mb_tolower(c);
// Reallocate string when byte count changes. This is rare, // Reallocate string when byte count changes. This is rare,
// thus it's OK to do another malloc()/free(). // thus it's OK to do another malloc()/free().
@ -327,13 +327,6 @@ char *strup_save(const char *const orig)
utf_char2bytes(uc, (char_u *)p); utf_char2bytes(uc, (char_u *)p);
p += newl; p += newl;
} else if (has_mbyte && (l = (*mb_ptr2len)((const char_u *)p)) > 1) {
p += l; // Skip multi-byte character.
} else {
// note that toupper() can be a macro
*p = (char)(uint8_t)TOUPPER_LOC(*p);
p++;
}
} }
return res; return res;

View File

@ -4246,75 +4246,72 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
if (rest != NULL) { if (rest != NULL) {
syn_id = syn_check_group(arg, (int)(group_name_end - arg)); syn_id = syn_check_group(arg, (int)(group_name_end - arg));
if (syn_id != 0) if (syn_id != 0) {
/* allocate a buffer, for removing backslashes in the keyword */ // Allocate a buffer, for removing backslashes in the keyword.
keyword_copy = xmalloc(STRLEN(rest) + 1); keyword_copy = xmalloc(STRLEN(rest) + 1);
}
if (keyword_copy != NULL) {
syn_opt_arg.flags = 0; syn_opt_arg.flags = 0;
syn_opt_arg.keyword = TRUE; syn_opt_arg.keyword = true;
syn_opt_arg.sync_idx = NULL; syn_opt_arg.sync_idx = NULL;
syn_opt_arg.has_cont_list = FALSE; syn_opt_arg.has_cont_list = false;
syn_opt_arg.cont_in_list = NULL; syn_opt_arg.cont_in_list = NULL;
syn_opt_arg.next_list = NULL; syn_opt_arg.next_list = NULL;
/* // The options given apply to ALL keywords, so all options must be
* The options given apply to ALL keywords, so all options must be // found before keywords can be created.
* found before keywords can be created. // 1: collect the options and copy the keywords to keyword_copy.
* 1: collect the options and copy the keywords to keyword_copy.
*/
cnt = 0; cnt = 0;
p = keyword_copy; p = keyword_copy;
for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) { for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) {
rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
if (rest == NULL || ends_excmd(*rest)) if (rest == NULL || ends_excmd(*rest)) {
break; break;
/* Copy the keyword, removing backslashes, and add a NUL. */ }
// Copy the keyword, removing backslashes, and add a NUL.
while (*rest != NUL && !ascii_iswhite(*rest)) { while (*rest != NUL && !ascii_iswhite(*rest)) {
if (*rest == '\\' && rest[1] != NUL) if (*rest == '\\' && rest[1] != NUL) {
++rest; rest++;
}
*p++ = *rest++; *p++ = *rest++;
} }
*p++ = NUL; *p++ = NUL;
++cnt; cnt++;
} }
if (!eap->skip) { if (!eap->skip) {
/* Adjust flags for use of ":syn include". */ // Adjust flags for use of ":syn include".
syn_incl_toplevel(syn_id, &syn_opt_arg.flags); syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
/* // 2: Add an entry for each keyword.
* 2: Add an entry for each keyword.
*/
for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) { for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) {
for (p = vim_strchr(kw, '[');; ) { for (p = vim_strchr(kw, '[');; ) {
if (p != NULL) if (p != NULL) {
*p = NUL; *p = NUL;
}
add_keyword(kw, syn_id, syn_opt_arg.flags, add_keyword(kw, syn_id, syn_opt_arg.flags,
syn_opt_arg.cont_in_list, syn_opt_arg.cont_in_list,
syn_opt_arg.next_list, conceal_char); syn_opt_arg.next_list, conceal_char);
if (p == NULL) if (p == NULL) {
break; break;
}
if (p[1] == NUL) { if (p[1] == NUL) {
EMSG2(_("E789: Missing ']': %s"), kw); emsgf(_("E789: Missing ']': %s"), kw);
goto error; goto error;
} }
if (p[1] == ']') { if (p[1] == ']') {
if (p[2] != NUL) { if (p[2] != NUL) {
EMSG3(_("E890: trailing char after ']': %s]%s"), emsgf(_("E890: trailing char after ']': %s]%s"),
kw, &p[2]); kw, &p[2]);
goto error; goto error;
} }
kw = p + 1; kw = p + 1;
break; // skip over the "]" break; // skip over the "]"
} }
if (has_mbyte) { const int l = (*mb_ptr2len)(p + 1);
int l = (*mb_ptr2len)(p + 1);
memmove(p, p + 1, l); memmove(p, p + 1, l);
p += l; p += l;
} else {
p[0] = p[1];
++p;
}
} }
} }
} }
@ -4324,6 +4321,7 @@ error:
xfree(syn_opt_arg.cont_in_list); xfree(syn_opt_arg.cont_in_list);
xfree(syn_opt_arg.next_list); xfree(syn_opt_arg.next_list);
} }
}
if (rest != NULL) if (rest != NULL)
eap->nextcmd = check_nextcmd(rest); eap->nextcmd = check_nextcmd(rest);

View File

@ -1847,14 +1847,14 @@ parse_line:
} }
} }
} else { } else {
#define TAG_SEP 0x01 #define TAG_SEP 0x02
size_t tag_fname_len = STRLEN(tag_fname); size_t tag_fname_len = STRLEN(tag_fname);
// Save the tag in a buffer. // Save the tag in a buffer.
// Use 0x01 to separate fields (Can't use NUL, because the // Use 0x02 to separate fields (Can't use NUL, because the
// hash key is terminated by NUL). // hash key is terminated by NUL).
// Emacs tag: <mtt><tag_fname><NUL><ebuf><NUL><lbuf> // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
// other tag: <mtt><tag_fname><NUL><NUL><lbuf> // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
// without Emacs tags: <mtt><tag_fname><NUL><lbuf> // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
// Here <mtt> is the "mtt" value plus 1 to avoid NUL. // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
len = (int)tag_fname_len + (int)STRLEN(lbuf) + 3; len = (int)tag_fname_len + (int)STRLEN(lbuf) + 3;
mfp = xmalloc(sizeof(char_u) + len + 1); mfp = xmalloc(sizeof(char_u) + len + 1);
@ -2797,11 +2797,9 @@ add_tag_field (
return retval; return retval;
} }
/* /// Add the tags matching the specified pattern "pat" to the list "list"
* Add the tags matching the specified pattern to the list "list" /// as a dictionary. Use "buf_fname" for priority, unless NULL.
* as a dictionary int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
*/
int get_tags(list_T *list, char_u *pat)
{ {
int num_matches, i, ret; int num_matches, i, ret;
char_u **matches, *p; char_u **matches, *p;
@ -2811,7 +2809,7 @@ int get_tags(list_T *list, char_u *pat)
bool is_static; bool is_static;
ret = find_tags(pat, &num_matches, &matches, ret = find_tags(pat, &num_matches, &matches,
TAG_REGEXP | TAG_NOIC, (int)MAXCOL, NULL); TAG_REGEXP | TAG_NOIC, (int)MAXCOL, buf_fname);
if (ret == OK && num_matches > 0) { if (ret == OK && num_matches > 0) {
for (i = 0; i < num_matches; ++i) { for (i = 0; i < num_matches; ++i) {
int parse_result = parse_match(matches[i], &tp); int parse_result = parse_match(matches[i], &tp);

View File

@ -26,6 +26,7 @@ source test_tabline.vim
" source test_tabpage.vim " source test_tabpage.vim
source test_tagcase.vim source test_tagcase.vim
source test_tagjump.vim source test_tagjump.vim
source test_taglist.vim
source test_true_false.vim source test_true_false.vim
source test_unlet.vim source test_unlet.vim
source test_utf8.vim source test_utf8.vim

View File

@ -29,3 +29,147 @@ func Test_setbufvar_options()
bwipe! bwipe!
endfunc endfunc
func Test_tolower()
call assert_equal("", tolower(""))
" Test with all printable ASCII characters.
call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~',
\ tolower(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'))
if !has('multi_byte')
return
endif
" Test with a few uppercase diacritics.
call assert_equal("aàáâãäåāăąǎǟǡả", tolower("AÀÁÂÃÄÅĀĂĄǍǞǠẢ"))
call assert_equal("bḃḇ", tolower("BḂḆ"))
call assert_equal("cçćĉċč", tolower("CÇĆĈĊČ"))
call assert_equal("dďđḋḏḑ", tolower("DĎĐḊḎḐ"))
call assert_equal("eèéêëēĕėęěẻẽ", tolower("EÈÉÊËĒĔĖĘĚẺẼ"))
call assert_equal("fḟ ", tolower("FḞ "))
call assert_equal("gĝğġģǥǧǵḡ", tolower("GĜĞĠĢǤǦǴḠ"))
call assert_equal("hĥħḣḧḩ", tolower("HĤĦḢḦḨ"))
call assert_equal("iìíîïĩīĭįiǐỉ", tolower("IÌÍÎÏĨĪĬĮİǏỈ"))
call assert_equal("jĵ", tolower("JĴ"))
call assert_equal("kķǩḱḵ", tolower("KĶǨḰḴ"))
call assert_equal("lĺļľŀłḻ", tolower("LĹĻĽĿŁḺ"))
call assert_equal("mḿṁ", tolower("MḾṀ"))
call assert_equal("nñńņňṅṉ", tolower("NÑŃŅŇṄṈ"))
call assert_equal("oòóôõöøōŏőơǒǫǭỏ", tolower("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ"))
call assert_equal("pṕṗ", tolower("PṔṖ"))
call assert_equal("q", tolower("Q"))
call assert_equal("rŕŗřṙṟ", tolower("RŔŖŘṘṞ"))
call assert_equal("sśŝşšṡ", tolower("SŚŜŞŠṠ"))
call assert_equal("tţťŧṫṯ", tolower("TŢŤŦṪṮ"))
call assert_equal("uùúûüũūŭůűųưǔủ", tolower("UÙÚÛÜŨŪŬŮŰŲƯǓỦ"))
call assert_equal("vṽ", tolower("VṼ"))
call assert_equal("wŵẁẃẅẇ", tolower("WŴẀẂẄẆ"))
call assert_equal("xẋẍ", tolower("XẊẌ"))
call assert_equal("yýŷÿẏỳỷỹ", tolower("YÝŶŸẎỲỶỸ"))
call assert_equal("zźżžƶẑẕ", tolower("ZŹŻŽƵẐẔ"))
" Test with a few lowercase diacritics, which should remain unchanged.
call assert_equal("aàáâãäåāăąǎǟǡả", tolower("aàáâãäåāăąǎǟǡả"))
call assert_equal("bḃḇ", tolower("bḃḇ"))
call assert_equal("cçćĉċč", tolower("cçćĉċč"))
call assert_equal("dďđḋḏḑ", tolower("dďđḋḏḑ"))
call assert_equal("eèéêëēĕėęěẻẽ", tolower("eèéêëēĕėęěẻẽ"))
call assert_equal("fḟ", tolower("fḟ"))
call assert_equal("gĝğġģǥǧǵḡ", tolower("gĝğġģǥǧǵḡ"))
call assert_equal("hĥħḣḧḩẖ", tolower("hĥħḣḧḩẖ"))
call assert_equal("iìíîïĩīĭįǐỉ", tolower("iìíîïĩīĭįǐỉ"))
call assert_equal("jĵǰ", tolower("jĵǰ"))
call assert_equal("kķǩḱḵ", tolower("kķǩḱḵ"))
call assert_equal("lĺļľŀłḻ", tolower("lĺļľŀłḻ"))
call assert_equal("mḿṁ ", tolower("mḿṁ "))
call assert_equal("nñńņňʼnṅṉ", tolower("nñńņňʼnṅṉ"))
call assert_equal("oòóôõöøōŏőơǒǫǭỏ", tolower("oòóôõöøōŏőơǒǫǭỏ"))
call assert_equal("pṕṗ", tolower("pṕṗ"))
call assert_equal("q", tolower("q"))
call assert_equal("rŕŗřṙṟ", tolower("rŕŗřṙṟ"))
call assert_equal("sśŝşšṡ", tolower("sśŝşšṡ"))
call assert_equal("tţťŧṫṯẗ", tolower("tţťŧṫṯẗ"))
call assert_equal("uùúûüũūŭůűųưǔủ", tolower("uùúûüũūŭůűųưǔủ"))
call assert_equal("vṽ", tolower("vṽ"))
call assert_equal("wŵẁẃẅẇẘ", tolower("wŵẁẃẅẇẘ"))
call assert_equal("ẋẍ", tolower("ẋẍ"))
call assert_equal("yýÿŷẏẙỳỷỹ", tolower("yýÿŷẏẙỳỷỹ"))
call assert_equal("zźżžƶẑẕ", tolower("zźżžƶẑẕ"))
" According to https://twitter.com/jifa/status/625776454479970304
" Ⱥ (U+023A) and Ⱦ (U+023E) are the *only* code points to increase
" in length (2 to 3 bytes) when lowercased. So let's test them.
call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ"))
endfunc
func Test_toupper()
call assert_equal("", toupper(""))
" Test with all printable ASCII characters.
call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~',
\ toupper(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'))
if !has('multi_byte')
return
endif
" Test with a few lowercase diacritics.
call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", toupper("aàáâãäåāăąǎǟǡả"))
call assert_equal("BḂḆ", toupper("bḃḇ"))
call assert_equal("CÇĆĈĊČ", toupper("cçćĉċč"))
call assert_equal("DĎĐḊḎḐ", toupper("dďđḋḏḑ"))
call assert_equal("EÈÉÊËĒĔĖĘĚẺẼ", toupper("eèéêëēĕėęěẻẽ"))
call assert_equal("FḞ", toupper("fḟ"))
call assert_equal("GĜĞĠĢǤǦǴḠ", toupper("gĝğġģǥǧǵḡ"))
call assert_equal("HĤĦḢḦḨẖ", toupper("hĥħḣḧḩẖ"))
call assert_equal("IÌÍÎÏĨĪĬĮǏỈ", toupper("iìíîïĩīĭįǐỉ"))
call assert_equal("JĴǰ", toupper("jĵǰ"))
call assert_equal("KĶǨḰḴ", toupper("kķǩḱḵ"))
call assert_equal("LĹĻĽĿŁḺ", toupper("lĺļľŀłḻ"))
call assert_equal("MḾṀ ", toupper("mḿṁ "))
call assert_equal("NÑŃŅŇʼnṄṈ", toupper("nñńņňʼnṅṉ"))
call assert_equal("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ", toupper("oòóôõöøōŏőơǒǫǭỏ"))
call assert_equal("PṔṖ", toupper("pṕṗ"))
call assert_equal("Q", toupper("q"))
call assert_equal("RŔŖŘṘṞ", toupper("rŕŗřṙṟ"))
call assert_equal("SŚŜŞŠṠ", toupper("sśŝşšṡ"))
call assert_equal("TŢŤŦṪṮẗ", toupper("tţťŧṫṯẗ"))
call assert_equal("UÙÚÛÜŨŪŬŮŰŲƯǓỦ", toupper("uùúûüũūŭůűųưǔủ"))
call assert_equal("VṼ", toupper("vṽ"))
call assert_equal("WŴẀẂẄẆẘ", toupper("wŵẁẃẅẇẘ"))
call assert_equal("ẊẌ", toupper("ẋẍ"))
call assert_equal("YÝŸŶẎẙỲỶỸ", toupper("yýÿŷẏẙỳỷỹ"))
call assert_equal("ZŹŻŽƵẐẔ", toupper("zźżžƶẑẕ"))
" Test that uppercase diacritics, which should remain unchanged.
call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", toupper("AÀÁÂÃÄÅĀĂĄǍǞǠẢ"))
call assert_equal("BḂḆ", toupper("BḂḆ"))
call assert_equal("CÇĆĈĊČ", toupper("CÇĆĈĊČ"))
call assert_equal("DĎĐḊḎḐ", toupper("DĎĐḊḎḐ"))
call assert_equal("EÈÉÊËĒĔĖĘĚẺẼ", toupper("EÈÉÊËĒĔĖĘĚẺẼ"))
call assert_equal("FḞ ", toupper("FḞ "))
call assert_equal("GĜĞĠĢǤǦǴḠ", toupper("GĜĞĠĢǤǦǴḠ"))
call assert_equal("HĤĦḢḦḨ", toupper("HĤĦḢḦḨ"))
call assert_equal("IÌÍÎÏĨĪĬĮİǏỈ", toupper("IÌÍÎÏĨĪĬĮİǏỈ"))
call assert_equal("JĴ", toupper("JĴ"))
call assert_equal("KĶǨḰḴ", toupper("KĶǨḰḴ"))
call assert_equal("LĹĻĽĿŁḺ", toupper("LĹĻĽĿŁḺ"))
call assert_equal("MḾṀ", toupper("MḾṀ"))
call assert_equal("NÑŃŅŇṄṈ", toupper("NÑŃŅŇṄṈ"))
call assert_equal("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ", toupper("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ"))
call assert_equal("PṔṖ", toupper("PṔṖ"))
call assert_equal("Q", toupper("Q"))
call assert_equal("RŔŖŘṘṞ", toupper("RŔŖŘṘṞ"))
call assert_equal("SŚŜŞŠṠ", toupper("SŚŜŞŠṠ"))
call assert_equal("TŢŤŦṪṮ", toupper("TŢŤŦṪṮ"))
call assert_equal("UÙÚÛÜŨŪŬŮŰŲƯǓỦ", toupper("UÙÚÛÜŨŪŬŮŰŲƯǓỦ"))
call assert_equal("VṼ", toupper("VṼ"))
call assert_equal("WŴẀẂẄẆ", toupper("WŴẀẂẄẆ"))
call assert_equal("XẊẌ", toupper("XẊẌ"))
call assert_equal("YÝŶŸẎỲỶỸ", toupper("YÝŶŸẎỲỶỸ"))
call assert_equal("ZŹŻŽƵẐẔ", toupper("ZŹŻŽƵẐẔ"))
call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ"))
endfunc

View File

@ -1606,6 +1606,40 @@ fun! Test_normal30_changecase()
norm! V~ norm! V~
call assert_equal('THIS IS A simple test: äüöss', getline('.')) call assert_equal('THIS IS A simple test: äüöss', getline('.'))
" Turkish ASCII turns to multi-byte. On Mac the Turkish locale is available
" but toupper()/tolower() don't do the right thing.
if !has('mac') && !has('osx')
try
lang tr_TR.UTF-8
set casemap=
call setline(1, 'iI')
1normal gUU
call assert_equal("\u0130I", getline(1))
call assert_equal("\u0130I", toupper("iI"))
call setline(1, 'iI')
1normal guu
call assert_equal("i\u0131", getline(1))
call assert_equal("i\u0131", tolower("iI"))
set casemap&
call setline(1, 'iI')
1normal gUU
call assert_equal("II", getline(1))
call assert_equal("II", toupper("iI"))
call setline(1, 'iI')
1normal guu
call assert_equal("ii", getline(1))
call assert_equal("ii", tolower("iI"))
lang en_US.UTF-8
catch /E197:/
" can't use Turkish locale
throw 'Skipped: Turkish locale not available'
endtry
endif
" clean up " clean up
bw! bw!
endfunc endfunc

View File

@ -0,0 +1,58 @@
" test 'taglist' function
func Test_taglist()
call writefile([
\ "FFoo\tXfoo\t1",
\ "FBar\tXfoo\t2",
\ "BFoo\tXbar\t1",
\ "BBar\tXbar\t2"
\ ], 'Xtags')
set tags=Xtags
split Xtext
call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo"), {i, v -> v.name}))
call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xtext"), {i, v -> v.name}))
call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xfoo"), {i, v -> v.name}))
call assert_equal(['BFoo', 'FFoo'], map(taglist("Foo", "Xbar"), {i, v -> v.name}))
call delete('Xtags')
bwipe
endfunc
func Test_taglist_native_etags()
if !has('emacs_tags')
return
endif
call writefile([
\ "\x0c",
\ "src/os_unix.c,13491",
\ "set_signals(\x7f1335,32699",
\ "reset_signals(\x7f1407,34136",
\ ], 'Xtags')
set tags=Xtags
call assert_equal([['set_signals', '1335,32699'], ['reset_signals', '1407,34136']],
\ map(taglist('set_signals'), {i, v -> [v.name, v.cmd]}))
call delete('Xtags')
endfunc
func Test_taglist_ctags_etags()
if !has('emacs_tags')
return
endif
call writefile([
\ "\x0c",
\ "src/os_unix.c,13491",
\ "set_signals(void)\x7fset_signals\x011335,32699",
\ "reset_signals(void)\x7freset_signals\x011407,34136",
\ ], 'Xtags')
set tags=Xtags
call assert_equal([['set_signals', '1335,32699'], ['reset_signals', '1407,34136']],
\ map(taglist('set_signals'), {i, v -> [v.name, v.cmd]}))
call delete('Xtags')
endfunc

View File

@ -76,6 +76,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
@ -966,12 +967,12 @@ static u_entry_T *unserialize_uep(bufinfo_T * bi, bool *error,
uep->ue_lcount = undo_read_4c(bi); uep->ue_lcount = undo_read_4c(bi);
uep->ue_size = undo_read_4c(bi); uep->ue_size = undo_read_4c(bi);
char_u **array; char_u **array = NULL;
if (uep->ue_size > 0) { if (uep->ue_size > 0) {
if ((size_t)uep->ue_size < SIZE_MAX / sizeof(char_u *)) {
array = xmalloc(sizeof(char_u *) * (size_t)uep->ue_size); array = xmalloc(sizeof(char_u *) * (size_t)uep->ue_size);
memset(array, 0, sizeof(char_u *) * (size_t)uep->ue_size); memset(array, 0, sizeof(char_u *) * (size_t)uep->ue_size);
} else { }
array = NULL;
} }
uep->ue_array = array; uep->ue_array = array;
@ -1400,7 +1401,9 @@ void u_read_undo(char *name, char_u *hash, char_u *orig_name)
// sequence numbers of the headers. // sequence numbers of the headers.
// When there are no headers uhp_table is NULL. // When there are no headers uhp_table is NULL.
if (num_head > 0) { if (num_head > 0) {
uhp_table = xmalloc((size_t)num_head * sizeof(u_header_T *)); if ((size_t)num_head < SIZE_MAX / sizeof(*uhp_table)) {
uhp_table = xmalloc((size_t)num_head * sizeof(*uhp_table));
}
} }
long num_read_uhps = 0; long num_read_uhps = 0;

View File

@ -11,9 +11,6 @@
#define RUNTIME_DIRNAME "runtime" #define RUNTIME_DIRNAME "runtime"
/* end */ /* end */
/* ============ the header file puzzle (ca. 50-100 pieces) ========= */
#ifdef HAVE_CONFIG_H /* GNU autoconf (or something else) was here */
#include "auto/config.h" #include "auto/config.h"
#define HAVE_PATHDEF #define HAVE_PATHDEF
@ -23,8 +20,7 @@
* test program. Other items from configure may also be wrong then! * test program. Other items from configure may also be wrong then!
*/ */
#if (SIZEOF_INT == 0) #if (SIZEOF_INT == 0)
Error: configure did not run properly.Check auto/config.log. # error Configure did not run properly.
# endif
#endif #endif
#include "nvim/os/os_defs.h" /* bring lots of system header files */ #include "nvim/os/os_defs.h" /* bring lots of system header files */
@ -46,11 +42,6 @@ enum { NUMBUFLEN = 65 };
#include "nvim/keymap.h" #include "nvim/keymap.h"
#include "nvim/macros.h" #include "nvim/macros.h"
/* ================ end of the header file puzzle =============== */
#include "nvim/gettext.h" #include "nvim/gettext.h"
/* special attribute addition: Put message in history */ /* special attribute addition: Put message in history */

View File

@ -5,7 +5,8 @@ for p in ("${TEST_INCLUDE_DIRS}" .. ";"):gmatch("[^;]+") do
table.insert(module.include_paths, p) table.insert(module.include_paths, p)
end end
module.test_include_path = "${CMAKE_BINARY_DIR}/test/includes/post" module.test_build_dir = "${CMAKE_BINARY_DIR}"
module.test_include_path = module.test_build_dir .. "/test/includes/post"
module.test_libnvim_path = "${TEST_LIBNVIM_PATH}" module.test_libnvim_path = "${TEST_LIBNVIM_PATH}"
module.test_source_path = "${CMAKE_SOURCE_DIR}" module.test_source_path = "${CMAKE_SOURCE_DIR}"
module.test_lua_prg = "${LUA_PRG}" module.test_lua_prg = "${LUA_PRG}"

View File

@ -5,7 +5,7 @@ local curbufmeths, ok = helpers.curbufmeths, helpers.ok
local funcs = helpers.funcs local funcs = helpers.funcs
local request = helpers.request local request = helpers.request
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local execute = helpers.execute local feed_command = helpers.feed_command
local insert = helpers.insert local insert = helpers.insert
local NIL = helpers.NIL local NIL = helpers.NIL
local meth_pcall = helpers.meth_pcall local meth_pcall = helpers.meth_pcall
@ -246,7 +246,7 @@ describe('api/buf', function()
end) end)
it("set_line on alternate buffer does not access invalid line (E315)", function() it("set_line on alternate buffer does not access invalid line (E315)", function()
execute('set hidden') feed_command('set hidden')
insert('Initial file') insert('Initial file')
command('enew') command('enew')
insert([[ insert([[
@ -257,7 +257,7 @@ describe('api/buf', function()
The The
Other Other
Buffer]]) Buffer]])
execute('$') feed_command('$')
local retval = exc_exec("call nvim_buf_set_lines(1, 0, 1, v:false, ['test'])") local retval = exc_exec("call nvim_buf_set_lines(1, 0, 1, v:false, ['test'])")
eq(0, retval) eq(0, retval)
end) end)

View File

@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local eq, clear, eval, execute, nvim, next_message = local eq, clear, eval, command, nvim, next_message =
helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.nvim, helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.nvim,
helpers.next_message helpers.next_message
local meths = helpers.meths local meths = helpers.meths
@ -16,8 +16,8 @@ describe('notify', function()
it('sends the notification/args to the corresponding channel', function() it('sends the notification/args to the corresponding channel', function()
eval('rpcnotify('..channel..', "test-event", 1, 2, 3)') eval('rpcnotify('..channel..', "test-event", 1, 2, 3)')
eq({'notification', 'test-event', {1, 2, 3}}, next_message()) eq({'notification', 'test-event', {1, 2, 3}}, next_message())
execute('au FileType lua call rpcnotify('..channel..', "lua!")') command('au FileType lua call rpcnotify('..channel..', "lua!")')
execute('set filetype=lua') command('set filetype=lua')
eq({'notification', 'lua!', {}}, next_message()) eq({'notification', 'lua!', {}}, next_message())
end) end)
end) end)

View File

@ -1,6 +1,8 @@
-- Test server -> client RPC scenarios. Note: unlike `rpcnotify`, to evaluate -- Test server -> client RPC scenarios. Note: unlike `rpcnotify`, to evaluate
-- `rpcrequest` calls we need the client event loop to be running. -- `rpcrequest` calls we need the client event loop to be running.
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local Paths = require('test.config.paths')
local clear, nvim, eval = helpers.clear, helpers.nvim, helpers.eval local clear, nvim, eval = helpers.clear, helpers.nvim, helpers.eval
local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop
local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.funcs local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.funcs
@ -200,7 +202,7 @@ describe('server -> client', function()
\ 'rpc': v:true \ 'rpc': v:true
\ } \ }
]]) ]])
local lua_prog = arg[-1] local lua_prog = Paths.test_lua_prg
meths.set_var("args", {lua_prog, 'test/functional/api/rpc_fixture.lua'}) meths.set_var("args", {lua_prog, 'test/functional/api/rpc_fixture.lua'})
jobid = eval("jobstart(g:args, g:job_opts)") jobid = eval("jobstart(g:args, g:job_opts)")
neq(0, 'jobid') neq(0, 'jobid')

View File

@ -1,9 +1,13 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq local eq = helpers.eq
local eval = helpers.eval local eval = helpers.eval
local clear = helpers.clear
local meths = helpers.meths
local expect = helpers.expect
local command = helpers.command
local exc_exec = helpers.exc_exec
local curbufmeths = helpers.curbufmeths
describe('autocmds:', function() describe('autocmds:', function()
before_each(clear) before_each(clear)
@ -33,4 +37,22 @@ describe('autocmds:', function()
it('v:vim_did_enter is 1 after VimEnter', function() it('v:vim_did_enter is 1 after VimEnter', function()
eq(1, eval('v:vim_did_enter')) eq(1, eval('v:vim_did_enter'))
end) end)
describe('BufLeave autocommand', function()
it('can wipe out the buffer created by :edit which triggered autocmd',
function()
meths.set_option('hidden', true)
curbufmeths.set_lines(0, 1, false, {
'start of test file xx',
'end of test file xx'})
command('autocmd BufLeave * bwipeout yy')
eq('Vim(edit):E143: Autocommands unexpectedly deleted new buffer yy',
exc_exec('edit yy'))
expect([[
start of test file xx
end of test file xx]])
end)
end)
end) end)

View File

@ -4,7 +4,6 @@ local clear = helpers.clear
local command = helpers.command local command = helpers.command
local eq = helpers.eq local eq = helpers.eq
local eval = helpers.eval local eval = helpers.eval
local execute = helpers.execute
local request = helpers.request local request = helpers.request
local source = helpers.source local source = helpers.source
@ -28,7 +27,7 @@ describe('autocmd BufEnter', function()
endtry endtry
endfunction endfunction
]]) ]])
execute("call Test()") command("call Test()")
eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory. eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory.
eq(2, eval("bufnr('%')")) -- Switched to the dir buffer. eq(2, eval("bufnr('%')")) -- Switched to the dir buffer.
end) end)

View File

@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq
local feed, execute, expect, command = helpers.feed, helpers.execute, helpers.expect, helpers.command local feed, command, expect = helpers.feed, helpers.command, helpers.expect
local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq
describe('TextYankPost', function() describe('TextYankPost', function()
@ -8,11 +8,11 @@ describe('TextYankPost', function()
clear() clear()
-- emulate the clipboard so system clipboard isn't affected -- emulate the clipboard so system clipboard isn't affected
execute('let &rtp = "test/functional/fixtures,".&rtp') command('let &rtp = "test/functional/fixtures,".&rtp')
execute('let g:count = 0') command('let g:count = 0')
execute('autocmd TextYankPost * let g:event = copy(v:event)') command('autocmd TextYankPost * let g:event = copy(v:event)')
execute('autocmd TextYankPost * let g:count += 1') command('autocmd TextYankPost * let g:count += 1')
curbufmeths.set_lines(0, -1, true, { curbufmeths.set_lines(0, -1, true, {
'foo\0bar', 'foo\0bar',
@ -61,27 +61,27 @@ describe('TextYankPost', function()
regtype = 'V' regtype = 'V'
}, eval('g:event')) }, eval('g:event'))
execute('set debug=msg') command('set debug=msg')
-- the regcontents should not be changed without copy. -- the regcontents should not be changed without copy.
local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])') local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])')
eq(status,false) eq(status,false)
neq(nil, string.find(err, ':E742:')) neq(nil, string.find(err, ':E742:'))
-- can't mutate keys inside the autocommand -- can't mutate keys inside the autocommand
execute('autocmd! TextYankPost * let v:event.regcontents = 0') command('autocmd! TextYankPost * let v:event.regcontents = 0')
status, err = pcall(command,'normal yy') status, err = pcall(command,'normal yy')
eq(status,false) eq(status,false)
neq(nil, string.find(err, ':E46:')) neq(nil, string.find(err, ':E46:'))
-- can't add keys inside the autocommand -- can't add keys inside the autocommand
execute('autocmd! TextYankPost * let v:event.mykey = 0') command('autocmd! TextYankPost * let v:event.mykey = 0')
status, err = pcall(command,'normal yy') status, err = pcall(command,'normal yy')
eq(status,false) eq(status,false)
neq(nil, string.find(err, ':E742:')) neq(nil, string.find(err, ':E742:'))
end) end)
it('is not invoked recursively', function() it('is not invoked recursively', function()
execute('autocmd TextYankPost * normal "+yy') command('autocmd TextYankPost * normal "+yy')
feed('yy') feed('yy')
eq({ eq({
operator = 'y', operator = 'y',
@ -134,7 +134,7 @@ describe('TextYankPost', function()
feed('"_yy') feed('"_yy')
eq(0, eval('g:count')) eq(0, eval('g:count'))
execute('delete _') command('delete _')
eq(0, eval('g:count')) eq(0, eval('g:count'))
end) end)
@ -155,7 +155,7 @@ describe('TextYankPost', function()
regtype = 'V' regtype = 'V'
}, eval('g:event')) }, eval('g:event'))
execute("set clipboard=unnamed") command("set clipboard=unnamed")
-- regname still shows the name the user requested -- regname still shows the name the user requested
feed('yy') feed('yy')
@ -176,7 +176,7 @@ describe('TextYankPost', function()
end) end)
it('works with Ex commands', function() it('works with Ex commands', function()
execute('1delete +') command('1delete +')
eq({ eq({
operator = 'd', operator = 'd',
regcontents = { 'foo\nbar' }, regcontents = { 'foo\nbar' },
@ -185,7 +185,7 @@ describe('TextYankPost', function()
}, eval('g:event')) }, eval('g:event'))
eq(1, eval('g:count')) eq(1, eval('g:count'))
execute('yank') command('yank')
eq({ eq({
operator = 'y', operator = 'y',
regcontents = { 'baz text' }, regcontents = { 'baz text' },
@ -194,7 +194,7 @@ describe('TextYankPost', function()
}, eval('g:event')) }, eval('g:event'))
eq(2, eval('g:count')) eq(2, eval('g:count'))
execute('normal yw') command('normal yw')
eq({ eq({
operator = 'y', operator = 'y',
regcontents = { 'baz ' }, regcontents = { 'baz ' },
@ -203,7 +203,7 @@ describe('TextYankPost', function()
}, eval('g:event')) }, eval('g:event'))
eq(3, eval('g:count')) eq(3, eval('g:count'))
execute('normal! dd') command('normal! dd')
eq({ eq({
operator = 'd', operator = 'd',
regcontents = { 'baz text' }, regcontents = { 'baz text' },

View File

@ -3,7 +3,7 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local execute, expect, eq, eval = helpers.execute, helpers.expect, helpers.eq, helpers.eval local feed_command, expect, eq, eval = helpers.feed_command, helpers.expect, helpers.eq, helpers.eval
local function basic_register_test(noblock) local function basic_register_test(noblock)
insert("some words") insert("some words")
@ -95,7 +95,7 @@ describe('clipboard usage', function()
before_each(function() before_each(function()
reset() reset()
execute('call getreg("*")') -- force load of provider feed_command('call getreg("*")') -- force load of provider
end) end)
it('has independent "* and unnamed registers per default', function() it('has independent "* and unnamed registers per default', function()
@ -140,8 +140,8 @@ describe('clipboard usage', function()
end) end)
it('support autodectection of regtype', function() it('support autodectection of regtype', function()
execute("let g:test_clip['*'] = ['linewise stuff','']") feed_command("let g:test_clip['*'] = ['linewise stuff','']")
execute("let g:test_clip['+'] = ['charwise','stuff']") feed_command("let g:test_clip['+'] = ['charwise','stuff']")
eq("V", eval("getregtype('*')")) eq("V", eval("getregtype('*')"))
eq("v", eval("getregtype('+')")) eq("v", eval("getregtype('+')"))
insert("just some text") insert("just some text")
@ -156,7 +156,7 @@ describe('clipboard usage', function()
insert([[ insert([[
much much
text]]) text]])
execute("let g:test_clip['*'] = [['very','block'],'b']") feed_command("let g:test_clip['*'] = [['very','block'],'b']")
feed('gg"*P') feed('gg"*P')
expect([[ expect([[
very much very much
@ -170,15 +170,15 @@ describe('clipboard usage', function()
end) end)
it('supports setreg', function() it('supports setreg', function()
execute('call setreg("*", "setted\\ntext", "c")') feed_command('call setreg("*", "setted\\ntext", "c")')
execute('call setreg("+", "explicitly\\nlines", "l")') feed_command('call setreg("+", "explicitly\\nlines", "l")')
feed('"+P"*p') feed('"+P"*p')
expect([[ expect([[
esetted esetted
textxplicitly textxplicitly
lines lines
]]) ]])
execute('call setreg("+", "blocky\\nindeed", "b")') feed_command('call setreg("+", "blocky\\nindeed", "b")')
feed('"+p') feed('"+p')
expect([[ expect([[
esblockyetted esblockyetted
@ -188,13 +188,13 @@ describe('clipboard usage', function()
end) end)
it('supports let @+ (issue #1427)', function() it('supports let @+ (issue #1427)', function()
execute("let @+ = 'some'") feed_command("let @+ = 'some'")
execute("let @* = ' other stuff'") feed_command("let @* = ' other stuff'")
eq({{'some'}, 'v'}, eval("g:test_clip['+']")) eq({{'some'}, 'v'}, eval("g:test_clip['+']"))
eq({{' other stuff'}, 'v'}, eval("g:test_clip['*']")) eq({{' other stuff'}, 'v'}, eval("g:test_clip['*']"))
feed('"+p"*p') feed('"+p"*p')
expect('some other stuff') expect('some other stuff')
execute("let @+ .= ' more'") feed_command("let @+ .= ' more'")
feed('dd"+p') feed('dd"+p')
expect('some more') expect('some more')
end) end)
@ -202,7 +202,7 @@ describe('clipboard usage', function()
it('pastes unnamed register if the provider fails', function() it('pastes unnamed register if the provider fails', function()
insert('the text') insert('the text')
feed('yy') feed('yy')
execute("let g:cliperror = 1") feed_command("let g:cliperror = 1")
feed('"*p') feed('"*p')
expect([[ expect([[
the text the text
@ -214,7 +214,7 @@ describe('clipboard usage', function()
-- the basic behavior of unnamed register should be the same -- the basic behavior of unnamed register should be the same
-- even when handled by clipboard provider -- even when handled by clipboard provider
before_each(function() before_each(function()
execute('set clipboard=unnamed') feed_command('set clipboard=unnamed')
end) end)
it('works', function() it('works', function()
@ -222,7 +222,7 @@ describe('clipboard usage', function()
end) end)
it('works with pure text clipboard', function() it('works with pure text clipboard', function()
execute("let g:cliplossy = 1") feed_command("let g:cliplossy = 1")
-- expect failure for block mode -- expect failure for block mode
basic_register_test(true) basic_register_test(true)
end) end)
@ -237,7 +237,7 @@ describe('clipboard usage', function()
-- "+ shouldn't have changed -- "+ shouldn't have changed
eq({''}, eval("g:test_clip['+']")) eq({''}, eval("g:test_clip['+']"))
execute("let g:test_clip['*'] = ['linewise stuff','']") feed_command("let g:test_clip['*'] = ['linewise stuff','']")
feed('p') feed('p')
expect([[ expect([[
words words
@ -247,7 +247,7 @@ describe('clipboard usage', function()
it('does not clobber "0 when pasting', function() it('does not clobber "0 when pasting', function()
insert('a line') insert('a line')
feed('yy') feed('yy')
execute("let g:test_clip['*'] = ['b line','']") feed_command("let g:test_clip['*'] = ['b line','']")
feed('"0pp"0p') feed('"0pp"0p')
expect([[ expect([[
a line a line
@ -258,20 +258,20 @@ describe('clipboard usage', function()
it('supports v:register and getreg() without parameters', function() it('supports v:register and getreg() without parameters', function()
eq('*', eval('v:register')) eq('*', eval('v:register'))
execute("let g:test_clip['*'] = [['some block',''], 'b']") feed_command("let g:test_clip['*'] = [['some block',''], 'b']")
eq('some block', eval('getreg()')) eq('some block', eval('getreg()'))
eq('\02210', eval('getregtype()')) eq('\02210', eval('getregtype()'))
end) end)
it('yanks visual selection when pasting', function() it('yanks visual selection when pasting', function()
insert("indeed visual") insert("indeed visual")
execute("let g:test_clip['*'] = [['clipboard'], 'c']") feed_command("let g:test_clip['*'] = [['clipboard'], 'c']")
feed("viwp") feed("viwp")
eq({{'visual'}, 'v'}, eval("g:test_clip['*']")) eq({{'visual'}, 'v'}, eval("g:test_clip['*']"))
expect("indeed clipboard") expect("indeed clipboard")
-- explicit "* should do the same -- explicit "* should do the same
execute("let g:test_clip['*'] = [['star'], 'c']") feed_command("let g:test_clip['*'] = [['star'], 'c']")
feed('viw"*p') feed('viw"*p')
eq({{'clipboard'}, 'v'}, eval("g:test_clip['*']")) eq({{'clipboard'}, 'v'}, eval("g:test_clip['*']"))
expect("indeed star") expect("indeed star")
@ -280,7 +280,7 @@ describe('clipboard usage', function()
it('unamed operations work even if the provider fails', function() it('unamed operations work even if the provider fails', function()
insert('the text') insert('the text')
feed('yy') feed('yy')
execute("let g:cliperror = 1") feed_command("let g:cliperror = 1")
feed('p') feed('p')
expect([[ expect([[
the text the text
@ -294,11 +294,11 @@ describe('clipboard usage', function()
match match
text text
]]) ]])
execute('g/match/d') feed_command('g/match/d')
eq('match\n', eval('getreg("*")')) eq('match\n', eval('getreg("*")'))
feed('u') feed('u')
eval('setreg("*", "---")') eval('setreg("*", "---")')
execute('g/test/') feed_command('g/test/')
feed('<esc>') feed('<esc>')
eq('---', eval('getreg("*")')) eq('---', eval('getreg("*")'))
end) end)
@ -307,7 +307,7 @@ describe('clipboard usage', function()
describe('with clipboard=unnamedplus', function() describe('with clipboard=unnamedplus', function()
before_each(function() before_each(function()
execute('set clipboard=unnamedplus') feed_command('set clipboard=unnamedplus')
end) end)
it('links the "+ and unnamed registers', function() it('links the "+ and unnamed registers', function()
@ -320,13 +320,13 @@ describe('clipboard usage', function()
-- "* shouldn't have changed -- "* shouldn't have changed
eq({''}, eval("g:test_clip['*']")) eq({''}, eval("g:test_clip['*']"))
execute("let g:test_clip['+'] = ['three']") feed_command("let g:test_clip['+'] = ['three']")
feed('p') feed('p')
expect('twothree') expect('twothree')
end) end)
it('and unnamed, yanks to both', function() it('and unnamed, yanks to both', function()
execute('set clipboard=unnamedplus,unnamed') feed_command('set clipboard=unnamedplus,unnamed')
insert([[ insert([[
really unnamed really unnamed
text]]) text]])
@ -340,8 +340,8 @@ describe('clipboard usage', function()
-- unnamedplus takes predecence when pasting -- unnamedplus takes predecence when pasting
eq('+', eval('v:register')) eq('+', eval('v:register'))
execute("let g:test_clip['+'] = ['the plus','']") feed_command("let g:test_clip['+'] = ['the plus','']")
execute("let g:test_clip['*'] = ['the star','']") feed_command("let g:test_clip['*'] = ['the star','']")
feed("p") feed("p")
expect([[ expect([[
text text
@ -356,11 +356,11 @@ describe('clipboard usage', function()
match match
text text
]]) ]])
execute('g/match/d') feed_command('g/match/d')
eq('match\n', eval('getreg("+")')) eq('match\n', eval('getreg("+")'))
feed('u') feed('u')
eval('setreg("+", "---")') eval('setreg("+", "---")')
execute('g/test/') feed_command('g/test/')
feed('<esc>') feed('<esc>')
eq('---', eval('getreg("+")')) eq('---', eval('getreg("+")'))
end) end)
@ -375,13 +375,13 @@ describe('clipboard usage', function()
it('supports :put', function() it('supports :put', function()
insert("a line") insert("a line")
execute("let g:test_clip['*'] = ['some text']") feed_command("let g:test_clip['*'] = ['some text']")
execute("let g:test_clip['+'] = ['more', 'text', '']") feed_command("let g:test_clip['+'] = ['more', 'text', '']")
execute(":put *") feed_command(":put *")
expect([[ expect([[
a line a line
some text]]) some text]])
execute(":put +") feed_command(":put +")
expect([[ expect([[
a line a line
some text some text
@ -392,9 +392,9 @@ describe('clipboard usage', function()
it('supports "+ and "* in registers', function() it('supports "+ and "* in registers', function()
local screen = Screen.new(60, 10) local screen = Screen.new(60, 10)
screen:attach() screen:attach()
execute("let g:test_clip['*'] = ['some', 'star data','']") feed_command("let g:test_clip['*'] = ['some', 'star data','']")
execute("let g:test_clip['+'] = ['such', 'plus', 'stuff']") feed_command("let g:test_clip['+'] = ['such', 'plus', 'stuff']")
execute("registers") feed_command("registers")
screen:expect([[ screen:expect([[
~ | ~ |
~ | ~ |
@ -418,17 +418,17 @@ describe('clipboard usage', function()
insert('s/s/t/') insert('s/s/t/')
feed('gg"*y$:<c-r>*<cr>') feed('gg"*y$:<c-r>*<cr>')
expect('t/s/t/') expect('t/s/t/')
execute("let g:test_clip['*'] = ['s/s/u']") feed_command("let g:test_clip['*'] = ['s/s/u']")
feed(':<c-r>*<cr>') feed(':<c-r>*<cr>')
expect('t/u/t/') expect('t/u/t/')
end) end)
it('supports :redir @*>', function() it('supports :redir @*>', function()
execute("let g:test_clip['*'] = ['stuff']") feed_command("let g:test_clip['*'] = ['stuff']")
execute('redir @*>') feed_command('redir @*>')
-- it is made empty -- it is made empty
eq({{''}, 'v'}, eval("g:test_clip['*']")) eq({{''}, 'v'}, eval("g:test_clip['*']"))
execute('let g:test = doesnotexist') feed_command('let g:test = doesnotexist')
feed('<cr>') feed('<cr>')
eq({{ eq({{
'', '',
@ -436,7 +436,7 @@ describe('clipboard usage', function()
'E121: Undefined variable: doesnotexist', 'E121: Undefined variable: doesnotexist',
'E15: Invalid expression: doesnotexist', 'E15: Invalid expression: doesnotexist',
}, 'v'}, eval("g:test_clip['*']")) }, 'v'}, eval("g:test_clip['*']"))
execute(':echo "Howdy!"') feed_command(':echo "Howdy!"')
eq({{ eq({{
'', '',
'', '',
@ -448,7 +448,7 @@ describe('clipboard usage', function()
end) end)
it('handles middleclick correctly', function() it('handles middleclick correctly', function()
execute('set mouse=a') feed_command('set mouse=a')
local screen = Screen.new(30, 5) local screen = Screen.new(30, 5)
screen:attach() screen:attach()
@ -471,7 +471,7 @@ describe('clipboard usage', function()
the a target]]) the a target]])
-- on error, fall back to unnamed register -- on error, fall back to unnamed register
execute("let g:cliperror = 1") feed_command("let g:cliperror = 1")
feed('<MiddleMouse><6,1>') feed('<MiddleMouse><6,1>')
expect([[ expect([[
the source the source

View File

@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, eq, eval, exc_exec, execute, feed, insert, neq, next_msg, nvim, local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim,
nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear, nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear,
helpers.eq, helpers.eval, helpers.exc_exec, helpers.execute, helpers.feed, helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed,
helpers.insert, helpers.neq, helpers.next_message, helpers.nvim, helpers.insert, helpers.neq, helpers.next_message, helpers.nvim,
helpers.nvim_dir, helpers.ok, helpers.source, helpers.nvim_dir, helpers.ok, helpers.source,
helpers.write_file, helpers.mkdir, helpers.rmdir helpers.write_file, helpers.mkdir, helpers.rmdir
@ -94,7 +94,7 @@ describe('jobs', function()
it('returns 0 when it fails to start', function() it('returns 0 when it fails to start', function()
eq("", eval("v:errmsg")) eq("", eval("v:errmsg"))
execute("let g:test_jobid = jobstart([])") feed_command("let g:test_jobid = jobstart([])")
eq(0, eval("g:test_jobid")) eq(0, eval("g:test_jobid"))
eq("E474:", string.match(eval("v:errmsg"), "E%d*:")) eq("E474:", string.match(eval("v:errmsg"), "E%d*:"))
end) end)
@ -470,7 +470,7 @@ describe('jobs', function()
end) end)
it('will return -2 when interrupted', function() it('will return -2 when interrupted', function()
execute('call rpcnotify(g:channel, "ready") | '.. feed_command('call rpcnotify(g:channel, "ready") | '..
'call rpcnotify(g:channel, "wait", '.. 'call rpcnotify(g:channel, "wait", '..
'jobwait([jobstart("sleep 10; exit 55")]))') 'jobwait([jobstart("sleep 10; exit 55")]))')
eq({'notification', 'ready', {}}, next_msg()) eq({'notification', 'ready', {}}, next_msg())
@ -514,7 +514,7 @@ describe('jobs', function()
\ ]) \ ])
endfunction endfunction
]]) ]])
execute('call Run()') feed_command('call Run()')
local r local r
for i = 10, 1, -1 do for i = 10, 1, -1 do
r = next_msg() r = next_msg()
@ -668,7 +668,7 @@ describe("pty process teardown", function()
it("does not prevent/delay exit. #4798 #4900", function() it("does not prevent/delay exit. #4798 #4900", function()
if helpers.pending_win32(pending) then return end if helpers.pending_win32(pending) then return end
-- Use a nested nvim (in :term) to test without --headless. -- Use a nested nvim (in :term) to test without --headless.
execute(":terminal '"..helpers.nvim_prog feed_command(":terminal '"..helpers.nvim_prog
-- Use :term again in the _nested_ nvim to get a PTY process. -- Use :term again in the _nested_ nvim to get a PTY process.
-- Use `sleep` to simulate a long-running child of the PTY. -- Use `sleep` to simulate a long-running child of the PTY.
.."' +terminal +'!(sleep 300 &)' +qa") .."' +terminal +'!(sleep 300 &)' +qa")

View File

@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local lfs = require('lfs') local lfs = require('lfs')
local neq, eq, execute = helpers.neq, helpers.eq, helpers.execute local neq, eq, command = helpers.neq, helpers.eq, helpers.command
local clear, curbufmeths = helpers.clear, helpers.curbufmeths local clear, curbufmeths = helpers.clear, helpers.curbufmeths
local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval
local insert = helpers.insert local insert = helpers.insert
@ -10,17 +10,17 @@ describe('api functions', function()
before_each(clear) before_each(clear)
it("work", function() it("work", function()
execute("call nvim_command('let g:test = 1')") command("call nvim_command('let g:test = 1')")
eq(1, eval("nvim_get_var('test')")) eq(1, eval("nvim_get_var('test')"))
local buf = eval("nvim_get_current_buf()") local buf = eval("nvim_get_current_buf()")
execute("call nvim_buf_set_lines("..buf..", 0, -1, v:true, ['aa', 'bb'])") command("call nvim_buf_set_lines("..buf..", 0, -1, v:true, ['aa', 'bb'])")
expect([[ expect([[
aa aa
bb]]) bb]])
execute("call nvim_win_set_cursor(0, [1, 1])") command("call nvim_win_set_cursor(0, [1, 1])")
execute("call nvim_input('ax<esc>')") command("call nvim_input('ax<esc>')")
expect([[ expect([[
aax aax
bb]]) bb]])
@ -57,7 +57,7 @@ describe('api functions', function()
eq(bnr, bhnd) eq(bnr, bhnd)
eq(wid, whnd) eq(wid, whnd)
execute("new") -- creates new buffer and new window command("new") -- creates new buffer and new window
local bnr2 = eval("bufnr('')") local bnr2 = eval("bufnr('')")
local bhnd2 = eval("nvim_get_current_buf()") local bhnd2 = eval("nvim_get_current_buf()")
local wid2 = eval("win_getid()") local wid2 = eval("win_getid()")
@ -69,7 +69,7 @@ describe('api functions', function()
-- 0 is synonymous to the current buffer -- 0 is synonymous to the current buffer
eq(bnr2, eval("nvim_buf_get_number(0)")) eq(bnr2, eval("nvim_buf_get_number(0)"))
execute("bn") -- show old buffer in new window command("bn") -- show old buffer in new window
eq(bnr, eval("nvim_get_current_buf()")) eq(bnr, eval("nvim_get_current_buf()"))
eq(bnr, eval("bufnr('')")) eq(bnr, eval("bufnr('')"))
eq(bnr, eval("nvim_buf_get_number(0)")) eq(bnr, eval("nvim_buf_get_number(0)"))
@ -81,7 +81,7 @@ describe('api functions', function()
curbufmeths.set_lines(0, -1, true, {"aa\0", "b\0b"}) curbufmeths.set_lines(0, -1, true, {"aa\0", "b\0b"})
eq({'aa\n', 'b\nb'}, eval("nvim_buf_get_lines(0, 0, -1, 1)")) eq({'aa\n', 'b\nb'}, eval("nvim_buf_get_lines(0, 0, -1, 1)"))
execute('call nvim_buf_set_lines(0, 1, 2, v:true, ["xx", "\\nyy"])') command('call nvim_buf_set_lines(0, 1, 2, v:true, ["xx", "\\nyy"])')
eq({'aa\0', 'xx', '\0yy'}, curbufmeths.get_lines(0, -1, 1)) eq({'aa\0', 'xx', '\0yy'}, curbufmeths.get_lines(0, -1, 1))
end) end)
@ -124,9 +124,9 @@ describe('api functions', function()
[5] = {bold = true, foreground = Screen.colors.Blue}, [5] = {bold = true, foreground = Screen.colors.Blue},
}) })
execute("set ft=vim") command("set ft=vim")
execute("let &rtp='build/runtime/,'.&rtp") command("let &rtp='build/runtime/,'.&rtp")
execute("syntax on") command("syntax on")
insert([[ insert([[
call bufnr('%') call bufnr('%')
call nvim_input('typing...') call nvim_input('typing...')

View File

@ -1,13 +1,13 @@
local lfs = require('lfs') local lfs = require('lfs')
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, execute, eval, eq = helpers.clear, helpers.execute, helpers.eval, helpers.eq local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq
before_each(function() before_each(function()
clear() clear()
lfs.mkdir('test-glob') lfs.mkdir('test-glob')
-- Long path might cause "Press ENTER" prompt; use :silent to avoid it. -- Long path might cause "Press ENTER" prompt; use :silent to avoid it.
execute('silent cd test-glob') command('silent cd test-glob')
end) end)
after_each(function() after_each(function()

View File

@ -4,16 +4,17 @@ local funcs = helpers.funcs
local meths = helpers.meths local meths = helpers.meths
local eq = helpers.eq local eq = helpers.eq
local eval = helpers.eval local eval = helpers.eval
local execute = helpers.execute local command = helpers.command
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local redir_exec = helpers.redir_exec local redir_exec = helpers.redir_exec
local NIL = helpers.NIL local NIL = helpers.NIL
local source = helpers.source
describe('json_decode() function', function() describe('json_decode() function', function()
local restart = function(...) local restart = function(...)
clear(...) clear(...)
execute('language C') source([[
execute([[ language C
function Eq(exp, act) function Eq(exp, act)
let act = a:act let act = a:act
let exp = a:exp let exp = a:exp
@ -45,8 +46,6 @@ describe('json_decode() function', function()
endif endif
return 1 return 1
endfunction endfunction
]])
execute([[
function EvalEq(exp, act_expr) function EvalEq(exp, act_expr)
let act = eval(a:act_expr) let act = eval(a:act_expr)
if Eq(a:exp, act) if Eq(a:exp, act)
@ -441,7 +440,7 @@ describe('json_decode() function', function()
local sp_decode_eq = function(expected, json) local sp_decode_eq = function(expected, json)
meths.set_var('__json', json) meths.set_var('__json', json)
speq(expected, 'json_decode(g:__json)') speq(expected, 'json_decode(g:__json)')
execute('unlet! g:__json') command('unlet! g:__json')
end end
it('parses strings with NUL properly', function() it('parses strings with NUL properly', function()
@ -527,7 +526,7 @@ end)
describe('json_encode() function', function() describe('json_encode() function', function()
before_each(function() before_each(function()
clear() clear()
execute('language C') command('language C')
end) end)
it('dumps strings', function() it('dumps strings', function()
@ -576,94 +575,94 @@ describe('json_encode() function', function()
it('cannot dump generic mapping with generic mapping keys and values', it('cannot dump generic mapping with generic mapping keys and values',
function() function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [todumpv1, todumpv2])') command('call add(todump._VAL, [todumpv1, todumpv2])')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end) end)
it('cannot dump generic mapping with ext key', function() it('cannot dump generic mapping with ext key', function()
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end) end)
it('cannot dump generic mapping with array key', function() it('cannot dump generic mapping with array key', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end) end)
it('cannot dump generic mapping with UINT64_MAX key', function() it('cannot dump generic mapping with UINT64_MAX key', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end) end)
it('cannot dump generic mapping with floating-point key', function() it('cannot dump generic mapping with floating-point key', function()
execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') command('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
end) end)
it('can dump generic mapping with STR special key and NUL', function() it('can dump generic mapping with STR special key and NUL', function()
execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}') command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('{"\\u0000": 1}', eval('json_encode(todump)')) eq('{"\\u0000": 1}', eval('json_encode(todump)'))
end) end)
it('can dump generic mapping with BIN special key and NUL', function() it('can dump generic mapping with BIN special key and NUL', function()
execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}') command('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}')
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
eq('{"\\u0000": 1}', eval('json_encode(todump)')) eq('{"\\u0000": 1}', eval('json_encode(todump)'))
end) end)
it('can dump STR special mapping with NUL and NL', function() it('can dump STR special mapping with NUL and NL', function()
execute('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}') command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}')
eq('"\\u0000\\n"', eval('json_encode(todump)')) eq('"\\u0000\\n"', eval('json_encode(todump)'))
end) end)
it('can dump BIN special mapping with NUL and NL', function() it('can dump BIN special mapping with NUL and NL', function()
execute('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}') command('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}')
eq('"\\u0000\\n"', eval('json_encode(todump)')) eq('"\\u0000\\n"', eval('json_encode(todump)'))
end) end)
it('cannot dump special ext mapping', function() it('cannot dump special ext mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)')) eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)'))
end) end)
it('can dump special array mapping', function() it('can dump special array mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
eq('[5, [""]]', eval('json_encode(todump)')) eq('[5, [""]]', eval('json_encode(todump)'))
end) end)
it('can dump special UINT64_MAX mapping', function() it('can dump special UINT64_MAX mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
eq('18446744073709551615', eval('json_encode(todump)')) eq('18446744073709551615', eval('json_encode(todump)'))
end) end)
it('can dump special INT64_MIN mapping', function() it('can dump special INT64_MIN mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [-1, 2, 0, 0]') command('let todump._VAL = [-1, 2, 0, 0]')
eq('-9223372036854775808', eval('json_encode(todump)')) eq('-9223372036854775808', eval('json_encode(todump)'))
end) end)
it('can dump special BOOLEAN true mapping', function() it('can dump special BOOLEAN true mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}')
eq('true', eval('json_encode(todump)')) eq('true', eval('json_encode(todump)'))
end) end)
it('can dump special BOOLEAN false mapping', function() it('can dump special BOOLEAN false mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}')
eq('false', eval('json_encode(todump)')) eq('false', eval('json_encode(todump)'))
end) end)
it('can dump special NIL mapping', function() it('can dump special NIL mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}')
eq('null', eval('json_encode(todump)')) eq('null', eval('json_encode(todump)'))
end) end)
@ -673,7 +672,7 @@ describe('json_encode() function', function()
end) end)
it('fails to dump a partial', function() it('fails to dump a partial', function()
execute('function T() dict\nendfunction') command('function T() dict\nendfunction')
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference', eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
exc_exec('call json_encode(function("T", [1, 2], {}))')) exc_exec('call json_encode(function("T", [1, 2], {}))'))
end) end)
@ -684,56 +683,56 @@ describe('json_encode() function', function()
end) end)
it('fails to dump a recursive list', function() it('fails to dump a recursive list', function()
execute('let todump = [[[]]]') command('let todump = [[[]]]')
execute('call add(todump[0][0], todump)') command('call add(todump[0][0], todump)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')) exc_exec('call json_encode(todump)'))
end) end)
it('fails to dump a recursive dict', function() it('fails to dump a recursive dict', function()
execute('let todump = {"d": {"d": {}}}') command('let todump = {"d": {"d": {}}}')
execute('call extend(todump.d.d, {"d": todump})') command('call extend(todump.d.d, {"d": todump})')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])')) exc_exec('call json_encode([todump])'))
end) end)
it('can dump dict with two same dicts inside', function() it('can dump dict with two same dicts inside', function()
execute('let inter = {}') command('let inter = {}')
execute('let todump = {"a": inter, "b": inter}') command('let todump = {"a": inter, "b": inter}')
eq('{"a": {}, "b": {}}', eval('json_encode(todump)')) eq('{"a": {}, "b": {}}', eval('json_encode(todump)'))
end) end)
it('can dump list with two same lists inside', function() it('can dump list with two same lists inside', function()
execute('let inter = []') command('let inter = []')
execute('let todump = [inter, inter]') command('let todump = [inter, inter]')
eq('[[], []]', eval('json_encode(todump)')) eq('[[], []]', eval('json_encode(todump)'))
end) end)
it('fails to dump a recursive list in a special dict', function() it('fails to dump a recursive list in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, todump)') command('call add(todump._VAL, todump)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')) exc_exec('call json_encode(todump)'))
end) end)
it('fails to dump a recursive (val) map in a special dict', function() it('fails to dump a recursive (val) map in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, ["", todump])') command('call add(todump._VAL, ["", todump])')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])')) exc_exec('call json_encode([todump])'))
end) end)
it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() it('fails to dump a recursive (val) map in a special dict, _VAL reference', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}')
execute('call add(todump._VAL[0][1], todump._VAL)') command('call add(todump._VAL[0][1], todump._VAL)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')) exc_exec('call json_encode(todump)'))
end) end)
it('fails to dump a recursive (val) special list in a special dict', it('fails to dump a recursive (val) special list in a special dict',
function() function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, ["", todump._VAL])') command('call add(todump._VAL, ["", todump._VAL])')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')) exc_exec('call json_encode(todump)'))
end) end)

View File

@ -1,5 +1,5 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, execute, write_file = helpers.clear, helpers.execute, helpers.write_file local clear, command, write_file = helpers.clear, helpers.command, helpers.write_file
local eq, eval = helpers.eq, helpers.eval local eq, eval = helpers.eq, helpers.eval
describe("modeline", function() describe("modeline", function()
@ -12,7 +12,7 @@ describe("modeline", function()
it('does not crash with a large version number', function() it('does not crash with a large version number', function()
write_file(tempfile, 'vim100000000000000000000000') write_file(tempfile, 'vim100000000000000000000000')
execute('e! ' .. tempfile) command('e! ' .. tempfile)
eq(2, eval('1+1')) -- Still alive? eq(2, eval('1+1')) -- Still alive?
end) end)

View File

@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear local clear = helpers.clear
local funcs = helpers.funcs local funcs = helpers.funcs
local eval, eq = helpers.eval, helpers.eq local eval, eq = helpers.eval, helpers.eq
local execute = helpers.execute local command = helpers.command
local nvim = helpers.nvim local nvim = helpers.nvim
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
@ -331,9 +331,9 @@ describe('msgpack*() functions', function()
obj_test('are able to dump and restore floating-point value', {0.125}) obj_test('are able to dump and restore floating-point value', {0.125})
it('can restore and dump UINT64_MAX', function() it('can restore and dump UINT64_MAX', function()
execute('let dumped = ["\\xCF" . repeat("\\xFF", 8)]') command('let dumped = ["\\xCF" . repeat("\\xFF", 8)]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
execute('let dumped2 = msgpackdump(parsed)') command('let dumped2 = msgpackdump(parsed)')
eq(1, eval('type(parsed[0]) == type(0) ' .. eq(1, eval('type(parsed[0]) == type(0) ' ..
'|| parsed[0]._TYPE is v:msgpack_types.integer')) '|| parsed[0]._TYPE is v:msgpack_types.integer'))
if eval('type(parsed[0]) == type(0)') == 1 then if eval('type(parsed[0]) == type(0)') == 1 then
@ -345,9 +345,9 @@ describe('msgpack*() functions', function()
end) end)
it('can restore and dump INT64_MIN', function() it('can restore and dump INT64_MIN', function()
execute('let dumped = ["\\xD3\\x80" . repeat("\\n", 7)]') command('let dumped = ["\\xD3\\x80" . repeat("\\n", 7)]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
execute('let dumped2 = msgpackdump(parsed)') command('let dumped2 = msgpackdump(parsed)')
eq(1, eval('type(parsed[0]) == type(0) ' .. eq(1, eval('type(parsed[0]) == type(0) ' ..
'|| parsed[0]._TYPE is v:msgpack_types.integer')) '|| parsed[0]._TYPE is v:msgpack_types.integer'))
if eval('type(parsed[0]) == type(0)') == 1 then if eval('type(parsed[0]) == type(0)') == 1 then
@ -359,33 +359,33 @@ describe('msgpack*() functions', function()
end) end)
it('can restore and dump BIN string with zero byte', function() it('can restore and dump BIN string with zero byte', function()
execute('let dumped = ["\\xC4\\x01\\n"]') command('let dumped = ["\\xC4\\x01\\n"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
execute('let dumped2 = msgpackdump(parsed)') command('let dumped2 = msgpackdump(parsed)')
eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed')) eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed'))
eq(1, eval('parsed[0]._TYPE is v:msgpack_types.binary')) eq(1, eval('parsed[0]._TYPE is v:msgpack_types.binary'))
eq(1, eval('dumped ==# dumped2')) eq(1, eval('dumped ==# dumped2'))
end) end)
it('can restore and dump STR string with zero byte', function() it('can restore and dump STR string with zero byte', function()
execute('let dumped = ["\\xA1\\n"]') command('let dumped = ["\\xA1\\n"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
execute('let dumped2 = msgpackdump(parsed)') command('let dumped2 = msgpackdump(parsed)')
eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed')) eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed'))
eq(1, eval('parsed[0]._TYPE is v:msgpack_types.string')) eq(1, eval('parsed[0]._TYPE is v:msgpack_types.string'))
eq(1, eval('dumped ==# dumped2')) eq(1, eval('dumped ==# dumped2'))
end) end)
it('can restore and dump BIN string with NL', function() it('can restore and dump BIN string with NL', function()
execute('let dumped = ["\\xC4\\x01", ""]') command('let dumped = ["\\xC4\\x01", ""]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
execute('let dumped2 = msgpackdump(parsed)') command('let dumped2 = msgpackdump(parsed)')
eq({"\n"}, eval('parsed')) eq({"\n"}, eval('parsed'))
eq(1, eval('dumped ==# dumped2')) eq(1, eval('dumped ==# dumped2'))
end) end)
it('dump and restore special mapping with floating-point value', function() it('dump and restore special mapping with floating-point value', function()
execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') command('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}')
eq({0.125}, eval('msgpackparse(msgpackdump([todump]))')) eq({0.125}, eval('msgpackparse(msgpackdump([todump]))'))
end) end)
end) end)
@ -394,52 +394,53 @@ describe('msgpackparse() function', function()
before_each(clear) before_each(clear)
it('restores nil as v:null', function() it('restores nil as v:null', function()
execute('let dumped = ["\\xC0"]') command('let dumped = ["\\xC0"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
eq('[v:null]', eval('string(parsed)')) eq('[v:null]', eval('string(parsed)'))
end) end)
it('restores boolean false as v:false', function() it('restores boolean false as v:false', function()
execute('let dumped = ["\\xC2"]') command('let dumped = ["\\xC2"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
eq({false}, eval('parsed')) eq({false}, eval('parsed'))
end) end)
it('restores boolean true as v:true', function() it('restores boolean true as v:true', function()
execute('let dumped = ["\\xC3"]') command('let dumped = ["\\xC3"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
eq({true}, eval('parsed')) eq({true}, eval('parsed'))
end) end)
it('restores FIXSTR as special dict', function() it('restores FIXSTR as special dict', function()
execute('let dumped = ["\\xa2ab"]') command('let dumped = ["\\xa2ab"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL={'ab'}}}, eval('parsed')) eq({{_TYPE={}, _VAL={'ab'}}}, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.string')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.string'))
end) end)
it('restores BIN 8 as string', function() it('restores BIN 8 as string', function()
execute('let dumped = ["\\xC4\\x02ab"]') command('let dumped = ["\\xC4\\x02ab"]')
eq({'ab'}, eval('msgpackparse(dumped)')) eq({'ab'}, eval('msgpackparse(dumped)'))
end) end)
it('restores FIXEXT1 as special dictionary', function() it('restores FIXEXT1 as special dictionary', function()
execute('let dumped = ["\\xD4\\x10", ""]') command('let dumped = ["\\xD4\\x10", ""]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, eval('parsed')) eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.ext')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.ext'))
end) end)
it('restores MAP with BIN key as special dictionary', function() it('restores MAP with BIN key as special dictionary', function()
execute('let dumped = ["\\x81\\xC4\\x01a\\xC4\\n"]') command('let dumped = ["\\x81\\xC4\\x01a\\xC4\\n"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL={{'a', ''}}}}, eval('parsed')) eq({{_TYPE={}, _VAL={{'a', ''}}}}, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
end) end)
it('restores MAP with duplicate STR keys as special dictionary', function() it('restores MAP with duplicate STR keys as special dictionary', function()
execute('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]') command('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]')
execute('let parsed = msgpackparse(dumped)') -- FIXME Internal error bug
command('silent! let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL={ {{_TYPE={}, _VAL={'a'}}, ''}, eq({{_TYPE={}, _VAL={ {{_TYPE={}, _VAL={'a'}}, ''},
{{_TYPE={}, _VAL={'a'}}, ''}}} }, eval('parsed')) {{_TYPE={}, _VAL={'a'}}, ''}}} }, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
@ -448,8 +449,8 @@ describe('msgpackparse() function', function()
end) end)
it('restores MAP with MAP key as special dictionary', function() it('restores MAP with MAP key as special dictionary', function()
execute('let dumped = ["\\x81\\x80\\xC4\\n"]') command('let dumped = ["\\x81\\x80\\xC4\\n"]')
execute('let parsed = msgpackparse(dumped)') command('let parsed = msgpackparse(dumped)')
eq({{_TYPE={}, _VAL={{{}, ''}}}}, eval('parsed')) eq({{_TYPE={}, _VAL={{{}, ''}}}}, eval('parsed'))
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
end) end)
@ -494,7 +495,7 @@ describe('msgpackparse() function', function()
end) end)
it('fails to parse a partial', function() it('fails to parse a partial', function()
execute('function T() dict\nendfunction') command('function T() dict\nendfunction')
eq('Vim(call):E686: Argument of msgpackparse() must be a List', eq('Vim(call):E686: Argument of msgpackparse() must be a List',
exc_exec('call msgpackparse(function("T", [1, 2], {}))')) exc_exec('call msgpackparse(function("T", [1, 2], {}))'))
end) end)
@ -514,10 +515,10 @@ describe('msgpackdump() function', function()
end) end)
it('can dump generic mapping with generic mapping keys and values', function() it('can dump generic mapping with generic mapping keys and values', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [todumpv1, todumpv2])') command('call add(todump._VAL, [todumpv1, todumpv2])')
eq({'\129\128\128'}, eval('msgpackdump([todump])')) eq({'\129\128\128'}, eval('msgpackdump([todump])'))
end) end)
@ -530,130 +531,130 @@ describe('msgpackdump() function', function()
end) end)
it('can v:null', function() it('can v:null', function()
execute('let todump = v:null') command('let todump = v:null')
end) end)
it('can dump special bool mapping (true)', function() it('can dump special bool mapping (true)', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}')
eq({'\195'}, eval('msgpackdump([todump])')) eq({'\195'}, eval('msgpackdump([todump])'))
end) end)
it('can dump special bool mapping (false)', function() it('can dump special bool mapping (false)', function()
execute('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}')
eq({'\194'}, eval('msgpackdump([todump])')) eq({'\194'}, eval('msgpackdump([todump])'))
end) end)
it('can dump special nil mapping', function() it('can dump special nil mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}')
eq({'\192'}, eval('msgpackdump([todump])')) eq({'\192'}, eval('msgpackdump([todump])'))
end) end)
it('can dump special ext mapping', function() it('can dump special ext mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
eq({'\212\005', ''}, eval('msgpackdump([todump])')) eq({'\212\005', ''}, eval('msgpackdump([todump])'))
end) end)
it('can dump special array mapping', function() it('can dump special array mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])')) eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])'))
end) end)
it('can dump special UINT64_MAX mapping', function() it('can dump special UINT64_MAX mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])')) eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])'))
end) end)
it('can dump special INT64_MIN mapping', function() it('can dump special INT64_MIN mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump = {"_TYPE": v:msgpack_types.integer}')
execute('let todump._VAL = [-1, 2, 0, 0]') command('let todump._VAL = [-1, 2, 0, 0]')
eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])')) eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])'))
end) end)
it('fails to dump a function reference', function() it('fails to dump a function reference', function()
execute('let Todump = function("tr")') command('let Todump = function("tr")')
eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
exc_exec('call msgpackdump([Todump])')) exc_exec('call msgpackdump([Todump])'))
end) end)
it('fails to dump a partial', function() it('fails to dump a partial', function()
execute('function T() dict\nendfunction') command('function T() dict\nendfunction')
execute('let Todump = function("T", [1, 2], {})') command('let Todump = function("T", [1, 2], {})')
eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference', eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
exc_exec('call msgpackdump([Todump])')) exc_exec('call msgpackdump([Todump])'))
end) end)
it('fails to dump a function reference in a list', function() it('fails to dump a function reference in a list', function()
execute('let todump = [function("tr")]') command('let todump = [function("tr")]')
eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference', eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('fails to dump a recursive list', function() it('fails to dump a recursive list', function()
execute('let todump = [[[]]]') command('let todump = [[[]]]')
execute('call add(todump[0][0], todump)') command('call add(todump[0][0], todump)')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('fails to dump a recursive dict', function() it('fails to dump a recursive dict', function()
execute('let todump = {"d": {"d": {}}}') command('let todump = {"d": {"d": {}}}')
execute('call extend(todump.d.d, {"d": todump})') command('call extend(todump.d.d, {"d": todump})')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('can dump dict with two same dicts inside', function() it('can dump dict with two same dicts inside', function()
execute('let inter = {}') command('let inter = {}')
execute('let todump = {"a": inter, "b": inter}') command('let todump = {"a": inter, "b": inter}')
eq({"\130\161a\128\161b\128"}, eval('msgpackdump([todump])')) eq({"\130\161a\128\161b\128"}, eval('msgpackdump([todump])'))
end) end)
it('can dump list with two same lists inside', function() it('can dump list with two same lists inside', function()
execute('let inter = []') command('let inter = []')
execute('let todump = [inter, inter]') command('let todump = [inter, inter]')
eq({"\146\144\144"}, eval('msgpackdump([todump])')) eq({"\146\144\144"}, eval('msgpackdump([todump])'))
end) end)
it('fails to dump a recursive list in a special dict', function() it('fails to dump a recursive list in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, todump)') command('call add(todump._VAL, todump)')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('fails to dump a recursive (key) map in a special dict', function() it('fails to dump a recursive (key) map in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [todump, 0])') command('call add(todump._VAL, [todump, 0])')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('fails to dump a recursive (val) map in a special dict', function() it('fails to dump a recursive (val) map in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [0, todump])') command('call add(todump._VAL, [0, todump])')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('fails to dump a recursive (key) map in a special dict, _VAL reference', function() it('fails to dump a recursive (key) map in a special dict, _VAL reference', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}')
execute('call add(todump._VAL[0][0], todump._VAL)') command('call add(todump._VAL[0][0], todump._VAL)')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() it('fails to dump a recursive (val) map in a special dict, _VAL reference', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}') command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}')
execute('call add(todump._VAL[0][1], todump._VAL)') command('call add(todump._VAL[0][1], todump._VAL)')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
it('fails to dump a recursive (val) special list in a special dict', it('fails to dump a recursive (val) special list in a special dict',
function() function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, [0, todump._VAL])') command('call add(todump._VAL, [0, todump._VAL])')
eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1', eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1',
exc_exec('call msgpackdump([todump])')) exc_exec('call msgpackdump([todump])'))
end) end)
@ -689,7 +690,7 @@ describe('msgpackdump() function', function()
end) end)
it('fails to dump a partial', function() it('fails to dump a partial', function()
execute('function T() dict\nendfunction') command('function T() dict\nendfunction')
eq('Vim(call):E686: Argument of msgpackdump() must be a List', eq('Vim(call):E686: Argument of msgpackdump() must be a List',
exc_exec('call msgpackdump(function("T", [1, 2], {}))')) exc_exec('call msgpackdump(function("T", [1, 2], {}))'))
end) end)

View File

@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
local neq, execute, funcs = helpers.neq, helpers.execute, helpers.funcs local neq, command, funcs = helpers.neq, helpers.command, helpers.funcs
local reltime, reltimestr, reltimefloat = funcs.reltime, funcs.reltimestr, funcs.reltimefloat local reltime, reltimestr, reltimefloat = funcs.reltime, funcs.reltimestr, funcs.reltimefloat
describe('reltimestr(), reltimefloat()', function() describe('reltimestr(), reltimefloat()', function()
@ -8,7 +8,7 @@ describe('reltimestr(), reltimefloat()', function()
it('Checks', function() it('Checks', function()
local now = reltime() local now = reltime()
execute('sleep 10m') command('sleep 10m')
local later = reltime() local later = reltime()
local elapsed = reltime(now) local elapsed = reltime(now)

View File

@ -3,7 +3,7 @@ local setpos = helpers.funcs.setpos
local getpos = helpers.funcs.getpos local getpos = helpers.funcs.getpos
local insert = helpers.insert local insert = helpers.insert
local clear = helpers.clear local clear = helpers.clear
local execute = helpers.execute local command = helpers.command
local eval = helpers.eval local eval = helpers.eval
local eq = helpers.eq local eq = helpers.eq
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
@ -16,7 +16,7 @@ describe('setpos() function', function()
First line of text First line of text
Second line of text Second line of text
Third line of text]]) Third line of text]])
execute('new') command('new')
insert([[ insert([[
Line of text 1 Line of text 1
Line of text 2 Line of text 2
@ -34,7 +34,8 @@ describe('setpos() function', function()
it('can set lowercase marks in the current buffer', function() it('can set lowercase marks in the current buffer', function()
setpos("'d", {0, 2, 1, 0}) setpos("'d", {0, 2, 1, 0})
eq(getpos("'d"), {0, 2, 1, 0}) eq(getpos("'d"), {0, 2, 1, 0})
execute('undo', 'call setpos("\'d", [2, 3, 1, 0])') command('undo')
command('call setpos("\'d", [2, 3, 1, 0])')
eq(getpos("'d"), {0, 3, 1, 0}) eq(getpos("'d"), {0, 3, 1, 0})
end) end)
it('can set lowercase marks in other buffers', function() it('can set lowercase marks in other buffers', function()
@ -42,7 +43,7 @@ describe('setpos() function', function()
eq(0, retval) eq(0, retval)
setpos("'d", {1, 2, 1, 0}) setpos("'d", {1, 2, 1, 0})
eq(getpos("'d"), {0, 0, 0, 0}) eq(getpos("'d"), {0, 0, 0, 0})
execute('wincmd w') command('wincmd w')
eq(eval('bufnr("%")'), 1) eq(eval('bufnr("%")'), 1)
eq(getpos("'d"), {0, 2, 1, 0}) eq(getpos("'d"), {0, 2, 1, 0})
end) end)

View File

@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local execute = helpers.execute local command = helpers.command
local funcs = helpers.funcs local funcs = helpers.funcs
local clear = helpers.clear local clear = helpers.clear
local eval = helpers.eval local eval = helpers.eval
@ -12,7 +12,7 @@ describe('Special values', function()
before_each(clear) before_each(clear)
it('do not cause error when freed', function() it('do not cause error when freed', function()
execute([[ command([[
function Test() function Test()
try try
return v:true return v:true
@ -109,7 +109,7 @@ describe('Special values', function()
it('does not work with +=/-=/.=', function() it('does not work with +=/-=/.=', function()
meths.set_var('true', true) meths.set_var('true', true)
meths.set_var('false', false) meths.set_var('false', false)
execute('let null = v:null') command('let null = v:null')
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let true += 1')) eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let true += 1'))
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let false += 1')) eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let false += 1'))

View File

@ -1,6 +1,8 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local eq, call, clear, eval, execute, feed, nvim =
helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.execute, local nvim_dir = helpers.nvim_dir
local eq, call, clear, eval, feed_command, feed, nvim =
helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command,
helpers.feed, helpers.nvim helpers.feed, helpers.nvim
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
@ -31,7 +33,7 @@ describe('system()', function()
describe('command passed as a List', function() describe('command passed as a List', function()
local function printargs_path() local function printargs_path()
return helpers.nvim_dir..'/printargs-test' return nvim_dir..'/printargs-test'
.. (helpers.os_name() == 'windows' and '.exe' or '') .. (helpers.os_name() == 'windows' and '.exe' or '')
end end
@ -43,10 +45,10 @@ describe('system()', function()
it('parameter validation does NOT modify v:shell_error', function() it('parameter validation does NOT modify v:shell_error', function()
-- 1. Call system() with invalid parameters. -- 1. Call system() with invalid parameters.
-- 2. Assert that v:shell_error was NOT set. -- 2. Assert that v:shell_error was NOT set.
execute('call system({})') feed_command('call system({})')
eq('E475: Invalid argument: expected String or List', eval('v:errmsg')) eq('E475: Invalid argument: expected String or List', eval('v:errmsg'))
eq(0, eval('v:shell_error')) eq(0, eval('v:shell_error'))
execute('call system([])') feed_command('call system([])')
eq('E474: Invalid argument', eval('v:errmsg')) eq('E474: Invalid argument', eval('v:errmsg'))
eq(0, eval('v:shell_error')) eq(0, eval('v:shell_error'))
@ -57,9 +59,9 @@ describe('system()', function()
-- 1. Call system() with invalid parameters. -- 1. Call system() with invalid parameters.
-- 2. Assert that v:shell_error was NOT modified. -- 2. Assert that v:shell_error was NOT modified.
execute('call system({})') feed_command('call system({})')
eq(old_val, eval('v:shell_error')) eq(old_val, eval('v:shell_error'))
execute('call system([])') feed_command('call system([])')
eq(old_val, eval('v:shell_error')) eq(old_val, eval('v:shell_error'))
end) end)
@ -182,7 +184,7 @@ describe('system()', function()
end) end)
it('to backgrounded command does not crash', function() it('to backgrounded command does not crash', function()
-- This is indeterminate, just exercise the codepath. May get E5677. -- This is indeterminate, just exercise the codepath. May get E5677.
execute('call system("echo -n echoed &")') feed_command('call system("echo -n echoed &")')
local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
if v_errnum then if v_errnum then
eq("E5677:", v_errnum) eq("E5677:", v_errnum)
@ -197,7 +199,7 @@ describe('system()', function()
end) end)
it('to backgrounded command does not crash', function() it('to backgrounded command does not crash', function()
-- This is indeterminate, just exercise the codepath. May get E5677. -- This is indeterminate, just exercise the codepath. May get E5677.
execute('call system("cat - &")') feed_command('call system("cat - &")')
local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
if v_errnum then if v_errnum then
eq("E5677:", v_errnum) eq("E5677:", v_errnum)

View File

@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval
local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run
local clear, execute, funcs = helpers.clear, helpers.execute, helpers.funcs local clear, command, funcs = helpers.clear, helpers.command, helpers.funcs
local curbufmeths = helpers.curbufmeths local curbufmeths = helpers.curbufmeths
describe('timers', function() describe('timers', function()
@ -17,14 +17,14 @@ describe('timers', function()
end) end)
it('works one-shot', function() it('works one-shot', function()
execute("call timer_start(50, 'MyHandler')") command("call timer_start(50, 'MyHandler')")
eq(0,eval("g:val")) eq(0,eval("g:val"))
run(nil, nil, nil, 200) run(nil, nil, nil, 200)
eq(1,eval("g:val")) eq(1,eval("g:val"))
end) end)
it('works one-shot when repeat=0', function() it('works one-shot when repeat=0', function()
execute("call timer_start(50, 'MyHandler', {'repeat': 0})") command("call timer_start(50, 'MyHandler', {'repeat': 0})")
eq(0,eval("g:val")) eq(0,eval("g:val"))
run(nil, nil, nil, 200) run(nil, nil, nil, 200)
eq(1,eval("g:val")) eq(1,eval("g:val"))
@ -32,14 +32,14 @@ describe('timers', function()
it('works with repeat two', function() it('works with repeat two', function()
execute("call timer_start(50, 'MyHandler', {'repeat': 2})") command("call timer_start(50, 'MyHandler', {'repeat': 2})")
eq(0,eval("g:val")) eq(0,eval("g:val"))
run(nil, nil, nil, 300) run(nil, nil, nil, 300)
eq(2,eval("g:val")) eq(2,eval("g:val"))
end) end)
it('are triggered during sleep', function() it('are triggered during sleep', function()
execute("call timer_start(50, 'MyHandler', {'repeat': 2})") command("call timer_start(50, 'MyHandler', {'repeat': 2})")
nvim_async("command", "sleep 10") nvim_async("command", "sleep 10")
eq(0,eval("g:val")) eq(0,eval("g:val"))
run(nil, nil, nil, 300) run(nil, nil, nil, 300)
@ -63,12 +63,12 @@ describe('timers', function()
end) end)
it('are paused when event processing is disabled', function() it('are paused when event processing is disabled', function()
execute("call timer_start(50, 'MyHandler', {'repeat': -1})") command("call timer_start(50, 'MyHandler', {'repeat': -1})")
run(nil, nil, nil, 100) run(nil, nil, nil, 100)
local count = eval("g:val") local count = eval("g:val")
-- shows two line error message and thus invokes the return prompt. -- shows two line error message and thus invokes the return prompt.
-- if we start to allow event processing here, we need to change this test. -- if we start to allow event processing here, we need to change this test.
execute("throw 'fatal error'") feed(':throw "fatal error"<CR>')
run(nil, nil, nil, 300) run(nil, nil, nil, 300)
feed("<cr>") feed("<cr>")
local diff = eval("g:val") - count local diff = eval("g:val") - count
@ -76,7 +76,7 @@ describe('timers', function()
end) end)
it('are triggered in blocking getchar() call', function() it('are triggered in blocking getchar() call', function()
execute("call timer_start(50, 'MyHandler', {'repeat': -1})") command("call timer_start(50, 'MyHandler', {'repeat': -1})")
nvim_async("command", "let g:c = getchar()") nvim_async("command", "let g:c = getchar()")
run(nil, nil, nil, 300) run(nil, nil, nil, 300)
feed("c") feed("c")
@ -157,7 +157,7 @@ describe('timers', function()
endif endif
endfunc endfunc
]]) ]])
execute("call timer_start(50, 'MyHandler', {'repeat': -1})") command("call timer_start(50, 'MyHandler', {'repeat': -1})")
eq(0,eval("g:val")) eq(0,eval("g:val"))
run(nil, nil, nil, 300) run(nil, nil, nil, 300)
eq(3,eval("g:val")) eq(3,eval("g:val"))
@ -170,8 +170,8 @@ describe('timers', function()
let g:val2 += 1 let g:val2 += 1
endfunc endfunc
]]) ]])
execute("call timer_start(50, 'MyHandler', {'repeat': 3})") command("call timer_start(50, 'MyHandler', {'repeat': 3})")
execute("call timer_start(100, 'MyHandler2', {'repeat': 2})") command("call timer_start(100, 'MyHandler2', {'repeat': 2})")
run(nil, nil, nil, 300) run(nil, nil, nil, 300)
eq(3,eval("g:val")) eq(3,eval("g:val"))
eq(2,eval("g:val2")) eq(2,eval("g:val2"))
@ -186,7 +186,7 @@ describe('timers', function()
let g:val += 1 let g:val += 1
endfunc endfunc
]]) ]])
execute("call timer_start(5, 'MyHandler', {'repeat': 1})") command("call timer_start(5, 'MyHandler', {'repeat': 1})")
run(nil, nil, nil, 300) run(nil, nil, nil, 300)
eq(1,eval("g:val")) eq(1,eval("g:val"))
end) end)
@ -201,7 +201,7 @@ describe('timers', function()
echo "evil" echo "evil"
endfunc endfunc
]]) ]])
execute("call timer_start(100, 'MyHandler', {'repeat': 1})") command("call timer_start(100, 'MyHandler', {'repeat': 1})")
feed(":good") feed(":good")
screen:sleep(200) screen:sleep(200)
screen:expect([[ screen:expect([[

View File

@ -1,5 +1,5 @@
local helpers = require("test.functional.helpers")(after_each) local helpers = require("test.functional.helpers")(after_each)
local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs local eq, command, funcs = helpers.eq, helpers.command, helpers.funcs
local ok = helpers.ok local ok = helpers.ok
local clear = helpers.clear local clear = helpers.clear
@ -9,15 +9,15 @@ describe(":argument", function()
end) end)
it("does not restart :terminal buffer", function() it("does not restart :terminal buffer", function()
execute("terminal") command("terminal")
helpers.feed([[<C-\><C-N>]]) helpers.feed([[<C-\><C-N>]])
execute("argadd") command("argadd")
helpers.feed([[<C-\><C-N>]]) helpers.feed([[<C-\><C-N>]])
local bufname_before = funcs.bufname("%") local bufname_before = funcs.bufname("%")
local bufnr_before = funcs.bufnr("%") local bufnr_before = funcs.bufnr("%")
helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity
execute("argument 1") command("argument 1")
helpers.feed([[<C-\><C-N>]]) helpers.feed([[<C-\><C-N>]])
local bufname_after = funcs.bufname("%") local bufname_after = funcs.bufname("%")

View File

@ -1,7 +1,7 @@
-- Specs for bang/filter commands -- Specs for bang/filter commands
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local feed, execute, clear = helpers.feed, helpers.execute, helpers.clear local feed, command, clear = helpers.feed, helpers.command, helpers.clear
local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir
if helpers.pending_win32(pending) then return end if helpers.pending_win32(pending) then return end
@ -28,7 +28,7 @@ describe('issues', function()
end) end)
it('#3269 Last line of shell output is not truncated', function() it('#3269 Last line of shell output is not truncated', function()
execute([[nnoremap <silent>\l :!ls bang_filter_spec<cr>]]) command([[nnoremap <silent>\l :!ls bang_filter_spec<cr>]])
feed([[\l]]) feed([[\l]])
screen:expect([[ screen:expect([[
~ | ~ |

View File

@ -6,7 +6,7 @@ local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq local eq = helpers.eq
local call = helpers.call local call = helpers.call
local clear = helpers.clear local clear = helpers.clear
local execute = helpers.execute local command = helpers.command
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
if helpers.pending_win32(pending) then return end if helpers.pending_win32(pending) then return end
@ -58,7 +58,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(0, lwd(globalwin)) eq(0, lwd(globalwin))
eq(0, lwd(globalwin, tabnr)) eq(0, lwd(globalwin, tabnr))
execute('bot split') command('bot split')
local localwin = call('winnr') local localwin = call('winnr')
-- Initial window is still using globalDir -- Initial window is still using globalDir
eq(globalDir, cwd(localwin)) eq(globalDir, cwd(localwin))
@ -66,7 +66,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(0, lwd(globalwin)) eq(0, lwd(globalwin))
eq(0, lwd(globalwin, tabnr)) eq(0, lwd(globalwin, tabnr))
execute('silent l' .. cmd .. ' ' .. directories.window) command('silent l' .. cmd .. ' ' .. directories.window)
-- From window with local dir, the original window -- From window with local dir, the original window
-- is still reporting the global dir -- is still reporting the global dir
eq(globalDir, cwd(globalwin)) eq(globalDir, cwd(globalwin))
@ -80,7 +80,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(1, lwd(localwin)) eq(1, lwd(localwin))
eq(1, lwd(localwin, tabnr)) eq(1, lwd(localwin, tabnr))
execute('tabnew') command('tabnew')
-- From new tab page, original window reports global dir -- From new tab page, original window reports global dir
eq(globalDir, cwd(globalwin, tabnr)) eq(globalDir, cwd(globalwin, tabnr))
eq(0, lwd(globalwin, tabnr)) eq(0, lwd(globalwin, tabnr))
@ -100,8 +100,8 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(0, lwd(-1, 0)) eq(0, lwd(-1, 0))
eq(0, lwd(-1, globaltab)) eq(0, lwd(-1, globaltab))
execute('tabnew') command('tabnew')
execute('silent t' .. cmd .. ' ' .. directories.tab) command('silent t' .. cmd .. ' ' .. directories.tab)
local localtab = call('tabpagenr') local localtab = call('tabpagenr')
-- From local tab page, original tab reports globalDir -- From local tab page, original tab reports globalDir
@ -114,7 +114,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(1, lwd(-1, 0)) eq(1, lwd(-1, 0))
eq(1, lwd(-1, localtab)) eq(1, lwd(-1, localtab))
execute('tabnext') command('tabnext')
-- From original tab page, local reports as such -- From original tab page, local reports as such
eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab))
eq(1, lwd(-1, localtab)) eq(1, lwd(-1, localtab))
@ -128,13 +128,13 @@ for _, cmd in ipairs {'cd', 'chdir'} do
end) end)
it('works with tab-local pwd', function() it('works with tab-local pwd', function()
execute('silent t' .. cmd .. ' ' .. directories.tab) command('silent t' .. cmd .. ' ' .. directories.tab)
eq(directories.start, cwd(-1, -1)) eq(directories.start, cwd(-1, -1))
eq(0, lwd(-1, -1)) eq(0, lwd(-1, -1))
end) end)
it('works with window-local pwd', function() it('works with window-local pwd', function()
execute('silent l' .. cmd .. ' ' .. directories.window) command('silent l' .. cmd .. ' ' .. directories.window)
eq(directories.start, cwd(-1, -1)) eq(directories.start, cwd(-1, -1))
eq(0, lwd(-1, -1)) eq(0, lwd(-1, -1))
end) end)
@ -145,18 +145,18 @@ for _, cmd in ipairs {'cd', 'chdir'} do
local globalDir = directories.start local globalDir = directories.start
-- Create a new tab and change directory -- Create a new tab and change directory
execute('tabnew') command('tabnew')
execute('silent t' .. cmd .. ' ' .. directories.tab) command('silent t' .. cmd .. ' ' .. directories.tab)
eq(globalDir .. '/' .. directories.tab, tcwd()) eq(globalDir .. '/' .. directories.tab, tcwd())
-- Create a new tab and verify it has inherited the directory -- Create a new tab and verify it has inherited the directory
execute('tabnew') command('tabnew')
eq(globalDir .. '/' .. directories.tab, tcwd()) eq(globalDir .. '/' .. directories.tab, tcwd())
-- Change tab and change back, verify that directories are correct -- Change tab and change back, verify that directories are correct
execute('tabnext') command('tabnext')
eq(globalDir, tcwd()) eq(globalDir, tcwd())
execute('tabprevious') command('tabprevious')
eq(globalDir .. '/' .. directories.tab, tcwd()) eq(globalDir .. '/' .. directories.tab, tcwd())
end) end)
end) end)
@ -164,7 +164,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do
it('works', function() it('works', function()
local globalDir = directories.start local globalDir = directories.start
-- Create a new tab first and verify that is has the same working dir -- Create a new tab first and verify that is has the same working dir
execute('tabnew') command('tabnew')
eq(globalDir, cwd()) eq(globalDir, cwd())
eq(globalDir, tcwd()) -- has no tab-local directory eq(globalDir, tcwd()) -- has no tab-local directory
eq(0, tlwd()) eq(0, tlwd())
@ -172,7 +172,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(0, wlwd()) eq(0, wlwd())
-- Change tab-local working directory and verify it is different -- Change tab-local working directory and verify it is different
execute('silent t' .. cmd .. ' ' .. directories.tab) command('silent t' .. cmd .. ' ' .. directories.tab)
eq(globalDir .. '/' .. directories.tab, cwd()) eq(globalDir .. '/' .. directories.tab, cwd())
eq(cwd(), tcwd()) -- working directory maches tab directory eq(cwd(), tcwd()) -- working directory maches tab directory
eq(1, tlwd()) eq(1, tlwd())
@ -180,46 +180,46 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(0, wlwd()) eq(0, wlwd())
-- Create a new window in this tab to test `:lcd` -- Create a new window in this tab to test `:lcd`
execute('new') command('new')
eq(1, tlwd()) -- Still tab-local working directory eq(1, tlwd()) -- Still tab-local working directory
eq(0, wlwd()) -- Still no window-local working directory eq(0, wlwd()) -- Still no window-local working directory
eq(globalDir .. '/' .. directories.tab, cwd()) eq(globalDir .. '/' .. directories.tab, cwd())
execute('silent l' .. cmd .. ' ../' .. directories.window) command('silent l' .. cmd .. ' ../' .. directories.window)
eq(globalDir .. '/' .. directories.window, cwd()) eq(globalDir .. '/' .. directories.window, cwd())
eq(globalDir .. '/' .. directories.tab, tcwd()) eq(globalDir .. '/' .. directories.tab, tcwd())
eq(1, wlwd()) eq(1, wlwd())
-- Verify the first window still has the tab local directory -- Verify the first window still has the tab local directory
execute('wincmd w') command('wincmd w')
eq(globalDir .. '/' .. directories.tab, cwd()) eq(globalDir .. '/' .. directories.tab, cwd())
eq(globalDir .. '/' .. directories.tab, tcwd()) eq(globalDir .. '/' .. directories.tab, tcwd())
eq(0, wlwd()) -- No window-local directory eq(0, wlwd()) -- No window-local directory
-- Change back to initial tab and verify working directory has stayed -- Change back to initial tab and verify working directory has stayed
execute('tabnext') command('tabnext')
eq(globalDir, cwd() ) eq(globalDir, cwd() )
eq(0, tlwd()) eq(0, tlwd())
eq(0, wlwd()) eq(0, wlwd())
-- Verify global changes don't affect local ones -- Verify global changes don't affect local ones
execute('silent ' .. cmd .. ' ' .. directories.global) command('silent ' .. cmd .. ' ' .. directories.global)
eq(globalDir .. '/' .. directories.global, cwd()) eq(globalDir .. '/' .. directories.global, cwd())
execute('tabnext') command('tabnext')
eq(globalDir .. '/' .. directories.tab, cwd()) eq(globalDir .. '/' .. directories.tab, cwd())
eq(globalDir .. '/' .. directories.tab, tcwd()) eq(globalDir .. '/' .. directories.tab, tcwd())
eq(0, wlwd()) -- Still no window-local directory in this window eq(0, wlwd()) -- Still no window-local directory in this window
-- Unless the global change happened in a tab with local directory -- Unless the global change happened in a tab with local directory
execute('silent ' .. cmd .. ' ..') command('silent ' .. cmd .. ' ..')
eq(globalDir, cwd() ) eq(globalDir, cwd() )
eq(0 , tlwd()) eq(0 , tlwd())
eq(0 , wlwd()) eq(0 , wlwd())
-- Which also affects the first tab -- Which also affects the first tab
execute('tabnext') command('tabnext')
eq(globalDir, cwd()) eq(globalDir, cwd())
-- But not in a window with its own local directory -- But not in a window with its own local directory
execute('tabnext | wincmd w') command('tabnext | wincmd w')
eq(globalDir .. '/' .. directories.window, cwd() ) eq(globalDir .. '/' .. directories.window, cwd() )
eq(0 , tlwd()) eq(0 , tlwd())
eq(globalDir .. '/' .. directories.window, wcwd()) eq(globalDir .. '/' .. directories.window, wcwd())
@ -280,8 +280,8 @@ describe("getcwd()", function ()
end) end)
it("returns empty string if working directory does not exist", function() it("returns empty string if working directory does not exist", function()
execute("cd "..directories.global) command("cd "..directories.global)
execute("call delete('../"..directories.global.."', 'd')") command("call delete('../"..directories.global.."', 'd')")
eq("", helpers.eval("getcwd()")) eq("", helpers.eval("getcwd()"))
end) end)
end) end)

View File

@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local clear, feed, source = helpers.clear, helpers.feed, helpers.source local clear, feed, source = helpers.clear, helpers.feed, helpers.source
local execute = helpers.execute local command = helpers.command
describe("CTRL-C (mapped)", function() describe("CTRL-C (mapped)", function()
before_each(function() before_each(function()
@ -20,7 +20,7 @@ describe("CTRL-C (mapped)", function()
nnoremap <C-C> <NOP> nnoremap <C-C> <NOP>
]]) ]])
execute("silent edit! test/functional/fixtures/bigfile.txt") command("silent edit! test/functional/fixtures/bigfile.txt")
local screen = Screen.new(52, 6) local screen = Screen.new(52, 6)
screen:attach() screen:attach()
screen:set_default_attr_ids({ screen:set_default_attr_ids({
@ -47,7 +47,7 @@ describe("CTRL-C (mapped)", function()
end end
-- The test is time-sensitive. Try different sleep values. -- The test is time-sensitive. Try different sleep values.
local ms_values = {1, 10, 100} local ms_values = {1, 10, 100, 1000, 10000}
for i, ms in ipairs(ms_values) do for i, ms in ipairs(ms_values) do
if i < #ms_values then if i < #ms_values then
local status, _ = pcall(test_ctrl_c, ms) local status, _ = pcall(test_ctrl_c, ms)

View File

@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute local clear, feed, feed_command = helpers.clear, helpers.feed, helpers.feed_command
describe(":drop", function() describe(":drop", function()
local screen local screen
@ -15,7 +15,7 @@ describe(":drop", function()
[2] = {reverse = true}, [2] = {reverse = true},
[3] = {bold = true}, [3] = {bold = true},
}) })
execute("set laststatus=2") feed_command("set laststatus=2")
end) end)
after_each(function() after_each(function()
@ -23,7 +23,7 @@ describe(":drop", function()
end) end)
it("works like :e when called with only one window open", function() it("works like :e when called with only one window open", function()
execute("drop tmp1.vim") feed_command("drop tmp1.vim")
screen:expect([[ screen:expect([[
^ | ^ |
{0:~ }| {0:~ }|
@ -39,10 +39,10 @@ describe(":drop", function()
end) end)
it("switches to an open window showing the buffer", function() it("switches to an open window showing the buffer", function()
execute("edit tmp1") feed_command("edit tmp1")
execute("vsplit") feed_command("vsplit")
execute("edit tmp2") feed_command("edit tmp2")
execute("drop tmp1") feed_command("drop tmp1")
screen:expect([[ screen:expect([[
{2:|}^ | {2:|}^ |
{0:~ }{2:|}{0:~ }| {0:~ }{2:|}{0:~ }|
@ -58,11 +58,11 @@ describe(":drop", function()
end) end)
it("splits off a new window when a buffer can't be abandoned", function() it("splits off a new window when a buffer can't be abandoned", function()
execute("edit tmp1") feed_command("edit tmp1")
execute("vsplit") feed_command("vsplit")
execute("edit tmp2") feed_command("edit tmp2")
feed("iABC<esc>") feed("iABC<esc>")
execute("drop tmp3") feed_command("drop tmp3")
screen:expect([[ screen:expect([[
^ {2:|} | ^ {2:|} |
{0:~ }{2:|}{0:~ }| {0:~ }{2:|}{0:~ }|

View File

@ -1,7 +1,8 @@
local helpers = require("test.functional.helpers")(after_each) local helpers = require("test.functional.helpers")(after_each)
local eq, execute, funcs = helpers.eq, helpers.execute, helpers.funcs local eq, command, funcs = helpers.eq, helpers.command, helpers.funcs
local ok = helpers.ok local ok = helpers.ok
local clear = helpers.clear local clear = helpers.clear
local feed = helpers.feed
describe(":edit", function() describe(":edit", function()
before_each(function() before_each(function()
@ -9,13 +10,13 @@ describe(":edit", function()
end) end)
it("without arguments does not restart :terminal buffer", function() it("without arguments does not restart :terminal buffer", function()
execute("terminal") command("terminal")
helpers.feed([[<C-\><C-N>]]) feed([[<C-\><C-N>]])
local bufname_before = funcs.bufname("%") local bufname_before = funcs.bufname("%")
local bufnr_before = funcs.bufnr("%") local bufnr_before = funcs.bufnr("%")
helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity helpers.ok(nil ~= string.find(bufname_before, "^term://")) -- sanity
execute("edit") command("edit")
local bufname_after = funcs.bufname("%") local bufname_after = funcs.bufname("%")
local bufnr_after = funcs.bufnr("%") local bufnr_after = funcs.bufnr("%")

View File

@ -1,5 +1,5 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, execute, feed = helpers.clear, helpers.execute, helpers.feed local clear, feed_command, feed = helpers.clear, helpers.feed_command, helpers.feed
local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval
describe('&encoding', function() describe('&encoding', function()
@ -12,7 +12,7 @@ describe('&encoding', function()
end) end)
it('cannot be changed after setup', function() it('cannot be changed after setup', function()
execute('set encoding=latin1') feed_command('set encoding=latin1')
-- error message expected -- error message expected
feed('<cr>') feed('<cr>')
neq(nil, string.find(eval('v:errmsg'), '^E474:')) neq(nil, string.find(eval('v:errmsg'), '^E474:'))
@ -31,7 +31,7 @@ describe('&encoding', function()
end) end)
it('can be set to utf-8 without error', function() it('can be set to utf-8 without error', function()
execute('set encoding=utf-8') feed_command('set encoding=utf-8')
eq("", eval('v:errmsg')) eq("", eval('v:errmsg'))
clear('--cmd', 'set enc=utf-8') clear('--cmd', 'set enc=utf-8')

View File

@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, execute, feed, ok, eval = local clear, feed_command, feed, ok, eval =
helpers.clear, helpers.execute, helpers.feed, helpers.ok, helpers.eval helpers.clear, helpers.feed_command, helpers.feed, helpers.ok, helpers.eval
describe(':grep', function() describe(':grep', function()
before_each(clear) before_each(clear)
@ -11,10 +11,10 @@ describe(':grep', function()
return return
end end
execute([[set grepprg=grep\ -r]]) feed_command([[set grepprg=grep\ -r]])
-- Change to test directory so that the test does not run too long. -- Change to test directory so that the test does not run too long.
execute('cd test') feed_command('cd test')
execute('grep a **/*') feed_command('grep a **/*')
feed('<cr>') -- Press ENTER feed('<cr>') -- Press ENTER
ok(eval('len(getqflist())') > 9000) -- IT'S OVER 9000!!1 ok(eval('len(getqflist())') > 9000) -- IT'S OVER 9000!!1
end) end)

View File

@ -1,23 +1,23 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim local clear, command, nvim = helpers.clear, helpers.command, helpers.nvim
local expect, feed, command = helpers.expect, helpers.feed, helpers.command local expect, feed = helpers.expect, helpers.feed
local eq, eval = helpers.eq, helpers.eval local eq, eval = helpers.eq, helpers.eval
describe(':emenu', function() describe(':emenu', function()
before_each(function() before_each(function()
clear() clear()
execute('nnoremenu Test.Test inormal<ESC>') command('nnoremenu Test.Test inormal<ESC>')
execute('inoremenu Test.Test insert') command('inoremenu Test.Test insert')
execute('vnoremenu Test.Test x') command('vnoremenu Test.Test x')
execute('cnoremenu Test.Test cmdmode') command('cnoremenu Test.Test cmdmode')
execute('nnoremenu Edit.Paste p') command('nnoremenu Edit.Paste p')
execute('cnoremenu Edit.Paste <C-R>"') command('cnoremenu Edit.Paste <C-R>"')
end) end)
it('executes correct bindings in normal mode without using API', function() it('executes correct bindings in normal mode without using API', function()
execute('emenu Test.Test') command('emenu Test.Test')
expect('normal') expect('normal')
end) end)

View File

@ -1,7 +1,7 @@
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local buf, eq, execute = helpers.curbufmeths, helpers.eq, helpers.execute local buf, eq, feed_command = helpers.curbufmeths, helpers.eq, helpers.feed_command
local feed, nvim_prog, wait = helpers.feed, helpers.nvim_prog, helpers.wait local feed, nvim_prog, wait = helpers.feed, helpers.nvim_prog, helpers.wait
local ok, set_session, spawn = helpers.ok, helpers.set_session, helpers.spawn local ok, set_session, spawn = helpers.ok, helpers.set_session, helpers.spawn
@ -27,12 +27,12 @@ describe(':oldfiles', function()
it('shows most recently used files', function() it('shows most recently used files', function()
local screen = Screen.new(100, 5) local screen = Screen.new(100, 5)
screen:attach() screen:attach()
execute('edit testfile1') feed_command('edit testfile1')
execute('edit testfile2') feed_command('edit testfile2')
execute('wshada ' .. shada_file) feed_command('wshada ' .. shada_file)
execute('rshada! ' .. shada_file) feed_command('rshada! ' .. shada_file)
local oldfiles = helpers.meths.get_vvar('oldfiles') local oldfiles = helpers.meths.get_vvar('oldfiles')
execute('oldfiles') feed_command('oldfiles')
screen:expect([[ screen:expect([[
testfile2 | testfile2 |
1: ]].. add_padding(oldfiles[1]) ..[[ | 1: ]].. add_padding(oldfiles[1]) ..[[ |
@ -50,14 +50,14 @@ describe(':browse oldfiles', function()
before_each(function() before_each(function()
_clear() _clear()
execute('edit testfile1') feed_command('edit testfile1')
filename = buf.get_name() filename = buf.get_name()
execute('edit testfile2') feed_command('edit testfile2')
filename2 = buf.get_name() filename2 = buf.get_name()
execute('wshada ' .. shada_file) feed_command('wshada ' .. shada_file)
wait() wait()
_clear() _clear()
execute('rshada! ' .. shada_file) feed_command('rshada! ' .. shada_file)
-- Ensure nvim is out of "Press ENTER..." prompt. -- Ensure nvim is out of "Press ENTER..." prompt.
feed('<cr>') feed('<cr>')
@ -70,7 +70,7 @@ describe(':browse oldfiles', function()
ok(filename == oldfiles[1] or filename == oldfiles[2]) ok(filename == oldfiles[1] or filename == oldfiles[2])
ok(filename2 == oldfiles[1] or filename2 == oldfiles[2]) ok(filename2 == oldfiles[1] or filename2 == oldfiles[2])
execute('browse oldfiles') feed_command('browse oldfiles')
end) end)
after_each(function() after_each(function()

View File

@ -2,8 +2,8 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local lfs = require('lfs') local lfs = require('lfs')
local execute, eq, clear, eval, feed, expect, source = local feed_command, eq, clear, eval, feed, expect, source =
helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.feed_command, helpers.eq, helpers.clear, helpers.eval, helpers.feed,
helpers.expect, helpers.source helpers.expect, helpers.source
if helpers.pending_win32(pending) then return end if helpers.pending_win32(pending) then return end
@ -13,7 +13,7 @@ describe(':recover', function()
it('fails if given a non-existent swapfile', function() it('fails if given a non-existent swapfile', function()
local swapname = 'bogus-swapfile' local swapname = 'bogus-swapfile'
execute('recover '..swapname) -- This should not segfault. #2117 feed_command('recover '..swapname) -- This should not segfault. #2117
eq('E305: No swap file found for '..swapname, eval('v:errmsg')) eq('E305: No swap file found for '..swapname, eval('v:errmsg'))
end) end)
@ -40,12 +40,12 @@ describe(':preserve', function()
]] ]]
source(init) source(init)
execute('set swapfile fileformat=unix undolevels=-1') feed_command('set swapfile fileformat=unix undolevels=-1')
-- Put swapdir at the start of the 'directory' list. #1836 -- Put swapdir at the start of the 'directory' list. #1836
execute('set directory^='..swapdir..'//') feed_command('set directory^='..swapdir..'//')
execute('edit '..testfile) feed_command('edit '..testfile)
feed('isometext<esc>') feed('isometext<esc>')
execute('preserve') feed_command('preserve')
source('redir => g:swapname | swapname | redir END') source('redir => g:swapname | swapname | redir END')
local swappath1 = eval('g:swapname') local swappath1 = eval('g:swapname')
@ -59,8 +59,8 @@ describe(':preserve', function()
source(init) source(init)
-- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog.
execute('autocmd SwapExists * let v:swapchoice = "r"') feed_command('autocmd SwapExists * let v:swapchoice = "r"')
execute('silent edit '..testfile) feed_command('silent edit '..testfile)
source('redir => g:swapname | swapname | redir END') source('redir => g:swapname | swapname | redir END')
local swappath2 = eval('g:swapname') local swappath2 = eval('g:swapname')

View File

@ -0,0 +1,17 @@
local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local clear = helpers.clear
local exc_exec = helpers.exc_exec
describe(':syntax', function()
before_each(clear)
describe('keyword', function()
it('does not crash when group name contains unprintable characters',
function()
eq('Vim(syntax):E669: Unprintable character in group name',
exc_exec('syntax keyword \024 foo bar'))
end)
end)
end)

View File

@ -5,7 +5,7 @@ local clear = helpers.clear
local insert = helpers.insert local insert = helpers.insert
local feed = helpers.feed local feed = helpers.feed
local expect = helpers.expect local expect = helpers.expect
local execute = helpers.execute local feed_command = helpers.feed_command
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
describe(':undojoin command', function() describe(':undojoin command', function()
@ -14,10 +14,10 @@ describe(':undojoin command', function()
insert([[ insert([[
Line of text 1 Line of text 1
Line of text 2]]) Line of text 2]])
execute('goto 1') feed_command('goto 1')
end) end)
it('joins changes in a buffer', function() it('joins changes in a buffer', function()
execute('undojoin | delete') feed_command('undojoin | delete')
expect([[ expect([[
Line of text 2]]) Line of text 2]])
feed('u') feed('u')
@ -26,7 +26,7 @@ describe(':undojoin command', function()
end) end)
it('does not corrupt undolist when connected with redo', function() it('does not corrupt undolist when connected with redo', function()
feed('ixx<esc>') feed('ixx<esc>')
execute('undojoin | redo') feed_command('undojoin | redo')
expect([[ expect([[
xxLine of text 1 xxLine of text 1
Line of text 2]]) Line of text 2]])

View File

@ -1,11 +1,12 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local lfs = require('lfs') local lfs = require('lfs')
local eq, eval, clear, write_file, execute, source, insert = local eq, eval, clear, write_file, source, insert =
helpers.eq, helpers.eval, helpers.clear, helpers.write_file, helpers.eq, helpers.eval, helpers.clear, helpers.write_file,
helpers.execute, helpers.source, helpers.insert helpers.source, helpers.insert
local redir_exec = helpers.redir_exec local redir_exec = helpers.redir_exec
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local command = helpers.command local command = helpers.command
local feed_command = helpers.feed_command
local funcs = helpers.funcs local funcs = helpers.funcs
local meths = helpers.meths local meths = helpers.meths
@ -33,9 +34,9 @@ describe(':write', function()
end) end)
it('&backupcopy=auto preserves symlinks', function() it('&backupcopy=auto preserves symlinks', function()
execute('set backupcopy=auto') command('set backupcopy=auto')
write_file('test_bkc_file.txt', 'content0') write_file('test_bkc_file.txt', 'content0')
execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") command("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
source([[ source([[
edit test_bkc_link.txt edit test_bkc_link.txt
call setline(1, ['content1']) call setline(1, ['content1'])
@ -46,9 +47,9 @@ describe(':write', function()
end) end)
it('&backupcopy=no replaces symlink with new file', function() it('&backupcopy=no replaces symlink with new file', function()
execute('set backupcopy=no') command('set backupcopy=no')
write_file('test_bkc_file.txt', 'content0') write_file('test_bkc_file.txt', 'content0')
execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") command("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
source([[ source([[
edit test_bkc_link.txt edit test_bkc_link.txt
call setline(1, ['content1']) call setline(1, ['content1'])
@ -69,7 +70,7 @@ describe(':write', function()
insert(text) insert(text)
-- Blocks until a consumer reads the FIFO. -- Blocks until a consumer reads the FIFO.
execute("write >> test_fifo") feed_command("write >> test_fifo")
-- Read the FIFO, this will unblock the :write above. -- Read the FIFO, this will unblock the :write above.
local fifo = assert(io.open("test_fifo")) local fifo = assert(io.open("test_fifo"))

View File

@ -1,20 +1,21 @@
-- Specs for :wundo and underlying functions -- Specs for :wundo and underlying functions
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local execute, clear, eval, feed, spawn, nvim_prog, set_session = local command, clear, eval, spawn, nvim_prog, set_session =
helpers.execute, helpers.clear, helpers.eval, helpers.feed, helpers.spawn, helpers.command, helpers.clear, helpers.eval, helpers.spawn,
helpers.nvim_prog, helpers.set_session helpers.nvim_prog, helpers.set_session
describe(':wundo', function() describe(':wundo', function()
before_each(clear) before_each(clear)
after_each(function()
os.remove(eval('getcwd()') .. '/foo')
end)
it('safely fails on new, non-empty buffer', function() it('safely fails on new, non-empty buffer', function()
feed('iabc<esc>') command('normal! iabc')
execute('wundo foo') -- This should not segfault. #1027 command('wundo foo') -- This should not segfault. #1027
--TODO: check messages for error message --TODO: check messages for error message
os.remove(eval('getcwd()') .. '/foo') --cleanup
end) end)
end) end)
@ -23,7 +24,7 @@ describe('u_* functions', function()
local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed',
'-c', 'set undodir=. undofile'}) '-c', 'set undodir=. undofile'})
set_session(session) set_session(session)
execute('echo "True"') -- Should not error out due to crashed Neovim command('echo "True"') -- Should not error out due to crashed Neovim
session:close() session:close()
end) end)
end) end)

View File

@ -1,8 +1,8 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local lfs = require('lfs') local lfs = require('lfs')
local execute, eq, neq, spawn, nvim_prog, set_session, wait, write_file local command, eq, neq, spawn, nvim_prog, set_session, write_file =
= helpers.execute, helpers.eq, helpers.neq, helpers.spawn, helpers.command, helpers.eq, helpers.neq, helpers.spawn,
helpers.nvim_prog, helpers.set_session, helpers.wait, helpers.write_file helpers.nvim_prog, helpers.set_session, helpers.write_file
describe(':wshada', function() describe(':wshada', function()
local shada_file = 'wshada_test' local shada_file = 'wshada_test'
@ -24,8 +24,7 @@ describe(':wshada', function()
it('creates a shada file', function() it('creates a shada file', function()
-- file should _not_ exist -- file should _not_ exist
eq(nil, lfs.attributes(shada_file)) eq(nil, lfs.attributes(shada_file))
execute('wsh! '..shada_file) command('wsh! '..shada_file)
wait()
-- file _should_ exist -- file _should_ exist
neq(nil, lfs.attributes(shada_file)) neq(nil, lfs.attributes(shada_file))
end) end)
@ -40,8 +39,7 @@ describe(':wshada', function()
eq(text, io.open(shada_file):read()) eq(text, io.open(shada_file):read())
neq(nil, lfs.attributes(shada_file)) neq(nil, lfs.attributes(shada_file))
execute('wsh! '..shada_file) command('wsh! '..shada_file)
wait()
-- File should have been overwritten with a shada file. -- File should have been overwritten with a shada file.
local fp = io.open(shada_file, 'r') local fp = io.open(shada_file, 'r')

View File

@ -8,6 +8,7 @@ local Session = require('nvim.session')
local TcpStream = require('nvim.tcp_stream') local TcpStream = require('nvim.tcp_stream')
local SocketStream = require('nvim.socket_stream') local SocketStream = require('nvim.socket_stream')
local ChildProcessStream = require('nvim.child_process_stream') local ChildProcessStream = require('nvim.child_process_stream')
local Paths = require('test.config.paths')
local check_cores = global_helpers.check_cores local check_cores = global_helpers.check_cores
local check_logs = global_helpers.check_logs local check_logs = global_helpers.check_logs
@ -20,7 +21,11 @@ local dedent = global_helpers.dedent
local start_dir = lfs.currentdir() local start_dir = lfs.currentdir()
-- XXX: NVIM_PROG takes precedence, QuickBuild sets it. -- XXX: NVIM_PROG takes precedence, QuickBuild sets it.
local nvim_prog = os.getenv('NVIM_PROG') or os.getenv('NVIM_PRG') or 'build/bin/nvim' local nvim_prog = (
os.getenv('NVIM_PROG')
or os.getenv('NVIM_PRG')
or Paths.test_build_dir .. '/bin/nvim'
)
-- Default settings for the test session. -- Default settings for the test session.
local nvim_set = 'set shortmess+=I background=light noswapfile noautoindent' local nvim_set = 'set shortmess+=I background=light noswapfile noautoindent'
..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.'
@ -246,12 +251,13 @@ local function retry(max, max_ms, fn)
return result return result
end end
if (max and tries >= max) or (luv.now() - start_time > timeout) then if (max and tries >= max) or (luv.now() - start_time > timeout) then
break if type(result) == "string" then
result = "\nretry() attempts: "..tostring(tries).."\n"..result
end
error(result)
end end
tries = tries + 1 tries = tries + 1
end end
-- Do not use pcall() for the final attempt, let the failure bubble up.
return fn()
end end
local function clear(...) local function clear(...)
@ -304,7 +310,7 @@ end
-- Executes an ex-command by user input. Because nvim_input() is used, VimL -- Executes an ex-command by user input. Because nvim_input() is used, VimL
-- errors will not manifest as client (lua) errors. Use command() for that. -- errors will not manifest as client (lua) errors. Use command() for that.
local function execute(...) local function feed_command(...)
for _, v in ipairs({...}) do for _, v in ipairs({...}) do
if v:sub(1, 1) ~= '/' then if v:sub(1, 1) ~= '/' then
-- not a search command, prefix with colon -- not a search command, prefix with colon
@ -565,7 +571,7 @@ local module = {
insert = insert, insert = insert,
iswin = iswin, iswin = iswin,
feed = feed, feed = feed,
execute = execute, feed_command = feed_command,
eval = nvim_eval, eval = nvim_eval,
call = nvim_call, call = nvim_call,
command = nvim_command, command = nvim_command,

View File

@ -3,7 +3,7 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local execute, expect = helpers.execute, helpers.expect local feed_command, expect = helpers.feed_command, helpers.expect
describe('filename recognition', function() describe('filename recognition', function()
setup(clear) setup(clear)
@ -17,17 +17,17 @@ describe('filename recognition', function()
fourth test for URL:\\machine.name\tmp\vimtest2d, and other text]]) fourth test for URL:\\machine.name\tmp\vimtest2d, and other text]])
-- Go to the first URL and append it to the beginning -- Go to the first URL and append it to the beginning
execute('/^first', '/tmp', 'call append(0, expand("<cfile>"))') feed_command('/^first', '/tmp', 'call append(0, expand("<cfile>"))')
-- Repeat for the second URL -- Repeat for the second URL
-- this time, navigate to the word "URL" instead of "tmp" -- this time, navigate to the word "URL" instead of "tmp"
execute('/^second', '/URL', 'call append(1, expand("<cfile>"))') feed_command('/^second', '/URL', 'call append(1, expand("<cfile>"))')
-- Repeat for the remaining URLs. This time, the 'isfname' option must be -- Repeat for the remaining URLs. This time, the 'isfname' option must be
-- set to allow '\' in filenames -- set to allow '\' in filenames
execute('set isf=@,48-57,/,.,-,_,+,,,$,:,~,\\') feed_command('set isf=@,48-57,/,.,-,_,+,,,$,:,~,\\')
execute('/^third', '/name', 'call append(2, expand("<cfile>"))') feed_command('/^third', '/name', 'call append(2, expand("<cfile>"))')
execute('/^fourth', '/URL', 'call append(3, expand("<cfile>"))') feed_command('/^fourth', '/URL', 'call append(3, expand("<cfile>"))')
-- Delete the initial text, which now starts at line 5 -- Delete the initial text, which now starts at line 5
feed('5GdG') feed('5GdG')

View File

@ -5,14 +5,14 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local feed, insert = helpers.feed, helpers.insert local feed, insert = helpers.feed, helpers.insert
local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect
-- Inserts text as usual, and additionally positions the cursor on line 1 and -- Inserts text as usual, and additionally positions the cursor on line 1 and
-- sets 'cindent' and tab settings. (In the original "test3.in" the modeline at -- sets 'cindent' and tab settings. (In the original "test3.in" the modeline at
-- the top of the file takes care of this.) -- the top of the file takes care of this.)
local function insert_(content) local function insert_(content)
insert(content) insert(content)
execute('1', 'set cin ts=4 sw=4') feed_command('1', 'set cin ts=4 sw=4')
end end
describe('cindent', function() describe('cindent', function()
@ -978,7 +978,7 @@ describe('cindent', function()
/* end of AUTO */ /* end of AUTO */
]=]) ]=])
execute('/start of AUTO') feed_command('/start of AUTO')
feed('=/end of AUTO<cr>') feed('=/end of AUTO<cr>')
expect([=[ expect([=[
@ -1954,8 +1954,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set tw=0 wm=60 columns=80 noai fo=croq') feed_command('set tw=0 wm=60 columns=80 noai fo=croq')
execute('/serious/e') feed_command('/serious/e')
feed('a about life, the universe, and the rest<esc>') feed('a about life, the universe, and the rest<esc>')
expect([=[ expect([=[
@ -1994,8 +1994,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set nocin') feed_command('set nocin')
execute('/comments') feed_command('/comments')
feed('joabout life<esc>/happens<cr>') feed('joabout life<esc>/happens<cr>')
feed('jothere<esc>/below<cr>') feed('jothere<esc>/below<cr>')
feed('oline<esc>/this<cr>') feed('oline<esc>/this<cr>')
@ -2034,8 +2034,8 @@ describe('cindent', function()
+ vec2[2] * vec[2]; + vec2[2] * vec[2];
} }
]=]) ]=])
execute('set cin') feed_command('set cin')
execute('/vec2') feed_command('/vec2')
feed('==<cr>') feed('==<cr>')
expect([=[ expect([=[
@ -2067,9 +2067,9 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cin') feed_command('set cin')
execute('set cino=}4') feed_command('set cino=}4')
execute('/testing1') feed_command('/testing1')
feed('k2==/testing2<cr>') feed('k2==/testing2<cr>')
feed('k2==<cr>') feed('k2==<cr>')
@ -2118,9 +2118,9 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cin') feed_command('set cin')
execute('set cino=(0,)20') feed_command('set cino=(0,)20')
execute('/main') feed_command('/main')
feed('=][<cr>') feed('=][<cr>')
expect([=[ expect([=[
@ -2164,9 +2164,9 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cin') feed_command('set cin')
execute('set cino=es,n0s') feed_command('set cino=es,n0s')
execute('/main') feed_command('/main')
feed('=][<cr>') feed('=][<cr>')
expect([=[ expect([=[
@ -2202,8 +2202,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cin') feed_command('set cin')
execute('set cino=') feed_command('set cino=')
feed(']]=][<cr>') feed(']]=][<cr>')
expect([=[ expect([=[
@ -2280,7 +2280,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino={s,e-s') feed_command('set cino={s,e-s')
feed(']]=][<cr>') feed(']]=][<cr>')
expect([=[ expect([=[
@ -2336,7 +2336,7 @@ describe('cindent', function()
/* foo */ /* foo */
]=]) ]=])
execute('set cino={s,fs') feed_command('set cino={s,fs')
feed(']]=/ foo<cr>') feed(']]=/ foo<cr>')
expect([=[ expect([=[
@ -2390,8 +2390,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=') feed_command('set cino=')
execute('/while') feed_command('/while')
feed('ohere<esc>') feed('ohere<esc>')
expect([=[ expect([=[
@ -2420,8 +2420,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino= com=') feed_command('set cino= com=')
execute('/comment') feed_command('/comment')
feed('olabel2: b();<cr>label3 /* post */:<cr>/* pre */ label4:<cr>f(/*com*/);<cr>if (/*com*/)<cr>cmd();<esc>') feed('olabel2: b();<cr>label3 /* post */:<cr>/* pre */ label4:<cr>f(/*com*/);<cr>if (/*com*/)<cr>cmd();<esc>')
expect([=[ expect([=[
@ -2453,8 +2453,8 @@ describe('cindent', function()
*/ */
]=]) ]=])
execute('set comments& comments^=s:/*,m:**,ex:*/') feed_command('set comments& comments^=s:/*,m:**,ex:*/')
execute('/simple') feed_command('/simple')
feed('=5j<cr>') feed('=5j<cr>')
expect([=[ expect([=[
@ -2482,8 +2482,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=c0') feed_command('set cino=c0')
execute('set comments& comments-=s1:/* comments^=s0:/*') feed_command('set comments& comments-=s1:/* comments^=s0:/*')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2511,8 +2511,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=c0,C1') feed_command('set cino=c0,C1')
execute('set comments& comments-=s1:/* comments^=s0:/*') feed_command('set comments& comments-=s1:/* comments^=s0:/*')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2540,7 +2540,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=') feed_command('set cino=')
feed(']]=][<cr>') feed(']]=][<cr>')
expect([=[ expect([=[
@ -2570,7 +2570,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(s') feed_command('set cino=(s')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2600,7 +2600,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(s,U1 ') feed_command('set cino=(s,U1 ')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2629,7 +2629,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(0') feed_command('set cino=(0')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2657,7 +2657,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(0,w1 ') feed_command('set cino=(0,w1 ')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2689,7 +2689,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(s') feed_command('set cino=(s')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2725,7 +2725,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(s,m1 ') feed_command('set cino=(s,m1 ')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2762,7 +2762,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=b1') feed_command('set cino=b1')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2801,7 +2801,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(0,W5') feed_command('set cino=(0,W5')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2834,7 +2834,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=/6') feed_command('set cino=/6')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2860,7 +2860,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=') feed_command('set cino=')
feed('2kdd]]/comment 1/+1<cr>') feed('2kdd]]/comment 1/+1<cr>')
feed('==<cr>') feed('==<cr>')
@ -2890,7 +2890,7 @@ describe('cindent', function()
}; };
]=]) ]=])
execute('set cino=g0') feed_command('set cino=g0')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2921,7 +2921,7 @@ describe('cindent', function()
}; };
]=]) ]=])
execute('set cino=(0,gs,hs') feed_command('set cino=(0,gs,hs')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -2951,7 +2951,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=+20') feed_command('set cino=+20')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -3009,7 +3009,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(0,W2s') feed_command('set cino=(0,W2s')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -3066,7 +3066,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=M1') feed_command('set cino=M1')
feed('2kdd]]=][<cr>') feed('2kdd]]=][<cr>')
expect([=[ expect([=[
@ -3095,7 +3095,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(0,ts') feed_command('set cino=(0,ts')
feed('2kdd2j=][<cr>') feed('2kdd2j=][<cr>')
expect([=[ expect([=[
@ -3127,7 +3127,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=(0') feed_command('set cino=(0')
feed('2kdd2j=][<cr>') feed('2kdd2j=][<cr>')
expect([=[ expect([=[
@ -3221,7 +3221,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino&') feed_command('set cino&')
feed('2kdd2j=7][<cr>') feed('2kdd2j=7][<cr>')
expect([=[ expect([=[
@ -3328,8 +3328,8 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino&') feed_command('set cino&')
execute('set cino+=l1') feed_command('set cino+=l1')
feed('2kdd2j=][<cr>') feed('2kdd2j=][<cr>')
expect([=[ expect([=[
@ -3373,7 +3373,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino&') feed_command('set cino&')
feed('2kdd2j=][<cr>') feed('2kdd2j=][<cr>')
expect([=[ expect([=[
@ -3399,7 +3399,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set com=s1:/*,m:*,ex:*/') feed_command('set com=s1:/*,m:*,ex:*/')
feed(']]3jofoo();<esc>') feed(']]3jofoo();<esc>')
expect([=[ expect([=[
@ -3429,7 +3429,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino&') feed_command('set cino&')
feed('2kdd2j=][<cr>') feed('2kdd2j=][<cr>')
expect([=[ expect([=[
@ -3477,7 +3477,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=k2s,(0') feed_command('set cino=k2s,(0')
feed('2kdd3j=][<cr>') feed('2kdd3j=][<cr>')
expect([=[ expect([=[
@ -3541,7 +3541,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=k2s,(s') feed_command('set cino=k2s,(s')
feed('2kdd3j=][<cr>') feed('2kdd3j=][<cr>')
expect([=[ expect([=[
@ -3606,7 +3606,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=k2s,(s,U1') feed_command('set cino=k2s,(s,U1')
feed('2kdd3j=][<cr>') feed('2kdd3j=][<cr>')
expect([=[ expect([=[
@ -3677,7 +3677,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=k2s,(0,W4') feed_command('set cino=k2s,(0,W4')
feed('2kdd3j=][<cr>') feed('2kdd3j=][<cr>')
expect([=[ expect([=[
@ -3742,7 +3742,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=k2s,u2') feed_command('set cino=k2s,u2')
feed('2kdd3j=][<cr>') feed('2kdd3j=][<cr>')
expect([=[ expect([=[
@ -3805,7 +3805,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=k2s,(0,w1') feed_command('set cino=k2s,(0,w1')
feed('2kdd3j=][<cr>') feed('2kdd3j=][<cr>')
expect([=[ expect([=[
@ -3864,7 +3864,7 @@ describe('cindent', function()
} }
]=]) ]=])
execute('set cino=k2,(s') feed_command('set cino=k2,(s')
feed('2kdd3j=][<cr>') feed('2kdd3j=][<cr>')
expect([=[ expect([=[
@ -3956,8 +3956,8 @@ describe('cindent', function()
NAMESPACEEND NAMESPACEEND
]=]) ]=])
execute('set cino=N-s') feed_command('set cino=N-s')
execute('/^NAMESPACESTART') feed_command('/^NAMESPACESTART')
feed('=/^NAMESPACEEND<cr>') feed('=/^NAMESPACEEND<cr>')
expect([=[ expect([=[
@ -4047,8 +4047,8 @@ describe('cindent', function()
JSEND JSEND
]=]) ]=])
execute('set cino=j1,J1') feed_command('set cino=j1,J1')
execute('/^JSSTART') feed_command('/^JSSTART')
feed('=/^JSEND<cr>') feed('=/^JSEND<cr>')
expect([=[ expect([=[
@ -4082,8 +4082,8 @@ describe('cindent', function()
JSEND JSEND
]=]) ]=])
execute('set cino=j1,J1') feed_command('set cino=j1,J1')
execute('/^JSSTART') feed_command('/^JSSTART')
feed('=/^JSEND<cr>') feed('=/^JSEND<cr>')
expect([=[ expect([=[
@ -4112,8 +4112,8 @@ describe('cindent', function()
JSEND JSEND
]=]) ]=])
execute('set cino=j1,J1') feed_command('set cino=j1,J1')
execute('/^JSSTART') feed_command('/^JSSTART')
feed('=/^JSEND<cr>') feed('=/^JSEND<cr>')
expect([=[ expect([=[
@ -4195,8 +4195,8 @@ describe('cindent', function()
JSEND JSEND
]=]) ]=])
execute('set cino=j1,J1') feed_command('set cino=j1,J1')
execute('/^JSSTART') feed_command('/^JSSTART')
feed('=/^JSEND<cr>') feed('=/^JSEND<cr>')
expect([=[ expect([=[
@ -4285,8 +4285,8 @@ describe('cindent', function()
JSEND JSEND
]=]) ]=])
execute('set cino=j1,J1') feed_command('set cino=j1,J1')
execute('/^JSSTART') feed_command('/^JSSTART')
feed('=/^JSEND<cr>') feed('=/^JSEND<cr>')
expect([=[ expect([=[
@ -4333,8 +4333,8 @@ describe('cindent', function()
JSEND JSEND
]=]) ]=])
execute('set cino=j1,J1') feed_command('set cino=j1,J1')
execute('/^JSSTART') feed_command('/^JSSTART')
feed('=/^JSEND<cr>') feed('=/^JSEND<cr>')
expect([=[ expect([=[
@ -4517,8 +4517,8 @@ describe('cindent', function()
]=]) ]=])
-- :set cino=j1,J1,+2 -- :set cino=j1,J1,+2
execute('set cino=j1,J1,+2') feed_command('set cino=j1,J1,+2')
execute('/^JSSTART') feed_command('/^JSSTART')
feed('=/^JSEND<cr>') feed('=/^JSEND<cr>')
expect([=[ expect([=[

View File

@ -1,10 +1,9 @@
-- vim: set foldmethod=marker foldmarker=[[,]] :
-- Test for autocommand that changes current buffer on BufEnter event. -- Test for autocommand that changes current buffer on BufEnter event.
-- Check if modelines are interpreted for the correct buffer. -- Check if modelines are interpreted for the correct buffer.
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local execute, expect = helpers.execute, helpers.expect local feed_command, expect = helpers.feed_command, helpers.expect
describe('BufEnter with modelines', function() describe('BufEnter with modelines', function()
setup(clear) setup(clear)
@ -20,34 +19,34 @@ describe('BufEnter with modelines', function()
this is a test this is a test
end of test file Xxx]]) end of test file Xxx]])
execute('au BufEnter Xxx brew') feed_command('au BufEnter Xxx brew')
-- Write test file Xxx -- Write test file Xxx
execute('/start of') feed_command('/start of')
execute('.,/end of/w! Xxx') feed_command('.,/end of/w! Xxx')
execute('set ai modeline modelines=3') feed_command('set ai modeline modelines=3')
-- Split to Xxx, autocmd will do :brew -- Split to Xxx, autocmd will do :brew
execute('sp Xxx') feed_command('sp Xxx')
-- Append text with autoindent to this file -- Append text with autoindent to this file
feed('G?this is a<CR>') feed('G?this is a<CR>')
feed('othis should be auto-indented<Esc>') feed('othis should be auto-indented<Esc>')
-- Go to Xxx, no autocmd anymore -- Go to Xxx, no autocmd anymore
execute('au! BufEnter Xxx') feed_command('au! BufEnter Xxx')
execute('buf Xxx') feed_command('buf Xxx')
-- Append text without autoindent to Xxx -- Append text without autoindent to Xxx
feed('G?this is a<CR>') feed('G?this is a<CR>')
feed('othis should be in column 1<Esc>') feed('othis should be in column 1<Esc>')
execute('wq') feed_command('wq')
-- Include Xxx in the current file -- Include Xxx in the current file
feed('G:r Xxx<CR>') feed('G:r Xxx<CR>')
-- Vim issue #57 do not move cursor on <c-o> when autoindent is set -- Vim issue #57 do not move cursor on <c-o> when autoindent is set
execute('set fo+=r') feed_command('set fo+=r')
feed('G') feed('G')
feed('o# abcdef<Esc>2hi<CR><c-o>d0<Esc>') feed('o# abcdef<Esc>2hi<CR><c-o>d0<Esc>')
feed('o# abcdef<Esc>2hi<c-o>d0<Esc>') feed('o# abcdef<Esc>2hi<c-o>d0<Esc>')

View File

@ -3,7 +3,8 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local execute, expect = helpers.execute, helpers.expect local command, expect = helpers.command, helpers.expect
local wait = helpers.wait
describe('test5', function() describe('test5', function()
setup(clear) setup(clear)
@ -18,35 +19,37 @@ describe('test5', function()
this is a test this is a test
end of test file Xxx]]) end of test file Xxx]])
execute('w! Xxx0') command('w! Xxx0')
execute('au BufLeave Xxx bwipe') command('au BufLeave Xxx bwipe')
execute('/start of') command('/start of')
-- Write test file Xxx. -- Write test file Xxx.
execute('.,/end of/w! Xxx') command('.,/end of/w! Xxx')
-- Split to Xxx. -- Split to Xxx.
execute('sp Xxx') command('sp Xxx')
-- Delete buffer Xxx, now we're back here. -- Delete buffer Xxx, now we're back here.
execute('bwipe') command('bwipe')
feed('G?this is a<cr>') feed('G?this is a<cr>')
feed('othis is some more text<esc>') feed('othis is some more text<esc>')
wait()
-- Append some text to this file. -- Append some text to this file.
-- Write current file contents. -- Write current file contents.
execute('?start?,$yank A') command('?start?,$yank A')
-- Delete current buffer, get an empty one. -- Delete current buffer, get an empty one.
execute('bwipe!') command('bwipe!')
-- Append an extra line to the output register. -- Append an extra line to the output register.
feed('ithis is another test line<esc>:yank A<cr>') feed('ithis is another test line<esc>:yank A<cr>')
wait()
-- Output results -- Output results
execute('%d') command('%d')
execute('0put a') command('0put a')
execute('$d') command('$d')
-- Assert buffer contents. -- Assert buffer contents.
expect([[ expect([[

Some files were not shown because too many files have changed in this diff Show More