Merge #4411 from ZyX-I/luaviml'/lua
This commit is contained in:
commit
0e873a30f3
|
@ -66,7 +66,9 @@ matrix:
|
|||
env: BUILD_32BIT=ON
|
||||
- os: linux
|
||||
compiler: clang-3.9
|
||||
env: CLANG_SANITIZER=ASAN_UBSAN
|
||||
env: >
|
||||
CLANG_SANITIZER=ASAN_UBSAN
|
||||
CMAKE_FLAGS="$CMAKE_FLAGS -DPREFER_LUAJIT=false"
|
||||
- os: linux
|
||||
compiler: clang-3.9
|
||||
env: CLANG_SANITIZER=TSAN
|
||||
|
|
|
@ -309,6 +309,14 @@ include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS})
|
|||
find_package(Msgpack 1.0.0 REQUIRED)
|
||||
include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS})
|
||||
|
||||
option(PREFER_LUAJIT "Prefer LuaJIT over Lua when compiling executable. Test library always uses luajit." ON)
|
||||
|
||||
find_package(LuaJit REQUIRED)
|
||||
|
||||
if(NOT PREFER_LUAJIT)
|
||||
find_package(Lua REQUIRED)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}")
|
||||
check_c_source_compiles("
|
||||
#include <msgpack.h>
|
||||
|
|
|
@ -10,7 +10,8 @@ build_deps() {
|
|||
if test "${BUILD_32BIT}" = ON ; then
|
||||
DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} ${CMAKE_FLAGS_32BIT}"
|
||||
fi
|
||||
if test "${FUNCTIONALTEST}" = "functionaltest-lua" ; then
|
||||
if test "${FUNCTIONALTEST}" = "functionaltest-lua" \
|
||||
|| test "${CLANG_SANITIZER}" = "ASAN_UBSAN" ; then
|
||||
DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} -DUSE_BUNDLED_LUA=ON"
|
||||
fi
|
||||
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#.rst:
|
||||
# FindLua
|
||||
# -------
|
||||
#
|
||||
#
|
||||
#
|
||||
# Locate Lua library This module defines
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# LUA_FOUND - if false, do not try to link to Lua
|
||||
# LUA_LIBRARIES - both lua and lualib
|
||||
# LUA_INCLUDE_DIR - where to find lua.h
|
||||
# LUA_VERSION_STRING - the version of Lua found
|
||||
# LUA_VERSION_MAJOR - the major version of Lua
|
||||
# LUA_VERSION_MINOR - the minor version of Lua
|
||||
# LUA_VERSION_PATCH - the patch version of Lua
|
||||
#
|
||||
#
|
||||
#
|
||||
# Note that the expected include convention is
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# #include "lua.h"
|
||||
#
|
||||
# and not
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# #include <lua/lua.h>
|
||||
#
|
||||
# This is because, the lua location is not standardized and may exist in
|
||||
# locations other than lua/
|
||||
|
||||
unset(_lua_include_subdirs)
|
||||
unset(_lua_library_names)
|
||||
unset(_lua_append_versions)
|
||||
|
||||
# this is a function only to have all the variables inside go away automatically
|
||||
function(_lua_set_version_vars)
|
||||
set(LUA_VERSIONS5 5.3 5.2 5.1 5.0)
|
||||
|
||||
if (Lua_FIND_VERSION_EXACT)
|
||||
if (Lua_FIND_VERSION_COUNT GREATER 1)
|
||||
set(_lua_append_versions ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR})
|
||||
endif ()
|
||||
elseif (Lua_FIND_VERSION)
|
||||
# once there is a different major version supported this should become a loop
|
||||
if (NOT Lua_FIND_VERSION_MAJOR GREATER 5)
|
||||
if (Lua_FIND_VERSION_COUNT EQUAL 1)
|
||||
set(_lua_append_versions ${LUA_VERSIONS5})
|
||||
else ()
|
||||
foreach (subver IN LISTS LUA_VERSIONS5)
|
||||
if (NOT subver VERSION_LESS ${Lua_FIND_VERSION})
|
||||
list(APPEND _lua_append_versions ${subver})
|
||||
endif ()
|
||||
endforeach ()
|
||||
endif ()
|
||||
endif ()
|
||||
else ()
|
||||
# once there is a different major version supported this should become a loop
|
||||
set(_lua_append_versions ${LUA_VERSIONS5})
|
||||
endif ()
|
||||
|
||||
list(APPEND _lua_include_subdirs "include/lua" "include")
|
||||
|
||||
foreach (ver IN LISTS _lua_append_versions)
|
||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _ver "${ver}")
|
||||
list(APPEND _lua_include_subdirs
|
||||
include/lua${CMAKE_MATCH_1}${CMAKE_MATCH_2}
|
||||
include/lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2}
|
||||
include/lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2}
|
||||
)
|
||||
endforeach ()
|
||||
|
||||
set(_lua_include_subdirs "${_lua_include_subdirs}" PARENT_SCOPE)
|
||||
set(_lua_append_versions "${_lua_append_versions}" PARENT_SCOPE)
|
||||
endfunction(_lua_set_version_vars)
|
||||
|
||||
function(_lua_check_header_version _hdr_file)
|
||||
# At least 5.[012] have different ways to express the version
|
||||
# so all of them need to be tested. Lua 5.2 defines LUA_VERSION
|
||||
# and LUA_RELEASE as joined by the C preprocessor, so avoid those.
|
||||
file(STRINGS "${_hdr_file}" lua_version_strings
|
||||
REGEX "^#define[ \t]+LUA_(RELEASE[ \t]+\"Lua [0-9]|VERSION([ \t]+\"Lua [0-9]|_[MR])).*")
|
||||
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MAJOR ";${lua_version_strings};")
|
||||
if (LUA_VERSION_MAJOR MATCHES "^[0-9]+$")
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MINOR ";${lua_version_strings};")
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_PATCH ";${lua_version_strings};")
|
||||
set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}")
|
||||
else ()
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};")
|
||||
if (NOT LUA_VERSION_STRING MATCHES "^[0-9.]+$")
|
||||
string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};")
|
||||
endif ()
|
||||
string(REGEX REPLACE "^([0-9]+)\\.[0-9.]*$" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_STRING}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)[0-9.]*$" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_STRING}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]).*" "\\1" LUA_VERSION_PATCH "${LUA_VERSION_STRING}")
|
||||
endif ()
|
||||
foreach (ver IN LISTS _lua_append_versions)
|
||||
if (ver STREQUAL "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
|
||||
set(LUA_VERSION_MAJOR ${LUA_VERSION_MAJOR} PARENT_SCOPE)
|
||||
set(LUA_VERSION_MINOR ${LUA_VERSION_MINOR} PARENT_SCOPE)
|
||||
set(LUA_VERSION_PATCH ${LUA_VERSION_PATCH} PARENT_SCOPE)
|
||||
set(LUA_VERSION_STRING ${LUA_VERSION_STRING} PARENT_SCOPE)
|
||||
return()
|
||||
endif ()
|
||||
endforeach ()
|
||||
endfunction(_lua_check_header_version)
|
||||
|
||||
_lua_set_version_vars()
|
||||
|
||||
if (LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h")
|
||||
_lua_check_header_version("${LUA_INCLUDE_DIR}/lua.h")
|
||||
endif ()
|
||||
|
||||
if (NOT LUA_VERSION_STRING)
|
||||
foreach (subdir IN LISTS _lua_include_subdirs)
|
||||
unset(LUA_INCLUDE_PREFIX CACHE)
|
||||
find_path(LUA_INCLUDE_PREFIX ${subdir}/lua.h
|
||||
HINTS
|
||||
ENV LUA_DIR
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
)
|
||||
if (LUA_INCLUDE_PREFIX)
|
||||
_lua_check_header_version("${LUA_INCLUDE_PREFIX}/${subdir}/lua.h")
|
||||
if (LUA_VERSION_STRING)
|
||||
set(LUA_INCLUDE_DIR "${LUA_INCLUDE_PREFIX}/${subdir}")
|
||||
break()
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
endif ()
|
||||
unset(_lua_include_subdirs)
|
||||
unset(_lua_append_versions)
|
||||
|
||||
if (LUA_VERSION_STRING)
|
||||
set(_lua_library_names
|
||||
lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}
|
||||
lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
|
||||
lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
|
||||
lua.${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
|
||||
)
|
||||
endif ()
|
||||
|
||||
find_library(LUA_LIBRARY
|
||||
NAMES ${_lua_library_names} lua
|
||||
HINTS
|
||||
ENV LUA_DIR
|
||||
PATH_SUFFIXES lib
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
unset(_lua_library_names)
|
||||
|
||||
if (LUA_LIBRARY)
|
||||
# include the math library for Unix
|
||||
if (UNIX AND NOT APPLE AND NOT BEOS)
|
||||
find_library(LUA_MATH_LIBRARY m)
|
||||
set(LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}")
|
||||
|
||||
# include dl library for statically-linked Lua library
|
||||
get_filename_component(LUA_LIB_EXT ${LUA_LIBRARY} EXT)
|
||||
if(LUA_LIB_EXT STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX)
|
||||
list(APPEND LUA_LIBRARIES ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
# For Windows and Mac, don't need to explicitly include the math library
|
||||
else ()
|
||||
set(LUA_LIBRARIES "${LUA_LIBRARY}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua
|
||||
REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR
|
||||
VERSION_VAR LUA_VERSION_STRING)
|
||||
|
||||
mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARY LUA_MATH_LIBRARY)
|
|
@ -2136,6 +2136,7 @@ lispindent({lnum}) Number Lisp indent for line {lnum}
|
|||
localtime() Number current time
|
||||
log({expr}) Float natural logarithm (base e) of {expr}
|
||||
log10({expr}) Float logarithm of Float {expr} to base 10
|
||||
luaeval({expr}[, {expr}]) any evaluate Lua expression
|
||||
map({expr1}, {expr2}) List/Dict change each item in {expr1} to {expr}
|
||||
maparg({name}[, {mode} [, {abbr} [, {dict}]]])
|
||||
String or Dict
|
||||
|
@ -5109,6 +5110,9 @@ log10({expr}) *log10()*
|
|||
:echo log10(0.01)
|
||||
< -2.0
|
||||
|
||||
luaeval({expr}[, {expr}])
|
||||
Evaluate Lua expression {expr} and return its result converted
|
||||
to Vim data structures. See |lua-luaeval| for more details.
|
||||
|
||||
map({expr1}, {expr2}) *map()*
|
||||
{expr1} must be a |List| or a |Dictionary|.
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
*if_lua.txt* For Neovim
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Luis Carvalho
|
||||
|
||||
|
||||
The Lua Interface to Vim *lua* *Lua*
|
||||
|
||||
1. Commands |lua-commands|
|
||||
2. The vim module |lua-vim|
|
||||
3. The luaeval function |lua-luaeval|
|
||||
|
||||
==============================================================================
|
||||
1. Commands *lua-commands*
|
||||
|
||||
*:lua*
|
||||
:[range]lua {chunk}
|
||||
Execute Lua chunk {chunk}. {not in Vi}
|
||||
|
||||
Examples:
|
||||
>
|
||||
:lua vim.api.nvim_command('echo "Hello, Neovim!"')
|
||||
<
|
||||
|
||||
:[range]lua << {endmarker}
|
||||
{script}
|
||||
{endmarker}
|
||||
Execute Lua script {script}. {not in Vi}
|
||||
Note: This command doesn't work when the Lua
|
||||
feature wasn't compiled in. To avoid errors, see
|
||||
|script-here|.
|
||||
|
||||
{endmarker} must NOT be preceded by any white space. If {endmarker} is
|
||||
omitted from after the "<<", a dot '.' must be used after {script}, like
|
||||
for the |:append| and |:insert| commands.
|
||||
This form of the |:lua| command is mainly useful for including Lua code
|
||||
in Vim scripts.
|
||||
|
||||
Example:
|
||||
>
|
||||
function! CurrentLineInfo()
|
||||
lua << EOF
|
||||
local linenr = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local curline = vim.api.nvim_buf_get_lines(
|
||||
0, linenr, linenr + 1, false)[1]
|
||||
print(string.format("Current line [%d] has %d bytes",
|
||||
linenr, #curline))
|
||||
EOF
|
||||
endfunction
|
||||
|
||||
Note that in example variables are prefixed with local: they will disappear
|
||||
when block finishes. This is not the case for globals.
|
||||
|
||||
To see what version of Lua you have: >
|
||||
:lua print(_VERSION)
|
||||
|
||||
If you use LuaJIT you can also use this: >
|
||||
:lua print(jit.version)
|
||||
<
|
||||
|
||||
*:luado*
|
||||
:[range]luado {body} Execute Lua function "function (line, linenr) {body}
|
||||
end" for each line in the [range], with the function
|
||||
argument being set to the text of each line in turn,
|
||||
without a trailing <EOL>, and the current line number.
|
||||
If the value returned by the function is a string it
|
||||
becomes the text of the line in the current turn. The
|
||||
default for [range] is the whole file: "1,$".
|
||||
{not in Vi}
|
||||
|
||||
Examples:
|
||||
>
|
||||
:luado return string.format("%s\t%d", line:reverse(), #line)
|
||||
|
||||
:lua require"lpeg"
|
||||
:lua -- balanced parenthesis grammar:
|
||||
:lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
|
||||
:luado if bp:match(line) then return "-->\t" .. line end
|
||||
<
|
||||
|
||||
*:luafile*
|
||||
:[range]luafile {file}
|
||||
Execute Lua script in {file}. {not in Vi}
|
||||
The whole argument is used as a single file name.
|
||||
|
||||
Examples:
|
||||
>
|
||||
:luafile script.lua
|
||||
:luafile %
|
||||
<
|
||||
|
||||
All these commands execute a Lua chunk from either the command line (:lua and
|
||||
:luado) or a file (:luafile) with the given line [range]. Similarly to the Lua
|
||||
interpreter, each chunk has its own scope and so only global variables are
|
||||
shared between command calls. All Lua default libraries are available. In
|
||||
addition, Lua "print" function has its output redirected to the Vim message
|
||||
area, with arguments separated by a white space instead of a tab.
|
||||
|
||||
Lua uses the "vim" module (see |lua-vim|) to issue commands to Neovim
|
||||
and manage buffers (|lua-buffer|) and windows (|lua-window|). However,
|
||||
procedures that alter buffer content, open new buffers, and change cursor
|
||||
position are restricted when the command is executed in the |sandbox|.
|
||||
|
||||
|
||||
==============================================================================
|
||||
2. The vim module *lua-vim*
|
||||
|
||||
Lua interfaces Vim through the "vim" module. Currently it only has `api`
|
||||
submodule which is a table with all API functions. Descriptions of these
|
||||
functions may be found in |api-funcs.txt|.
|
||||
|
||||
==============================================================================
|
||||
3. The luaeval function *lua-luaeval* *lua-eval*
|
||||
*luaeval()*
|
||||
|
||||
The (dual) equivalent of "vim.eval" for passing Lua values to Vim is
|
||||
"luaeval". "luaeval" takes an expression string and an optional argument used
|
||||
for _A inside expression and returns the result of the expression. It is
|
||||
semantically equivalent in Lua to:
|
||||
>
|
||||
local chunkheader = "local _A = select(1, ...) return "
|
||||
function luaeval (expstr, arg)
|
||||
local chunk = assert(loadstring(chunkheader .. expstr, "luaeval"))
|
||||
return chunk(arg) -- return typval
|
||||
end
|
||||
|
||||
Note that "_A" receives the argument to "luaeval". Lua nils, numbers, strings,
|
||||
tables and booleans are converted to their Vim respective types. An error is
|
||||
thrown if conversion of any of the remaining Lua types is attempted.
|
||||
|
||||
Note 2: lua tables are used as both dictionaries and lists, thus making it
|
||||
impossible to determine whether empty table is meant to be empty list or empty
|
||||
dictionary. Additionally lua does not have integer numbers. To distinguish
|
||||
between these cases there is the following agreement:
|
||||
|
||||
0. Empty table is empty list.
|
||||
1. Table with N incrementally growing integral numbers, starting from 1 and
|
||||
ending with N is considered to be a list.
|
||||
2. Table with string keys, none of which contains NUL byte, is considered to
|
||||
be a dictionary.
|
||||
3. Table with string keys, at least one of which contains NUL byte, is also
|
||||
considered to be a dictionary, but this time it is converted to
|
||||
a |msgpack-special-map|.
|
||||
4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
|
||||
value:
|
||||
- `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to
|
||||
a floating-point 1.0. Note that by default integral lua numbers are
|
||||
converted to |Number|s, non-integral are converted to |Float|s. This
|
||||
variant allows integral |Float|s.
|
||||
- `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty
|
||||
dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is
|
||||
converted to a dictionary `{'a': 42}`: non-string keys are ignored.
|
||||
Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3.
|
||||
are errors.
|
||||
- `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well
|
||||
as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not
|
||||
form a 1-step sequence from 1 to N are ignored, as well as all
|
||||
non-integral keys.
|
||||
|
||||
Examples: >
|
||||
|
||||
:echo luaeval('math.pi')
|
||||
:function Rand(x,y) " random uniform between x and y
|
||||
: return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y})
|
||||
: endfunction
|
||||
:echo Rand(1,10)
|
||||
|
||||
Note that currently second argument to `luaeval` undergoes VimL to lua
|
||||
conversion, so changing containers in lua do not affect values in VimL. Return
|
||||
value is also always converted. When converting, |msgpack-special-dict|s are
|
||||
treated specially.
|
||||
|
||||
==============================================================================
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -1331,6 +1331,9 @@ tag command action ~
|
|||
|:ltag| :lt[ag] jump to tag and add matching tags to the
|
||||
location list
|
||||
|:lunmap| :lu[nmap] like ":unmap!" but includes Lang-Arg mode
|
||||
|:lua| :lua execute Lua command
|
||||
|:luado| :luad[o] execute Lua command for each line
|
||||
|:luafile| :luaf[ile] execute Lua script file
|
||||
|:lvimgrep| :lv[imgrep] search for pattern in files
|
||||
|:lvimgrepadd| :lvimgrepa[dd] like :vimgrep, but append to current list
|
||||
|:lwindow| :lw[indow] open or close location window
|
||||
|
|
|
@ -950,6 +950,7 @@ Various: *various-functions*
|
|||
taglist() get list of matching tags
|
||||
tagfiles() get a list of tags files
|
||||
|
||||
luaeval() evaluate Lua expression
|
||||
py3eval() evaluate Python expression (|+python3|)
|
||||
pyeval() evaluate Python expression (|+python|)
|
||||
pyxeval() evaluate |python_x| expression
|
||||
|
|
|
@ -214,22 +214,15 @@ Additional differences:
|
|||
|
||||
- |shada-c| has no effect.
|
||||
- |shada-s| now limits size of every item and not just registers.
|
||||
- When reading ShaDa files items are merged according to the timestamp.
|
||||
|shada-merging|
|
||||
- 'viminfo' option got renamed to 'shada'. Old option is kept as an alias for
|
||||
compatibility reasons.
|
||||
- |:wviminfo| was renamed to |:wshada|, |:rviminfo| to |:rshada|. Old
|
||||
commands are still kept.
|
||||
- When writing (|:wshada| without bang or at exit) it merges much more data,
|
||||
and does this according to the timestamp. Vim merges only marks.
|
||||
|shada-merging|
|
||||
- ShaDa file format was designed with forward and backward compatibility in
|
||||
mind. |shada-compatibility|
|
||||
- Some errors make ShaDa code keep temporary file in-place for user to decide
|
||||
what to do with it. Vim deletes temporary file in these cases.
|
||||
|shada-error-handling|
|
||||
- Vim keeps no timestamps at all, neither in viminfo file nor in the instance
|
||||
itself.
|
||||
- ShaDa file keeps search direction (|v:searchforward|), viminfo does not.
|
||||
|
||||
|printf()| returns something meaningful when used with `%p` argument: in Vim
|
||||
|
@ -240,6 +233,17 @@ coerced to strings. See |id()| for more details, currently it uses
|
|||
|
||||
|c_CTRL-R| pasting a non-special register into |cmdline| omits the last <CR>.
|
||||
|
||||
Lua interface (|if_lua.txt|):
|
||||
|
||||
- `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim
|
||||
that prints `a` and `b` on separate lines, exactly like
|
||||
`:lua print("a\nb")` .
|
||||
- `:lua error('TEST')` will print “TEST” as the error in Vim and “E5105: Error
|
||||
while calling lua chunk: [string "<VimL compiled string>"]:1: TEST” in
|
||||
Neovim.
|
||||
- Lua has direct access to Neovim api via `vim.api`.
|
||||
- Currently most of features are missing.
|
||||
|
||||
==============================================================================
|
||||
5. Missing legacy features *nvim-features-missing*
|
||||
*if_lua* *if_perl* *if_mzscheme* *if_tcl*
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
if arg[1] == '--help' then
|
||||
print('Usage:')
|
||||
print(' gencharblob.lua source target varname')
|
||||
print('')
|
||||
print('Generates C file with big uint8_t blob.')
|
||||
print('Blob will be stored in a static const array named varname.')
|
||||
os.exit()
|
||||
end
|
||||
|
||||
assert(#arg == 3)
|
||||
|
||||
local source_file = arg[1]
|
||||
local target_file = arg[2]
|
||||
local varname = arg[3]
|
||||
|
||||
source = io.open(source_file, 'r')
|
||||
target = io.open(target_file, 'w')
|
||||
|
||||
target:write('#include <stdint.h>\n\n')
|
||||
target:write(('static const uint8_t %s[] = {\n'):format(varname))
|
||||
|
||||
num_bytes = 0
|
||||
MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line
|
||||
target:write(' ')
|
||||
|
||||
increase_num_bytes = function()
|
||||
num_bytes = num_bytes + 1
|
||||
if num_bytes == MAX_NUM_BYTES then
|
||||
num_bytes = 0
|
||||
target:write('\n ')
|
||||
end
|
||||
end
|
||||
|
||||
for line in source:lines() do
|
||||
for i = 1,string.len(line) do
|
||||
byte = string.byte(line, i)
|
||||
assert(byte ~= 0)
|
||||
target:write(string.format(' %3u,', byte))
|
||||
increase_num_bytes()
|
||||
end
|
||||
target:write(string.format(' %3u,', string.byte('\n', 1)))
|
||||
increase_num_bytes()
|
||||
end
|
||||
|
||||
target:write(' 0};\n')
|
||||
|
||||
source:close()
|
||||
target:close()
|
|
@ -69,17 +69,18 @@ local word = branch(
|
|||
right_word
|
||||
)
|
||||
)
|
||||
local inline_comment = concat(
|
||||
lit('/*'),
|
||||
any_amount(concat(
|
||||
neg_look_ahead(lit('*/')),
|
||||
any_character
|
||||
)),
|
||||
lit('*/')
|
||||
)
|
||||
local spaces = any_amount(branch(
|
||||
s,
|
||||
-- Comments are really handled by preprocessor, so the following is not needed
|
||||
concat(
|
||||
lit('/*'),
|
||||
any_amount(concat(
|
||||
neg_look_ahead(lit('*/')),
|
||||
any_character
|
||||
)),
|
||||
lit('*/')
|
||||
),
|
||||
inline_comment,
|
||||
concat(
|
||||
lit('//'),
|
||||
any_amount(concat(
|
||||
|
@ -110,6 +111,7 @@ local typ = one_or_more(typ_part)
|
|||
local typ_id = two_or_more(typ_part)
|
||||
local arg = typ_id -- argument name is swallowed by typ
|
||||
local pattern = concat(
|
||||
any_amount(branch(set(' ', '\t'), inline_comment)),
|
||||
typ_id, -- return type with function name
|
||||
spaces,
|
||||
lit('('),
|
||||
|
@ -188,24 +190,44 @@ local footer = [[
|
|||
local non_static = header
|
||||
local static = header
|
||||
|
||||
local filepattern = '^#%a* %d+ "[^"]-/?([^"/]+)"'
|
||||
local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"'
|
||||
local curfile
|
||||
|
||||
init = 0
|
||||
curfile = nil
|
||||
neededfile = fname:match('[^/]+$')
|
||||
local init = 0
|
||||
local curfile = nil
|
||||
local neededfile = fname:match('[^/]+$')
|
||||
local declline = 0
|
||||
local declendpos = 0
|
||||
local curdir = nil
|
||||
local is_needed_file = false
|
||||
while init ~= nil do
|
||||
init = text:find('\n', init)
|
||||
init = text:find('[\n;}]', init)
|
||||
if init == nil then
|
||||
break
|
||||
end
|
||||
local init_is_nl = text:sub(init, init) == '\n'
|
||||
init = init + 1
|
||||
if text:sub(init, init) == '#' then
|
||||
file = text:match(filepattern, init)
|
||||
if init_is_nl and is_needed_file then
|
||||
declline = declline + 1
|
||||
end
|
||||
if init_is_nl and text:sub(init, init) == '#' then
|
||||
local line, dir, file = text:match(filepattern, init)
|
||||
if file ~= nil then
|
||||
curfile = file
|
||||
is_needed_file = (curfile == neededfile)
|
||||
declline = tonumber(line) - 1
|
||||
local curdir_start = dir:find('src/nvim/')
|
||||
if curdir_start ~= nil then
|
||||
curdir = dir:sub(curdir_start + #('src/nvim/'))
|
||||
else
|
||||
curdir = dir
|
||||
end
|
||||
else
|
||||
declline = declline - 1
|
||||
end
|
||||
elseif curfile == neededfile then
|
||||
elseif init < declendpos then
|
||||
-- Skipping over declaration
|
||||
elseif is_needed_file then
|
||||
s = init
|
||||
e = pattern:match(text, init)
|
||||
if e ~= nil then
|
||||
|
@ -225,13 +247,17 @@ while init ~= nil do
|
|||
declaration = declaration:gsub(' ?(%*+) ?', ' %1')
|
||||
declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1')
|
||||
declaration = declaration:gsub(' $', '')
|
||||
declaration = declaration .. ';\n'
|
||||
if text:sub(s, s + 5) == 'static' then
|
||||
declaration = declaration:gsub('^ ', '')
|
||||
declaration = declaration .. ';'
|
||||
declaration = declaration .. (' // %s/%s:%u'):format(
|
||||
curdir, curfile, declline)
|
||||
declaration = declaration .. '\n'
|
||||
if declaration:sub(1, 6) == 'static' then
|
||||
static = static .. declaration
|
||||
else
|
||||
non_static = non_static .. declaration
|
||||
end
|
||||
init = e
|
||||
declendpos = e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,7 +47,16 @@ c_proto = Ct(
|
|||
grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
|
||||
|
||||
-- we need at least 4 arguments since the last two are output files
|
||||
assert(#arg >= 3)
|
||||
if arg[1] == '--help' then
|
||||
print('Usage: genmsgpack.lua args')
|
||||
print('Args: 1: source directory')
|
||||
print(' 2: dispatch output file (dispatch_wrappers.generated.h)')
|
||||
print(' 3: functions metadata output file (funcs_metadata.generated.h)')
|
||||
print(' 4: API metadata output file (api_metadata.mpack)')
|
||||
print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)')
|
||||
print(' rest: C files where API functions are defined')
|
||||
end
|
||||
assert(#arg >= 4)
|
||||
functions = {}
|
||||
|
||||
local nvimsrcdir = arg[1]
|
||||
|
@ -58,17 +67,18 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path
|
|||
headers = {}
|
||||
|
||||
-- output h file with generated dispatch functions
|
||||
dispatch_outputf = arg[#arg-2]
|
||||
dispatch_outputf = arg[2]
|
||||
-- output h file with packed metadata
|
||||
funcs_metadata_outputf = arg[#arg-1]
|
||||
funcs_metadata_outputf = arg[3]
|
||||
-- output metadata mpack file, for use by other build scripts
|
||||
mpack_outputf = arg[#arg]
|
||||
mpack_outputf = arg[4]
|
||||
lua_c_bindings_outputf = arg[5]
|
||||
|
||||
-- set of function names, used to detect duplicates
|
||||
function_names = {}
|
||||
|
||||
-- read each input file, parse and append to the api metadata
|
||||
for i = 2, #arg - 3 do
|
||||
for i = 6, #arg do
|
||||
local full_path = arg[i]
|
||||
local parts = {}
|
||||
for part in string.gmatch(full_path, '[^/]+') do
|
||||
|
@ -119,7 +129,9 @@ local deprecated_aliases = require("api.dispatch_deprecated")
|
|||
for i,f in ipairs(shallowcopy(functions)) do
|
||||
local ismethod = false
|
||||
if startswith(f.name, "nvim_") then
|
||||
if f.since == nil then
|
||||
if startswith(f.name, "nvim__") then
|
||||
f.since = -1
|
||||
elseif f.since == nil then
|
||||
print("Function "..f.name.." lacks since field.\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
@ -170,11 +182,13 @@ exported_attributes = {'name', 'parameters', 'return_type', 'method',
|
|||
'since', 'deprecated_since'}
|
||||
exported_functions = {}
|
||||
for _,f in ipairs(functions) do
|
||||
local f_exported = {}
|
||||
for _,attr in ipairs(exported_attributes) do
|
||||
f_exported[attr] = f[attr]
|
||||
if not startswith(f.name, "nvim__") then
|
||||
local f_exported = {}
|
||||
for _,attr in ipairs(exported_attributes) do
|
||||
f_exported[attr] = f[attr]
|
||||
end
|
||||
exported_functions[#exported_functions+1] = f_exported
|
||||
end
|
||||
exported_functions[#exported_functions+1] = f_exported
|
||||
end
|
||||
|
||||
|
||||
|
@ -212,6 +226,14 @@ local function real_type(type)
|
|||
return rv
|
||||
end
|
||||
|
||||
local function attr_name(rt)
|
||||
if rt == 'Float' then
|
||||
return 'floating'
|
||||
else
|
||||
return rt:lower()
|
||||
end
|
||||
end
|
||||
|
||||
-- start the handler functions. Visit each function metadata to build the
|
||||
-- handler function with code generated for validating arguments and calling to
|
||||
-- the real API.
|
||||
|
@ -248,7 +270,7 @@ for i = 1, #functions do
|
|||
output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;')
|
||||
else
|
||||
output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {')
|
||||
output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';')
|
||||
output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..attr_name(rt)..';')
|
||||
end
|
||||
if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then
|
||||
-- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages
|
||||
|
@ -336,3 +358,155 @@ output:close()
|
|||
mpack_output = io.open(mpack_outputf, 'wb')
|
||||
mpack_output:write(mpack.pack(functions))
|
||||
mpack_output:close()
|
||||
|
||||
local function include_headers(output, headers)
|
||||
for i = 1, #headers do
|
||||
if headers[i]:sub(-12) ~= '.generated.h' then
|
||||
output:write('\n#include "nvim/'..headers[i]..'"')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function write_shifted_output(output, str)
|
||||
str = str:gsub('\n ', '\n')
|
||||
str = str:gsub('^ ', '')
|
||||
str = str:gsub(' +$', '')
|
||||
output:write(str)
|
||||
end
|
||||
|
||||
-- start building lua output
|
||||
output = io.open(lua_c_bindings_outputf, 'wb')
|
||||
|
||||
output:write([[
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/lua/converter.h"
|
||||
]])
|
||||
include_headers(output, headers)
|
||||
output:write('\n')
|
||||
|
||||
lua_c_functions = {}
|
||||
|
||||
local function process_function(fn)
|
||||
lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name)
|
||||
write_shifted_output(output, string.format([[
|
||||
|
||||
static int %s(lua_State *lstate)
|
||||
{
|
||||
Error err = ERROR_INIT;
|
||||
if (lua_gettop(lstate) != %i) {
|
||||
api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s");
|
||||
goto exit_0;
|
||||
}
|
||||
]], lua_c_function_name, #fn.parameters, #fn.parameters,
|
||||
(#fn.parameters == 1) and '' or 's'))
|
||||
lua_c_functions[#lua_c_functions + 1] = {
|
||||
binding=lua_c_function_name,
|
||||
api=fn.name
|
||||
}
|
||||
local cparams = ''
|
||||
local free_code = {}
|
||||
for j = #fn.parameters,1,-1 do
|
||||
param = fn.parameters[j]
|
||||
cparam = string.format('arg%u', j)
|
||||
param_type = real_type(param[1])
|
||||
lc_param_type = param_type:lower()
|
||||
write_shifted_output(output, string.format([[
|
||||
const %s %s = nlua_pop_%s(lstate, &err);
|
||||
|
||||
if (ERROR_SET(&err)) {
|
||||
goto exit_%u;
|
||||
}
|
||||
]], param[1], cparam, param_type, #fn.parameters - j))
|
||||
free_code[#free_code + 1] = ('api_free_%s(%s);'):format(
|
||||
lc_param_type, cparam)
|
||||
cparams = cparam .. ', ' .. cparams
|
||||
end
|
||||
if fn.receives_channel_id then
|
||||
cparams = 'LUA_INTERNAL_CALL, ' .. cparams
|
||||
end
|
||||
if fn.can_fail then
|
||||
cparams = cparams .. '&err'
|
||||
else
|
||||
cparams = cparams:gsub(', $', '')
|
||||
end
|
||||
local free_at_exit_code = ''
|
||||
for i = 1, #free_code do
|
||||
local rev_i = #free_code - i + 1
|
||||
local code = free_code[rev_i]
|
||||
if i == 1 then
|
||||
free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code)
|
||||
else
|
||||
free_at_exit_code = free_at_exit_code .. ('\n exit_%u:\n %s'):format(
|
||||
rev_i, code)
|
||||
end
|
||||
end
|
||||
local err_throw_code = [[
|
||||
|
||||
exit_0:
|
||||
if (ERROR_SET(&err)) {
|
||||
luaL_where(lstate, 1);
|
||||
lua_pushstring(lstate, err.msg);
|
||||
api_clear_error(&err);
|
||||
lua_concat(lstate, 2);
|
||||
return lua_error(lstate);
|
||||
}
|
||||
]]
|
||||
if fn.return_type ~= 'void' then
|
||||
if fn.return_type:match('^ArrayOf') then
|
||||
return_type = 'Array'
|
||||
else
|
||||
return_type = fn.return_type
|
||||
end
|
||||
write_shifted_output(output, string.format([[
|
||||
const %s ret = %s(%s);
|
||||
nlua_push_%s(lstate, ret);
|
||||
api_free_%s(ret);
|
||||
%s
|
||||
%s
|
||||
return 1;
|
||||
]], fn.return_type, fn.name, cparams, return_type, return_type:lower(),
|
||||
free_at_exit_code, err_throw_code))
|
||||
else
|
||||
write_shifted_output(output, string.format([[
|
||||
%s(%s);
|
||||
%s
|
||||
%s
|
||||
return 0;
|
||||
]], fn.name, cparams, free_at_exit_code, err_throw_code))
|
||||
end
|
||||
write_shifted_output(output, [[
|
||||
}
|
||||
]])
|
||||
end
|
||||
|
||||
for _, fn in ipairs(functions) do
|
||||
if not fn.noeval or fn.name:sub(1, 4) == '_vim' then
|
||||
process_function(fn)
|
||||
end
|
||||
end
|
||||
|
||||
output:write(string.format([[
|
||||
void nlua_add_api_functions(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
lua_createtable(lstate, 0, %u);
|
||||
]], #lua_c_functions))
|
||||
for _, func in ipairs(lua_c_functions) do
|
||||
output:write(string.format([[
|
||||
|
||||
lua_pushcfunction(lstate, &%s);
|
||||
lua_setfield(lstate, -2, "%s");]], func.binding, func.api))
|
||||
end
|
||||
output:write([[
|
||||
|
||||
lua_setfield(lstate, -2, "api");
|
||||
}
|
||||
]])
|
||||
|
||||
output:close()
|
|
@ -182,6 +182,7 @@ _ERROR_CATEGORIES = [
|
|||
'build/include_order',
|
||||
'build/printf_format',
|
||||
'build/storage_class',
|
||||
'build/useless_fattr',
|
||||
'readability/alt_tokens',
|
||||
'readability/bool',
|
||||
'readability/braces',
|
||||
|
@ -1225,6 +1226,10 @@ def CheckForHeaderGuard(filename, lines, error):
|
|||
lines: An array of strings, each representing a line of the file.
|
||||
error: The function to call with any errors found.
|
||||
"""
|
||||
if filename.endswith('.c.h') or FileInfo(filename).RelativePath() in set((
|
||||
'func_attr.h',
|
||||
)):
|
||||
return
|
||||
|
||||
cppvar = GetHeaderGuardCPPVariable(filename)
|
||||
|
||||
|
|
|
@ -12,11 +12,10 @@ endif()
|
|||
|
||||
set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)
|
||||
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
|
||||
set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua)
|
||||
file(GLOB API_HEADERS api/*.h)
|
||||
file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
|
||||
set(MSGPACK_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genmsgpack.lua)
|
||||
set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
|
||||
set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack)
|
||||
set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c)
|
||||
set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua)
|
||||
set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
|
||||
set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h)
|
||||
|
@ -33,8 +32,10 @@ set(EVENTS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gen_events.lua)
|
|||
set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua)
|
||||
set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua)
|
||||
set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode)
|
||||
file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt)
|
||||
set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h)
|
||||
set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h)
|
||||
set(VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua)
|
||||
set(CHAR_BLOB_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gencharblob.lua)
|
||||
set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json)
|
||||
set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint")
|
||||
set(LINT_SUPPRESS_URL "${LINT_SUPPRESS_URL_BASE}/errors.json")
|
||||
|
@ -46,6 +47,10 @@ 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 API_HEADERS api/*.h)
|
||||
file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
|
||||
|
||||
include_directories(${GENERATED_DIR})
|
||||
include_directories(${CACHED_GENERATED_DIR})
|
||||
include_directories(${GENERATED_INCLUDES_DIR})
|
||||
|
@ -67,6 +72,7 @@ foreach(subdir
|
|||
tui
|
||||
event
|
||||
eval
|
||||
lua
|
||||
)
|
||||
if(${subdir} MATCHES "tui" AND NOT FEAT_TUI)
|
||||
continue()
|
||||
|
@ -157,7 +163,7 @@ if(CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN)
|
|||
endif()
|
||||
|
||||
get_directory_property(gen_includes INCLUDE_DIRECTORIES)
|
||||
foreach(gen_include ${gen_includes})
|
||||
foreach(gen_include ${gen_includes} ${LUAJIT_INCLUDE_DIRS})
|
||||
list(APPEND gen_cflags "-I${gen_include}")
|
||||
endforeach()
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
|
||||
|
@ -219,18 +225,34 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES}
|
|||
${UNICODE_FILES}
|
||||
)
|
||||
|
||||
add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA}
|
||||
${API_METADATA}
|
||||
COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
|
||||
${API_HEADERS} ${GENERATED_API_DISPATCH}
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA}
|
||||
${API_METADATA} ${MSGPACK_LUA_C_BINDINGS}
|
||||
COMMAND ${LUA_PRG} ${MSGPACK_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
|
||||
${GENERATED_API_DISPATCH}
|
||||
${GENERATED_FUNCS_METADATA} ${API_METADATA}
|
||||
${MSGPACK_LUA_C_BINDINGS}
|
||||
${API_HEADERS}
|
||||
DEPENDS
|
||||
${API_HEADERS}
|
||||
${MSGPACK_RPC_HEADERS}
|
||||
${DISPATCH_GENERATOR}
|
||||
${MSGPACK_GENERATOR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${VIM_MODULE_FILE}
|
||||
COMMAND ${LUA_PRG} ${CHAR_BLOB_GENERATOR} ${VIM_MODULE_SOURCE}
|
||||
${VIM_MODULE_FILE} vim_module
|
||||
DEPENDS
|
||||
${CHAR_BLOB_GENERATOR}
|
||||
${VIM_MODULE_SOURCE}
|
||||
)
|
||||
|
||||
list(APPEND NVIM_GENERATED_SOURCES
|
||||
"${MSGPACK_LUA_C_BINDINGS}"
|
||||
)
|
||||
|
||||
list(APPEND NVIM_GENERATED_FOR_HEADERS
|
||||
"${GENERATED_EX_CMDS_ENUM}"
|
||||
"${GENERATED_EVENTS_ENUM}"
|
||||
|
@ -242,6 +264,7 @@ list(APPEND NVIM_GENERATED_FOR_SOURCES
|
|||
"${GENERATED_EVENTS_NAMES_MAP}"
|
||||
"${GENERATED_OPTIONS}"
|
||||
"${GENERATED_UNICODE_TABLES}"
|
||||
"${VIM_MODULE_FILE}"
|
||||
)
|
||||
|
||||
list(APPEND NVIM_GENERATED_SOURCES
|
||||
|
@ -315,6 +338,23 @@ if(UNIX)
|
|||
endif()
|
||||
|
||||
set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES})
|
||||
set(NVIM_TEST_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES})
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "2.8.8")
|
||||
if(PREFER_LUAJIT)
|
||||
include_directories(${LUAJIT_INCLUDE_DIRS})
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Must support INCLUDE_DIRECTORIES target property to build")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(PREFER_LUAJIT)
|
||||
list(APPEND NVIM_EXEC_LINK_LIBRARIES ${LUAJIT_LIBRARIES})
|
||||
else()
|
||||
list(APPEND NVIM_EXEC_LINK_LIBRARIES ${LUA_LIBRARIES})
|
||||
endif()
|
||||
list(APPEND NVIM_TEST_LINK_LIBRARIES ${LUAJIT_LIBRARIES})
|
||||
|
||||
# Don't use jemalloc in the unit test library.
|
||||
if(JEMALLOC_FOUND)
|
||||
|
@ -326,6 +366,14 @@ add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
|||
target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES})
|
||||
install_helper(TARGETS nvim)
|
||||
|
||||
if(PREFER_LUAJIT)
|
||||
set(LUA_PREFERRED_INCLUDE_DIRS ${LUAJIT_INCLUDE_DIRS})
|
||||
else()
|
||||
set(LUA_PREFERRED_INCLUDE_DIRS ${LUA_INCLUDE_DIRS})
|
||||
endif()
|
||||
set_property(TARGET nvim APPEND PROPERTY
|
||||
INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS})
|
||||
|
||||
if(WIN32)
|
||||
# Copy DLLs and third-party tools to bin/ and install them along with nvim
|
||||
add_custom_target(nvim_runtime_deps ALL
|
||||
|
@ -372,6 +420,50 @@ if(WIN32)
|
|||
add_dependencies(nvim_runtime_deps external_blobs)
|
||||
endif()
|
||||
|
||||
add_library(
|
||||
libnvim
|
||||
STATIC
|
||||
EXCLUDE_FROM_ALL
|
||||
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
|
||||
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
||||
)
|
||||
set_property(TARGET libnvim APPEND PROPERTY
|
||||
INCLUDE_DIRECTORIES ${LUAJIT_INCLUDE_DIRS})
|
||||
target_link_libraries(libnvim ${NVIM_TEST_LINK_LIBRARIES})
|
||||
set_target_properties(
|
||||
libnvim
|
||||
PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
OUTPUT_NAME nvim
|
||||
)
|
||||
set_property(
|
||||
TARGET libnvim
|
||||
APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB "
|
||||
)
|
||||
|
||||
add_library(
|
||||
nvim-test
|
||||
MODULE
|
||||
EXCLUDE_FROM_ALL
|
||||
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
|
||||
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
||||
${UNIT_TEST_FIXTURES}
|
||||
)
|
||||
target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES})
|
||||
set_property(
|
||||
TARGET nvim-test
|
||||
APPEND PROPERTY INCLUDE_DIRECTORIES ${LUAJIT_INCLUDE_DIRS}
|
||||
)
|
||||
set_target_properties(
|
||||
nvim-test
|
||||
PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
)
|
||||
set_property(
|
||||
TARGET nvim-test
|
||||
APPEND_STRING PROPERTY COMPILE_FLAGS " -DUNIT_TESTING "
|
||||
)
|
||||
|
||||
if(CLANG_ASAN_UBSAN)
|
||||
message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.")
|
||||
check_c_compiler_flag(-fno-sanitize-recover=all SANITIZE_RECOVER_ALL)
|
||||
|
@ -396,19 +488,6 @@ elseif(CLANG_TSAN)
|
|||
set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ")
|
||||
endif()
|
||||
|
||||
add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
||||
${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS})
|
||||
target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES})
|
||||
set_target_properties(libnvim PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
OUTPUT_NAME nvim)
|
||||
set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ")
|
||||
|
||||
add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
|
||||
${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NVIM_HEADERS})
|
||||
target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES})
|
||||
set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING)
|
||||
|
||||
function(get_test_target prefix sfile relative_path_var target_var)
|
||||
get_filename_component(full_d "${sfile}" PATH)
|
||||
file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}")
|
||||
|
@ -446,6 +525,10 @@ foreach(hfile ${NVIM_HEADERS})
|
|||
${texe}
|
||||
EXCLUDE_FROM_ALL
|
||||
${tsource} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_HEADERS})
|
||||
set_property(
|
||||
TARGET ${texe}
|
||||
APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
list(FIND NO_SINGLE_CHECK_HEADERS "${relative_path}" hfile_exclude_idx)
|
||||
if(${hfile_exclude_idx} EQUAL -1)
|
||||
|
|
|
@ -195,7 +195,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
|
|||
Object str = STRING_OBJ(cstr_to_string(bufstr));
|
||||
|
||||
// Vim represents NULs as NLs, but this may confuse clients.
|
||||
if (channel_id != INTERNAL_CALL) {
|
||||
if (channel_id != VIML_INTERNAL_CALL) {
|
||||
strchrsub(str.data.string.data, '\n', '\0');
|
||||
}
|
||||
|
||||
|
@ -295,6 +295,24 @@ void nvim_buf_set_lines(uint64_t channel_id,
|
|||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < replacement.size; i++) {
|
||||
if (replacement.items[i].type != kObjectTypeString) {
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"All items in the replacement array must be strings");
|
||||
return;
|
||||
}
|
||||
// Disallow newlines in the middle of the line.
|
||||
if (channel_id != VIML_INTERNAL_CALL) {
|
||||
const String l = replacement.items[i].data.string;
|
||||
if (memchr(l.data, NL, l.size)) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"String cannot contain newlines");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
win_T *save_curwin = NULL;
|
||||
tabpage_T *save_curtab = NULL;
|
||||
size_t new_len = replacement.size;
|
||||
|
@ -303,27 +321,12 @@ void nvim_buf_set_lines(uint64_t channel_id,
|
|||
char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL;
|
||||
|
||||
for (size_t i = 0; i < new_len; i++) {
|
||||
if (replacement.items[i].type != kObjectTypeString) {
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"All items in the replacement array must be strings");
|
||||
goto end;
|
||||
}
|
||||
const String l = replacement.items[i].data.string;
|
||||
|
||||
String l = replacement.items[i].data.string;
|
||||
|
||||
// Fill lines[i] with l's contents. Disallow newlines in the middle of a
|
||||
// line and convert NULs to newlines to avoid truncation.
|
||||
lines[i] = xmallocz(l.size);
|
||||
for (size_t j = 0; j < l.size; j++) {
|
||||
if (l.data[j] == '\n' && channel_id != INTERNAL_CALL) {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"String cannot contain newlines");
|
||||
new_len = i + 1;
|
||||
goto end;
|
||||
}
|
||||
lines[i][j] = (char) (l.data[j] == '\0' ? '\n' : l.data[j]);
|
||||
}
|
||||
// Fill lines[i] with l's contents. Convert NULs to newlines as required by
|
||||
// NL-used-for-NUL.
|
||||
lines[i] = xmemdupz(l.data, l.size);
|
||||
memchrsub(lines[i], NUL, NL, l.size);
|
||||
}
|
||||
|
||||
try_start();
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nvim/func_attr.h"
|
||||
|
||||
#define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL}
|
||||
#define STRING_INIT {.data = NULL, .size = 0}
|
||||
#define OBJECT_INIT { .type = kObjectTypeNil }
|
||||
|
@ -36,8 +38,27 @@ typedef enum {
|
|||
/// Used as the message ID of notifications.
|
||||
#define NO_RESPONSE UINT64_MAX
|
||||
|
||||
/// Used as channel_id when the call is local.
|
||||
#define INTERNAL_CALL UINT64_MAX
|
||||
/// Mask for all internal calls
|
||||
#define INTERNAL_CALL_MASK (((uint64_t)1) << (sizeof(uint64_t) * 8 - 1))
|
||||
|
||||
/// Internal call from VimL code
|
||||
#define VIML_INTERNAL_CALL INTERNAL_CALL_MASK
|
||||
|
||||
/// Internal call from lua code
|
||||
#define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1)
|
||||
|
||||
static inline bool is_internal_call(uint64_t channel_id)
|
||||
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST;
|
||||
|
||||
/// Check whether call is internal
|
||||
///
|
||||
/// @param[in] channel_id Channel id.
|
||||
///
|
||||
/// @return true if channel_id refers to internal channel.
|
||||
static inline bool is_internal_call(const uint64_t channel_id)
|
||||
{
|
||||
return !!(channel_id & INTERNAL_CALL_MASK);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ErrorType type;
|
||||
|
@ -78,16 +99,17 @@ typedef struct {
|
|||
} Dictionary;
|
||||
|
||||
typedef enum {
|
||||
kObjectTypeBuffer,
|
||||
kObjectTypeWindow,
|
||||
kObjectTypeTabpage,
|
||||
kObjectTypeNil,
|
||||
kObjectTypeNil = 0,
|
||||
kObjectTypeBoolean,
|
||||
kObjectTypeInteger,
|
||||
kObjectTypeFloat,
|
||||
kObjectTypeString,
|
||||
kObjectTypeArray,
|
||||
kObjectTypeDictionary,
|
||||
// EXT types, cannot be split or reordered, see #EXT_OBJECT_TYPE_SHIFT
|
||||
kObjectTypeBuffer,
|
||||
kObjectTypeWindow,
|
||||
kObjectTypeTabpage,
|
||||
} ObjectType;
|
||||
|
||||
struct object {
|
||||
|
|
|
@ -353,7 +353,7 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
|
|||
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
|
||||
|
||||
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
|
||||
kv_push(edata->stack, FLOATING_OBJ((Float)(flt)))
|
||||
kv_push(edata->stack, FLOAT_OBJ((Float)(flt)))
|
||||
|
||||
#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
|
||||
do { \
|
||||
|
@ -864,15 +864,18 @@ static void init_type_metadata(Dictionary *metadata)
|
|||
Dictionary types = ARRAY_DICT_INIT;
|
||||
|
||||
Dictionary buffer_metadata = ARRAY_DICT_INIT;
|
||||
PUT(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer));
|
||||
PUT(buffer_metadata, "id",
|
||||
INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT));
|
||||
PUT(buffer_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_buf_")));
|
||||
|
||||
Dictionary window_metadata = ARRAY_DICT_INIT;
|
||||
PUT(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow));
|
||||
PUT(window_metadata, "id",
|
||||
INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT));
|
||||
PUT(window_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_win_")));
|
||||
|
||||
Dictionary tabpage_metadata = ARRAY_DICT_INIT;
|
||||
PUT(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage));
|
||||
PUT(tabpage_metadata, "id",
|
||||
INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT));
|
||||
PUT(tabpage_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_tabpage_")));
|
||||
|
||||
PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata));
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
.type = kObjectTypeInteger, \
|
||||
.data.integer = i })
|
||||
|
||||
#define FLOATING_OBJ(f) ((Object) { \
|
||||
#define FLOAT_OBJ(f) ((Object) { \
|
||||
.type = kObjectTypeFloat, \
|
||||
.data.floating = f })
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
|
|||
{
|
||||
if (str.size == 0) {
|
||||
// Empty string
|
||||
return str;
|
||||
return (String) { .data = NULL, .size = 0 };
|
||||
}
|
||||
|
||||
char *ptr = NULL;
|
||||
|
@ -203,7 +203,8 @@ Object nvim_eval(String expr, Error *err)
|
|||
return rv;
|
||||
}
|
||||
|
||||
/// Calls a VimL function with the given arguments.
|
||||
/// Calls a VimL function with the given arguments
|
||||
///
|
||||
/// On VimL error: Returns a generic error; v:errmsg is not updated.
|
||||
///
|
||||
/// @param fname Function to call
|
||||
|
@ -855,3 +856,57 @@ static void write_msg(String message, bool to_err)
|
|||
--no_wait_return;
|
||||
msg_end();
|
||||
}
|
||||
|
||||
// Functions used for testing purposes
|
||||
|
||||
/// Returns object given as argument
|
||||
///
|
||||
/// This API function is used for testing. One should not rely on its presence
|
||||
/// in plugins.
|
||||
///
|
||||
/// @param[in] obj Object to return.
|
||||
///
|
||||
/// @return its argument.
|
||||
Object nvim__id(Object obj)
|
||||
{
|
||||
return copy_object(obj);
|
||||
}
|
||||
|
||||
/// Returns array given as argument
|
||||
///
|
||||
/// This API function is used for testing. One should not rely on its presence
|
||||
/// in plugins.
|
||||
///
|
||||
/// @param[in] arr Array to return.
|
||||
///
|
||||
/// @return its argument.
|
||||
Array nvim__id_array(Array arr)
|
||||
{
|
||||
return copy_object(ARRAY_OBJ(arr)).data.array;
|
||||
}
|
||||
|
||||
/// Returns dictionary given as argument
|
||||
///
|
||||
/// This API function is used for testing. One should not rely on its presence
|
||||
/// in plugins.
|
||||
///
|
||||
/// @param[in] dct Dictionary to return.
|
||||
///
|
||||
/// @return its argument.
|
||||
Dictionary nvim__id_dictionary(Dictionary dct)
|
||||
{
|
||||
return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
|
||||
}
|
||||
|
||||
/// Returns floating-point value given as argument
|
||||
///
|
||||
/// This API function is used for testing. One should not rely on its presence
|
||||
/// in plugins.
|
||||
///
|
||||
/// @param[in] flt Value to return.
|
||||
///
|
||||
/// @return its argument.
|
||||
Float nvim__id_float(Float flt)
|
||||
{
|
||||
return flt;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,10 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
|
|||
{
|
||||
win_T *win = find_window_by_handle(window, err);
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger
|
||||
|| pos.items[1].type != kObjectTypeInteger) {
|
||||
api_set_error(err,
|
||||
|
@ -70,10 +74,6 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t row = pos.items[0].data.integer;
|
||||
int64_t col = pos.items[1].data.integer;
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/lib/khash.h"
|
||||
#include "nvim/lib/queue.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/executor.h"
|
||||
#include "nvim/eval/gc.h"
|
||||
|
@ -6510,7 +6511,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
}
|
||||
|
||||
Error err = ERROR_INIT;
|
||||
Object result = fn(INTERNAL_CALL, args, &err);
|
||||
Object result = fn(VIML_INTERNAL_CALL, args, &err);
|
||||
|
||||
if (ERROR_SET(&err)) {
|
||||
nvim_err_writeln(cstr_as_string(err.msg));
|
||||
|
@ -10973,7 +10974,9 @@ static int inputsecret_flag = 0;
|
|||
* prompt. The third argument to f_inputdialog() specifies the value to return
|
||||
* when the user cancels the prompt.
|
||||
*/
|
||||
static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog)
|
||||
void get_user_input(const typval_T *const argvars,
|
||||
typval_T *const rettv, const bool inputdialog)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char *prompt = tv_get_string_chk(&argvars[0]);
|
||||
int cmd_silent_save = cmd_silent;
|
||||
|
@ -12076,6 +12079,18 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
|
|||
}
|
||||
}
|
||||
|
||||
/// luaeval() function implementation
|
||||
static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char *const str = (const char *)tv_get_string_chk(&argvars[0]);
|
||||
if (str == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
executor_eval_lua(cstr_as_string((char *)str), &argvars[1], rettv);
|
||||
}
|
||||
|
||||
/*
|
||||
* "map()" function
|
||||
*/
|
||||
|
|
|
@ -193,6 +193,7 @@ return {
|
|||
localtime={},
|
||||
log={args=1, func="float_op_wrapper", data="&log"},
|
||||
log10={args=1, func="float_op_wrapper", data="&log10"},
|
||||
luaeval={args={1, 2}},
|
||||
map={args=2},
|
||||
maparg={args={1, 4}},
|
||||
mapcheck={args={1, 3}},
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nvim/ascii.h"
|
||||
#include "nvim/macros.h"
|
||||
#include "nvim/message.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/charset.h" // vim_str2nr
|
||||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/vim.h" // OK, FAIL
|
||||
|
@ -223,6 +224,78 @@ static inline int json_decoder_pop(ValuesStackItem obj,
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
/// Create a new special dictionary that ought to represent a MAP
|
||||
///
|
||||
/// @param[out] ret_tv Address where new special dictionary is saved.
|
||||
///
|
||||
/// @return [allocated] list which should contain key-value pairs. Return value
|
||||
/// may be safely ignored.
|
||||
list_T *decode_create_map_special_dict(typval_T *const ret_tv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
list_T *const list = tv_list_alloc();
|
||||
list->lv_refcount++;
|
||||
create_special_dict(ret_tv, kMPMap, ((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
return list;
|
||||
}
|
||||
|
||||
/// Convert char* string to typval_T
|
||||
///
|
||||
/// Depending on whether string has (no) NUL bytes, it may use a special
|
||||
/// dictionary or decode string to VAR_STRING.
|
||||
///
|
||||
/// @param[in] s String to decode.
|
||||
/// @param[in] len String length.
|
||||
/// @param[in] hasnul Whether string has NUL byte, not or it was not yet
|
||||
/// determined.
|
||||
/// @param[in] binary If true, save special string type as kMPBinary,
|
||||
/// otherwise kMPString.
|
||||
/// @param[in] s_allocated If true, then `s` was allocated and can be saved in
|
||||
/// a returned structure. If it is not saved there, it
|
||||
/// will be freed.
|
||||
///
|
||||
/// @return Decoded string.
|
||||
typval_T decode_string(const char *const s, const size_t len,
|
||||
const TriState hasnul, const bool binary,
|
||||
const bool s_allocated)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
assert(s != NULL || len == 0);
|
||||
const bool really_hasnul = (hasnul == kNone
|
||||
? memchr(s, NUL, len) != NULL
|
||||
: (bool)hasnul);
|
||||
if (really_hasnul) {
|
||||
list_T *const list = tv_list_alloc();
|
||||
list->lv_refcount++;
|
||||
typval_T tv;
|
||||
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
const int elw_ret = encode_list_write((void *)list, s, len);
|
||||
if (s_allocated) {
|
||||
xfree((void *)s);
|
||||
}
|
||||
if (elw_ret == -1) {
|
||||
tv_clear(&tv);
|
||||
return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
|
||||
}
|
||||
return tv;
|
||||
} else {
|
||||
return (typval_T) {
|
||||
.v_type = VAR_STRING,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_string = (char_u *)(
|
||||
s_allocated ? (char *)s : xmemdupz(s, len)) },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse JSON double-quoted string
|
||||
///
|
||||
/// @param[in] buf Buffer being converted.
|
||||
|
@ -416,29 +489,13 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
|
|||
}
|
||||
PUT_FST_IN_PAIR(fst_in_pair, str_end);
|
||||
#undef PUT_FST_IN_PAIR
|
||||
if (hasnul) {
|
||||
typval_T obj;
|
||||
list_T *const list = tv_list_alloc();
|
||||
list->lv_refcount++;
|
||||
create_special_dict(&obj, kMPString, ((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
if (encode_list_write((void *) list, str, (size_t) (str_end - str))
|
||||
== -1) {
|
||||
tv_clear(&obj);
|
||||
goto parse_json_string_fail;
|
||||
}
|
||||
xfree(str);
|
||||
POP(obj, true);
|
||||
} else {
|
||||
*str_end = NUL;
|
||||
POP(((typval_T) {
|
||||
.v_type = VAR_STRING,
|
||||
.vval = { .v_string = (char_u *) str },
|
||||
}), false);
|
||||
*str_end = NUL;
|
||||
typval_T obj = decode_string(
|
||||
str, (size_t)(str_end - str), hasnul ? kTrue : kFalse, false, true);
|
||||
if (obj.v_type == VAR_UNKNOWN) {
|
||||
goto parse_json_string_fail;
|
||||
}
|
||||
POP(obj, obj.v_type != VAR_STRING);
|
||||
goto parse_json_string_ret;
|
||||
parse_json_string_fail:
|
||||
ret = FAIL;
|
||||
|
@ -812,13 +869,7 @@ json_decode_string_cycle_start:
|
|||
list_T *val_list = NULL;
|
||||
if (next_map_special) {
|
||||
next_map_special = false;
|
||||
val_list = tv_list_alloc();
|
||||
val_list->lv_refcount++;
|
||||
create_special_dict(&tv, kMPMap, ((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_list = val_list },
|
||||
}));
|
||||
val_list = decode_create_map_special_dict(&tv);
|
||||
} else {
|
||||
dict_T *dict = tv_dict_alloc();
|
||||
dict->dv_refcount++;
|
||||
|
@ -971,37 +1022,17 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
|
|||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_STR: {
|
||||
list_T *const list = tv_list_alloc();
|
||||
list->lv_refcount++;
|
||||
create_special_dict(rettv, kMPString, ((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
|
||||
== -1) {
|
||||
*rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kTrue, false,
|
||||
false);
|
||||
if (rettv->v_type == VAR_UNKNOWN) {
|
||||
return FAIL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_BIN: {
|
||||
if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
|
||||
*rettv = (typval_T) {
|
||||
.v_type = VAR_STRING,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
|
||||
};
|
||||
break;
|
||||
}
|
||||
list_T *const list = tv_list_alloc();
|
||||
list->lv_refcount++;
|
||||
create_special_dict(rettv, kMPBinary, ((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
|
||||
== -1) {
|
||||
*rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kNone, true,
|
||||
false);
|
||||
if (rettv->v_type == VAR_UNKNOWN) {
|
||||
return FAIL;
|
||||
}
|
||||
break;
|
||||
|
@ -1058,13 +1089,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
|
|||
}
|
||||
break;
|
||||
msgpack_to_vim_generic_map: {}
|
||||
list_T *const list = tv_list_alloc();
|
||||
list->lv_refcount++;
|
||||
create_special_dict(rettv, kMPMap, ((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
list_T *const list = decode_create_map_special_dict(rettv);
|
||||
for (size_t i = 0; i < mobj.via.map.size; i++) {
|
||||
list_T *const kv_pair = tv_list_alloc();
|
||||
tv_list_append_list(list, kv_pair);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <msgpack.h>
|
||||
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/globals.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "eval/decode.h.generated.h"
|
||||
|
|
|
@ -233,10 +233,6 @@
|
|||
///
|
||||
/// This name will only be used by one of the above macros which are defined by
|
||||
/// the caller. Functions defined here do not use first argument directly.
|
||||
#ifndef NVIM_EVAL_TYPVAL_ENCODE_C_H
|
||||
#define NVIM_EVAL_TYPVAL_ENCODE_C_H
|
||||
#undef NVIM_EVAL_TYPVAL_ENCODE_C_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
@ -816,4 +812,3 @@ encode_vim_to__error_ret:
|
|||
// Prevent “unused label” warnings.
|
||||
goto typval_encode_stop_converting_one_item; // -V779
|
||||
}
|
||||
#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H
|
||||
|
|
|
@ -1548,19 +1548,19 @@ return {
|
|||
command='lua',
|
||||
flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
|
||||
addr_type=ADDR_LINES,
|
||||
func='ex_script_ni',
|
||||
func='ex_lua',
|
||||
},
|
||||
{
|
||||
command='luado',
|
||||
flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
|
||||
addr_type=ADDR_LINES,
|
||||
func='ex_ni',
|
||||
func='ex_luado',
|
||||
},
|
||||
{
|
||||
command='luafile',
|
||||
flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
|
||||
addr_type=ADDR_LINES,
|
||||
func='ex_ni',
|
||||
func='ex_luafile',
|
||||
},
|
||||
{
|
||||
command='lvimgrep',
|
||||
|
|
|
@ -3705,19 +3705,18 @@ char_u *get_locales(expand_T *xp, int idx)
|
|||
|
||||
static void script_host_execute(char *name, exarg_T *eap)
|
||||
{
|
||||
uint8_t *script = script_get(eap, eap->arg);
|
||||
size_t len;
|
||||
char *const script = script_get(eap, &len);
|
||||
|
||||
if (!eap->skip) {
|
||||
list_T *args = tv_list_alloc();
|
||||
if (script != NULL) {
|
||||
list_T *const args = tv_list_alloc();
|
||||
// script
|
||||
tv_list_append_string(args, (const char *)(script ? script : eap->arg), -1);
|
||||
tv_list_append_allocated_string(args, script);
|
||||
// current range
|
||||
tv_list_append_number(args, (int)eap->line1);
|
||||
tv_list_append_number(args, (int)eap->line2);
|
||||
(void)eval_call_provider(name, "execute", args);
|
||||
}
|
||||
|
||||
xfree(script);
|
||||
}
|
||||
|
||||
static void script_host_execute_file(char *name, exarg_T *eap)
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
#include "nvim/event/rstream.h"
|
||||
#include "nvim/event/wstream.h"
|
||||
#include "nvim/shada.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/globals.h"
|
||||
|
||||
static int quitmore = 0;
|
||||
|
@ -3807,10 +3808,12 @@ void ex_ni(exarg_T *eap)
|
|||
/// Skips over ":perl <<EOF" constructs.
|
||||
static void ex_script_ni(exarg_T *eap)
|
||||
{
|
||||
if (!eap->skip)
|
||||
if (!eap->skip) {
|
||||
ex_ni(eap);
|
||||
else
|
||||
xfree(script_get(eap, eap->arg));
|
||||
} else {
|
||||
size_t len;
|
||||
xfree(script_get(eap, &len));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5816,10 +5819,10 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt,
|
|||
* copied to allocated memory and stored in "*compl_arg".
|
||||
* Returns FAIL if something is wrong.
|
||||
*/
|
||||
int parse_compl_arg(char_u *value, int vallen, int *complp,
|
||||
int parse_compl_arg(const char_u *value, int vallen, int *complp,
|
||||
uint32_t *argt, char_u **compl_arg)
|
||||
{
|
||||
char_u *arg = NULL;
|
||||
const char_u *arg = NULL;
|
||||
size_t arglen = 0;
|
||||
int i;
|
||||
int valend = vallen;
|
||||
|
|
|
@ -5380,47 +5380,61 @@ static int ex_window(void)
|
|||
return cmdwin_result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used for commands that either take a simple command string argument, or:
|
||||
* cmd << endmarker
|
||||
* {script}
|
||||
* endmarker
|
||||
* Returns a pointer to allocated memory with {script} or NULL.
|
||||
*/
|
||||
char_u *script_get(exarg_T *eap, char_u *cmd)
|
||||
/// Get script string
|
||||
///
|
||||
/// Used for commands which accept either `:command script` or
|
||||
///
|
||||
/// :command << endmarker
|
||||
/// script
|
||||
/// endmarker
|
||||
///
|
||||
/// @param eap Command being run.
|
||||
/// @param[out] lenp Location where length of resulting string is saved. Will
|
||||
/// be set to zero when skipping.
|
||||
///
|
||||
/// @return [allocated] NULL or script. Does not show any error messages.
|
||||
/// NULL is returned when skipping and on error.
|
||||
char *script_get(exarg_T *const eap, size_t *const lenp)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
|
||||
{
|
||||
char_u *theline;
|
||||
char *end_pattern = NULL;
|
||||
char dot[] = ".";
|
||||
garray_T ga;
|
||||
const char *const cmd = (const char *)eap->arg;
|
||||
|
||||
if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL)
|
||||
return NULL;
|
||||
if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) {
|
||||
*lenp = STRLEN(eap->arg);
|
||||
return xmemdupz(eap->arg, *lenp);
|
||||
}
|
||||
|
||||
ga_init(&ga, 1, 0x400);
|
||||
garray_T ga = { .ga_data = NULL, .ga_len = 0 };
|
||||
if (!eap->skip) {
|
||||
ga_init(&ga, 1, 0x400);
|
||||
}
|
||||
|
||||
if (cmd[2] != NUL)
|
||||
end_pattern = (char *)skipwhite(cmd + 2);
|
||||
else
|
||||
end_pattern = dot;
|
||||
|
||||
for (;; ) {
|
||||
theline = eap->getline(
|
||||
const char *const end_pattern = (
|
||||
cmd[2] != NUL
|
||||
? (const char *)skipwhite((const char_u *)cmd + 2)
|
||||
: ".");
|
||||
for (;;) {
|
||||
char *const theline = (char *)eap->getline(
|
||||
eap->cstack->cs_looplevel > 0 ? -1 :
|
||||
NUL, eap->cookie, 0);
|
||||
|
||||
if (theline == NULL || STRCMP(end_pattern, theline) == 0) {
|
||||
if (theline == NULL || strcmp(end_pattern, theline) == 0) {
|
||||
xfree(theline);
|
||||
break;
|
||||
}
|
||||
|
||||
ga_concat(&ga, theline);
|
||||
ga_append(&ga, '\n');
|
||||
if (!eap->skip) {
|
||||
ga_concat(&ga, (const char_u *)theline);
|
||||
ga_append(&ga, '\n');
|
||||
}
|
||||
xfree(theline);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
*lenp = (size_t)ga.ga_len; // Set length without trailing NUL.
|
||||
if (!eap->skip) {
|
||||
ga_append(&ga, NUL);
|
||||
}
|
||||
|
||||
return (char_u *)ga.ga_data;
|
||||
return (char *)ga.ga_data;
|
||||
}
|
||||
|
||||
/// Iterate over history items
|
||||
|
|
|
@ -41,10 +41,6 @@
|
|||
// $ gcc -E -dM - </dev/null
|
||||
// $ echo | clang -dM -E -
|
||||
|
||||
#ifndef NVIM_FUNC_ATTR_H
|
||||
#define NVIM_FUNC_ATTR_H
|
||||
#undef NVIM_FUNC_ATTR_H
|
||||
|
||||
#ifdef FUNC_ATTR_MALLOC
|
||||
# undef FUNC_ATTR_MALLOC
|
||||
#endif
|
||||
|
@ -214,4 +210,3 @@
|
|||
# define FUNC_ATTR_NONNULL_ARG(...)
|
||||
# define FUNC_ATTR_NONNULL_RET
|
||||
#endif
|
||||
#endif // NVIM_FUNC_ATTR_H
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
|||
#ifndef NVIM_LUA_CONVERTER_H
|
||||
#define NVIM_LUA_CONVERTER_H
|
||||
|
||||
#include <lua.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/eval.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "lua/converter.h.generated.h"
|
||||
#endif
|
||||
#endif // NVIM_LUA_CONVERTER_H
|
|
@ -0,0 +1,576 @@
|
|||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "nvim/misc1.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/garray.h"
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/vim.h"
|
||||
#include "nvim/vim.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/message.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/macros.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/cursor.h"
|
||||
#include "nvim/undo.h"
|
||||
#include "nvim/ascii.h"
|
||||
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/lua/converter.h"
|
||||
|
||||
typedef struct {
|
||||
Error err;
|
||||
String lua_err_str;
|
||||
} LuaError;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "lua/vim_module.generated.h"
|
||||
# include "lua/executor.c.generated.h"
|
||||
#endif
|
||||
|
||||
/// Name of the run code for use in messages
|
||||
#define NLUA_EVAL_NAME "<VimL compiled string>"
|
||||
|
||||
/// Call C function which does not expect any arguments
|
||||
///
|
||||
/// @param function Called function
|
||||
/// @param numret Number of returned arguments
|
||||
#define NLUA_CALL_C_FUNCTION_0(lstate, function, numret) \
|
||||
do { \
|
||||
lua_pushcfunction(lstate, &function); \
|
||||
lua_call(lstate, 0, numret); \
|
||||
} while (0)
|
||||
/// Call C function which expects one argument
|
||||
///
|
||||
/// @param function Called function
|
||||
/// @param numret Number of returned arguments
|
||||
/// @param a… Supplied argument (should be a void* pointer)
|
||||
#define NLUA_CALL_C_FUNCTION_1(lstate, function, numret, a1) \
|
||||
do { \
|
||||
lua_pushcfunction(lstate, &function); \
|
||||
lua_pushlightuserdata(lstate, a1); \
|
||||
lua_call(lstate, 1, numret); \
|
||||
} while (0)
|
||||
/// Call C function which expects two arguments
|
||||
///
|
||||
/// @param function Called function
|
||||
/// @param numret Number of returned arguments
|
||||
/// @param a… Supplied argument (should be a void* pointer)
|
||||
#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \
|
||||
do { \
|
||||
lua_pushcfunction(lstate, &function); \
|
||||
lua_pushlightuserdata(lstate, a1); \
|
||||
lua_pushlightuserdata(lstate, a2); \
|
||||
lua_call(lstate, 2, numret); \
|
||||
} while (0)
|
||||
/// Call C function which expects three arguments
|
||||
///
|
||||
/// @param function Called function
|
||||
/// @param numret Number of returned arguments
|
||||
/// @param a… Supplied argument (should be a void* pointer)
|
||||
#define NLUA_CALL_C_FUNCTION_3(lstate, function, numret, a1, a2, a3) \
|
||||
do { \
|
||||
lua_pushcfunction(lstate, &function); \
|
||||
lua_pushlightuserdata(lstate, a1); \
|
||||
lua_pushlightuserdata(lstate, a2); \
|
||||
lua_pushlightuserdata(lstate, a3); \
|
||||
lua_call(lstate, 3, numret); \
|
||||
} while (0)
|
||||
/// Call C function which expects five arguments
|
||||
///
|
||||
/// @param function Called function
|
||||
/// @param numret Number of returned arguments
|
||||
/// @param a… Supplied argument (should be a void* pointer)
|
||||
#define NLUA_CALL_C_FUNCTION_4(lstate, function, numret, a1, a2, a3, a4) \
|
||||
do { \
|
||||
lua_pushcfunction(lstate, &function); \
|
||||
lua_pushlightuserdata(lstate, a1); \
|
||||
lua_pushlightuserdata(lstate, a2); \
|
||||
lua_pushlightuserdata(lstate, a3); \
|
||||
lua_pushlightuserdata(lstate, a4); \
|
||||
lua_call(lstate, 4, numret); \
|
||||
} while (0)
|
||||
|
||||
/// Convert lua error into a Vim error message
|
||||
///
|
||||
/// @param lstate Lua interpreter state.
|
||||
/// @param[in] msg Message base, must contain one `%s`.
|
||||
static void nlua_error(lua_State *const lstate, const char *const msg)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
size_t len;
|
||||
const char *const str = lua_tolstring(lstate, -1, &len);
|
||||
|
||||
emsgf(msg, (int)len, str);
|
||||
|
||||
lua_pop(lstate, 1);
|
||||
}
|
||||
|
||||
/// Compare two strings, ignoring case
|
||||
///
|
||||
/// Expects two values on the stack: compared strings. Returns one of the
|
||||
/// following numbers: 0, -1 or 1.
|
||||
///
|
||||
/// Does no error handling: never call it with non-string or with some arguments
|
||||
/// omitted.
|
||||
static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char *s1 = luaL_checklstring(lstate, 1, NULL);
|
||||
const char *s2 = luaL_checklstring(lstate, 2, NULL);
|
||||
const int ret = STRICMP(s1, s2);
|
||||
lua_pop(lstate, 2);
|
||||
lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Evaluate lua string
|
||||
///
|
||||
/// Expects two values on the stack: string to evaluate, pointer to the
|
||||
/// location where result is saved. Always returns nothing (from the lua point
|
||||
/// of view).
|
||||
static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const String *const str = (const String *)lua_touserdata(lstate, 1);
|
||||
typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2);
|
||||
lua_pop(lstate, 2);
|
||||
|
||||
if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) {
|
||||
nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s"));
|
||||
return 0;
|
||||
}
|
||||
if (lua_pcall(lstate, 0, 1, 0)) {
|
||||
nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s"));
|
||||
return 0;
|
||||
}
|
||||
if (!nlua_pop_typval(lstate, ret_tv)) {
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Evaluate lua string for each line in range
|
||||
///
|
||||
/// Expects two values on the stack: string to evaluate and pointer to integer
|
||||
/// array with line range. Always returns nothing (from the lua point of view).
|
||||
static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const String *const str = (const String *)lua_touserdata(lstate, 1);
|
||||
const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 2);
|
||||
lua_pop(lstate, 2);
|
||||
|
||||
#define DOSTART "return function(line, linenr) "
|
||||
#define DOEND " end"
|
||||
const size_t lcmd_len = (str->size
|
||||
+ (sizeof(DOSTART) - 1)
|
||||
+ (sizeof(DOEND) - 1));
|
||||
char *lcmd;
|
||||
if (lcmd_len < IOSIZE) {
|
||||
lcmd = (char *)IObuff;
|
||||
} else {
|
||||
lcmd = xmalloc(lcmd_len + 1);
|
||||
}
|
||||
memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1);
|
||||
memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size);
|
||||
memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, DOEND, sizeof(DOEND) - 1);
|
||||
#undef DOSTART
|
||||
#undef DOEND
|
||||
|
||||
if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
|
||||
nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s"));
|
||||
if (lcmd_len >= IOSIZE) {
|
||||
xfree(lcmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (lcmd_len >= IOSIZE) {
|
||||
xfree(lcmd);
|
||||
}
|
||||
if (lua_pcall(lstate, 0, 1, 0)) {
|
||||
nlua_error(lstate, _("E5110: Error while creating lua function: %.*s"));
|
||||
return 0;
|
||||
}
|
||||
for (linenr_T l = range[0]; l <= range[1]; l++) {
|
||||
if (l > curbuf->b_ml.ml_line_count) {
|
||||
break;
|
||||
}
|
||||
lua_pushvalue(lstate, -1);
|
||||
lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false));
|
||||
lua_pushnumber(lstate, (lua_Number)l);
|
||||
if (lua_pcall(lstate, 2, 1, 0)) {
|
||||
nlua_error(lstate, _("E5111: Error while calling lua function: %.*s"));
|
||||
break;
|
||||
}
|
||||
if (lua_isstring(lstate, -1)) {
|
||||
size_t new_line_len;
|
||||
const char *const new_line = lua_tolstring(lstate, -1, &new_line_len);
|
||||
char *const new_line_transformed = xmemdupz(new_line, new_line_len);
|
||||
for (size_t i = 0; i < new_line_len; i++) {
|
||||
if (new_line_transformed[i] == NUL) {
|
||||
new_line_transformed[i] = '\n';
|
||||
}
|
||||
}
|
||||
ml_replace(l, (char_u *)new_line_transformed, false);
|
||||
changed_bytes(l, 0);
|
||||
}
|
||||
lua_pop(lstate, 1);
|
||||
}
|
||||
lua_pop(lstate, 1);
|
||||
check_cursor();
|
||||
update_screen(NOT_VALID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Evaluate lua file
|
||||
///
|
||||
/// Expects one value on the stack: file to evaluate. Always returns nothing
|
||||
/// (from the lua point of view).
|
||||
static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char *const filename = (const char *)lua_touserdata(lstate, 1);
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
if (luaL_loadfile(lstate, filename)) {
|
||||
nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s"));
|
||||
return 0;
|
||||
}
|
||||
if (lua_pcall(lstate, 0, 0, 0)) {
|
||||
nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s"));
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Initialize lua interpreter state
|
||||
///
|
||||
/// Called by lua interpreter itself to initialize state.
|
||||
static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// stricmp
|
||||
lua_pushcfunction(lstate, &nlua_stricmp);
|
||||
lua_setglobal(lstate, "stricmp");
|
||||
|
||||
// print
|
||||
lua_pushcfunction(lstate, &nlua_print);
|
||||
lua_setglobal(lstate, "print");
|
||||
|
||||
// debug.debug
|
||||
lua_getglobal(lstate, "debug");
|
||||
lua_pushcfunction(lstate, &nlua_debug);
|
||||
lua_setfield(lstate, -2, "debug");
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
// vim
|
||||
if (luaL_dostring(lstate, (char *)&vim_module[0])) {
|
||||
nlua_error(lstate, _("E5106: Error while creating vim module: %.*s"));
|
||||
return 1;
|
||||
}
|
||||
// vim.api
|
||||
nlua_add_api_functions(lstate);
|
||||
// vim.types, vim.type_idx, vim.val_idx
|
||||
nlua_init_types(lstate);
|
||||
lua_setglobal(lstate, "vim");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Initialize lua interpreter
|
||||
///
|
||||
/// Crashes NeoVim if initialization fails. Should be called once per lua
|
||||
/// interpreter instance.
|
||||
static lua_State *init_lua(void)
|
||||
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
lua_State *lstate = luaL_newstate();
|
||||
if (lstate == NULL) {
|
||||
EMSG(_("E970: Failed to initialize lua interpreter"));
|
||||
preserve_exit();
|
||||
}
|
||||
luaL_openlibs(lstate);
|
||||
NLUA_CALL_C_FUNCTION_0(lstate, nlua_state_init, 0);
|
||||
return lstate;
|
||||
}
|
||||
|
||||
static lua_State *global_lstate = NULL;
|
||||
|
||||
/// Execute lua string
|
||||
///
|
||||
/// @param[in] str String to execute.
|
||||
/// @param[out] ret_tv Location where result will be saved.
|
||||
///
|
||||
/// @return Result of the execution.
|
||||
void executor_exec_lua(const String str, typval_T *const ret_tv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (global_lstate == NULL) {
|
||||
global_lstate = init_lua();
|
||||
}
|
||||
|
||||
NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0,
|
||||
(void *)&str, ret_tv);
|
||||
}
|
||||
|
||||
/// Evaluate lua string
|
||||
///
|
||||
/// Used for luaeval(). Expects three values on the stack:
|
||||
///
|
||||
/// 1. String to evaluate.
|
||||
/// 2. _A value.
|
||||
/// 3. Pointer to location where result is saved.
|
||||
///
|
||||
/// @param[in,out] lstate Lua interpreter state.
|
||||
static int nlua_eval_lua_string(lua_State *const lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const String *const str = (const String *)lua_touserdata(lstate, 1);
|
||||
typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2);
|
||||
typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3);
|
||||
lua_pop(lstate, 3);
|
||||
|
||||
garray_T str_ga;
|
||||
ga_init(&str_ga, 1, 80);
|
||||
#define EVALHEADER "local _A=select(1,...) return ("
|
||||
const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1;
|
||||
char *lcmd;
|
||||
if (lcmd_len < IOSIZE) {
|
||||
lcmd = (char *)IObuff;
|
||||
} else {
|
||||
lcmd = xmalloc(lcmd_len);
|
||||
}
|
||||
memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1);
|
||||
memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size);
|
||||
lcmd[lcmd_len - 1] = ')';
|
||||
#undef EVALHEADER
|
||||
if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
|
||||
nlua_error(lstate,
|
||||
_("E5107: Error while creating lua chunk for luaeval(): %.*s"));
|
||||
if (lcmd != (char *)IObuff) {
|
||||
xfree(lcmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (lcmd != (char *)IObuff) {
|
||||
xfree(lcmd);
|
||||
}
|
||||
|
||||
if (arg == NULL || arg->v_type == VAR_UNKNOWN) {
|
||||
lua_pushnil(lstate);
|
||||
} else {
|
||||
nlua_push_typval(lstate, arg);
|
||||
}
|
||||
if (lua_pcall(lstate, 1, 1, 0)) {
|
||||
nlua_error(lstate,
|
||||
_("E5108: Error while calling lua chunk for luaeval(): %.*s"));
|
||||
return 0;
|
||||
}
|
||||
if (!nlua_pop_typval(lstate, ret_tv)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Print as a Vim message
|
||||
///
|
||||
/// @param lstate Lua interpreter state.
|
||||
static int nlua_print(lua_State *const lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
#define PRINT_ERROR(msg) \
|
||||
do { \
|
||||
errmsg = msg; \
|
||||
errmsg_len = sizeof(msg) - 1; \
|
||||
goto nlua_print_error; \
|
||||
} while (0)
|
||||
const int nargs = lua_gettop(lstate);
|
||||
lua_getglobal(lstate, "tostring");
|
||||
const char *errmsg = NULL;
|
||||
size_t errmsg_len = 0;
|
||||
garray_T msg_ga;
|
||||
ga_init(&msg_ga, 1, 80);
|
||||
int curargidx = 1;
|
||||
for (; curargidx <= nargs; curargidx++) {
|
||||
lua_pushvalue(lstate, -1); // tostring
|
||||
lua_pushvalue(lstate, curargidx); // arg
|
||||
if (lua_pcall(lstate, 1, 1, 0)) {
|
||||
errmsg = lua_tolstring(lstate, -1, &errmsg_len);
|
||||
goto nlua_print_error;
|
||||
}
|
||||
size_t len;
|
||||
const char *const s = lua_tolstring(lstate, -1, &len);
|
||||
if (s == NULL) {
|
||||
PRINT_ERROR(
|
||||
"<Unknown error: lua_tolstring returned NULL for tostring result>");
|
||||
}
|
||||
ga_concat_len(&msg_ga, s, len);
|
||||
if (curargidx < nargs) {
|
||||
ga_append(&msg_ga, ' ');
|
||||
}
|
||||
lua_pop(lstate, 1);
|
||||
}
|
||||
#undef PRINT_ERROR
|
||||
lua_pop(lstate, nargs + 1);
|
||||
ga_append(&msg_ga, NUL);
|
||||
{
|
||||
const size_t len = (size_t)msg_ga.ga_len - 1;
|
||||
char *const str = (char *)msg_ga.ga_data;
|
||||
|
||||
for (size_t i = 0; i < len;) {
|
||||
const size_t start = i;
|
||||
while (i < len) {
|
||||
switch (str[i]) {
|
||||
case NUL: {
|
||||
str[i] = NL;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
case NL: {
|
||||
str[i] = NUL;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
msg((char_u *)str + start);
|
||||
}
|
||||
if (str[len - 1] == NUL) { // Last was newline
|
||||
msg((char_u *)"");
|
||||
}
|
||||
}
|
||||
ga_clear(&msg_ga);
|
||||
return 0;
|
||||
nlua_print_error:
|
||||
emsgf(_("E5114: Error while converting print argument #%i: %.*s"),
|
||||
curargidx, errmsg_len, errmsg);
|
||||
ga_clear(&msg_ga);
|
||||
lua_pop(lstate, lua_gettop(lstate));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// debug.debug implementation: interaction with user while debugging
|
||||
///
|
||||
/// @param lstate Lua interpreter state.
|
||||
int nlua_debug(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const typval_T input_args[] = {
|
||||
{
|
||||
.v_lock = VAR_FIXED,
|
||||
.v_type = VAR_STRING,
|
||||
.vval.v_string = (char_u *)"lua_debug> ",
|
||||
},
|
||||
{
|
||||
.v_type = VAR_UNKNOWN,
|
||||
},
|
||||
};
|
||||
for (;;) {
|
||||
lua_settop(lstate, 0);
|
||||
typval_T input;
|
||||
get_user_input(input_args, &input, false);
|
||||
msg_putchar('\n'); // Avoid outputting on input line.
|
||||
if (input.v_type != VAR_STRING
|
||||
|| input.vval.v_string == NULL
|
||||
|| *input.vval.v_string == NUL
|
||||
|| STRCMP(input.vval.v_string, "cont") == 0) {
|
||||
tv_clear(&input);
|
||||
return 0;
|
||||
}
|
||||
if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string,
|
||||
STRLEN(input.vval.v_string), "=(debug command)")) {
|
||||
nlua_error(lstate, _("E5115: Error while loading debug string: %.*s"));
|
||||
}
|
||||
tv_clear(&input);
|
||||
if (lua_pcall(lstate, 0, 0, 0)) {
|
||||
nlua_error(lstate, _("E5116: Error while calling debug string: %.*s"));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Evaluate lua string
|
||||
///
|
||||
/// Used for luaeval().
|
||||
///
|
||||
/// @param[in] str String to execute.
|
||||
/// @param[in] arg Second argument to `luaeval()`.
|
||||
/// @param[out] ret_tv Location where result will be saved.
|
||||
///
|
||||
/// @return Result of the execution.
|
||||
void executor_eval_lua(const String str, typval_T *const arg,
|
||||
typval_T *const ret_tv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (global_lstate == NULL) {
|
||||
global_lstate = init_lua();
|
||||
}
|
||||
|
||||
NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0,
|
||||
(void *)&str, arg, ret_tv);
|
||||
}
|
||||
|
||||
/// Run lua string
|
||||
///
|
||||
/// Used for :lua.
|
||||
///
|
||||
/// @param eap VimL command being run.
|
||||
void ex_lua(exarg_T *const eap)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
size_t len;
|
||||
char *const code = script_get(eap, &len);
|
||||
if (eap->skip) {
|
||||
xfree(code);
|
||||
return;
|
||||
}
|
||||
typval_T tv = { .v_type = VAR_UNKNOWN };
|
||||
executor_exec_lua((String) { .data = code, .size = len }, &tv);
|
||||
tv_clear(&tv);
|
||||
xfree(code);
|
||||
}
|
||||
|
||||
/// Run lua string for each line in range
|
||||
///
|
||||
/// Used for :luado.
|
||||
///
|
||||
/// @param eap VimL command being run.
|
||||
void ex_luado(exarg_T *const eap)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (global_lstate == NULL) {
|
||||
global_lstate = init_lua();
|
||||
}
|
||||
if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) {
|
||||
EMSG(_("cannot save undo information"));
|
||||
return;
|
||||
}
|
||||
const String cmd = {
|
||||
.size = STRLEN(eap->arg),
|
||||
.data = (char *)eap->arg,
|
||||
};
|
||||
const linenr_T range[] = { eap->line1, eap->line2 };
|
||||
NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0,
|
||||
(void *)&cmd, (void *)range);
|
||||
}
|
||||
|
||||
/// Run lua file
|
||||
///
|
||||
/// Used for :luafile.
|
||||
///
|
||||
/// @param eap VimL command being run.
|
||||
void ex_luafile(exarg_T *const eap)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (global_lstate == NULL) {
|
||||
global_lstate = init_lua();
|
||||
}
|
||||
NLUA_CALL_C_FUNCTION_1(global_lstate, nlua_exec_lua_file, 0,
|
||||
(void *)eap->arg);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef NVIM_LUA_EXECUTOR_H
|
||||
#define NVIM_LUA_EXECUTOR_H
|
||||
|
||||
#include <lua.h>
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
|
||||
// Generated by msgpack-gen.lua
|
||||
void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
#define set_api_error(s, err) \
|
||||
do { \
|
||||
Error *err_ = (err); \
|
||||
err_->type = kErrorTypeException; \
|
||||
err_->set = true; \
|
||||
memcpy(&err_->msg[0], s, sizeof(s)); \
|
||||
} while (0)
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "lua/executor.h.generated.h"
|
||||
#endif
|
||||
#endif // NVIM_LUA_EXECUTOR_H
|
|
@ -0,0 +1,2 @@
|
|||
-- TODO(ZyX-I): Create compatibility layer.
|
||||
return {}
|
|
@ -24,12 +24,12 @@ static msgpack_zone zone;
|
|||
static msgpack_sbuffer sbuffer;
|
||||
|
||||
#define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \
|
||||
bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \
|
||||
Integer *const arg) \
|
||||
FUNC_ATTR_NONNULL_ALL \
|
||||
static bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \
|
||||
Integer *const arg) \
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
|
||||
{ \
|
||||
if (obj->type != MSGPACK_OBJECT_EXT \
|
||||
|| obj->via.ext.type != kObjectType##t) { \
|
||||
|| obj->via.ext.type + EXT_OBJECT_TYPE_SHIFT != kObjectType##t) { \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
|
@ -48,13 +48,14 @@ static msgpack_sbuffer sbuffer;
|
|||
return true; \
|
||||
} \
|
||||
\
|
||||
void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \
|
||||
static void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \
|
||||
FUNC_ATTR_NONNULL_ARG(2) \
|
||||
{ \
|
||||
msgpack_packer pac; \
|
||||
msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \
|
||||
msgpack_pack_int64(&pac, (handle_T)o); \
|
||||
msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \
|
||||
msgpack_pack_ext(res, sbuffer.size, \
|
||||
kObjectType##t - EXT_OBJECT_TYPE_SHIFT); \
|
||||
msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \
|
||||
msgpack_sbuffer_clear(&sbuffer); \
|
||||
}
|
||||
|
@ -126,7 +127,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
|
|||
{
|
||||
STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64),
|
||||
"Msgpack floating-point size does not match API integer");
|
||||
*cur.aobj = FLOATING_OBJ(cur.mobj->via.f64);
|
||||
*cur.aobj = FLOAT_OBJ(cur.mobj->via.f64);
|
||||
break;
|
||||
}
|
||||
#define STR_CASE(type, attr, obj, dest, conv) \
|
||||
|
@ -225,7 +226,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
|
|||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_EXT: {
|
||||
switch (cur.mobj->via.ext.type) {
|
||||
switch ((ObjectType)(cur.mobj->via.ext.type + EXT_OBJECT_TYPE_SHIFT)) {
|
||||
case kObjectTypeBuffer: {
|
||||
cur.aobj->type = kObjectTypeBuffer;
|
||||
ret = msgpack_rpc_to_buffer(cur.mobj, &cur.aobj->data.integer);
|
||||
|
@ -241,6 +242,15 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
|
|||
ret = msgpack_rpc_to_tabpage(cur.mobj, &cur.aobj->data.integer);
|
||||
break;
|
||||
}
|
||||
case kObjectTypeNil:
|
||||
case kObjectTypeBoolean:
|
||||
case kObjectTypeInteger:
|
||||
case kObjectTypeFloat:
|
||||
case kObjectTypeString:
|
||||
case kObjectTypeArray:
|
||||
case kObjectTypeDictionary: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -364,6 +374,9 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res)
|
|||
kv_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 }));
|
||||
while (kv_size(stack)) {
|
||||
APIToMPObjectStackItem cur = kv_last(stack);
|
||||
STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1
|
||||
&& kObjectTypeTabpage == kObjectTypeWindow + 1,
|
||||
"Buffer, window and tabpage enum items are in order");
|
||||
switch (cur.aobj->type) {
|
||||
case kObjectTypeNil: {
|
||||
msgpack_pack_nil(res);
|
||||
|
|
|
@ -9,6 +9,13 @@
|
|||
#include "nvim/event/wstream.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
|
||||
/// Value by which objects represented as EXT type are shifted
|
||||
///
|
||||
/// Subtracted when packing, added when unpacking. Used to allow moving
|
||||
/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be
|
||||
/// split or reordered.
|
||||
#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "msgpack_rpc/helpers.h.generated.h"
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@ local insert = helpers.insert
|
|||
local NIL = helpers.NIL
|
||||
local meth_pcall = helpers.meth_pcall
|
||||
local command = helpers.command
|
||||
local bufmeths = helpers.bufmeths
|
||||
|
||||
describe('api/buf', function()
|
||||
before_each(clear)
|
||||
|
@ -121,6 +122,15 @@ describe('api/buf', function()
|
|||
local get_lines, set_lines = curbufmeths.get_lines, curbufmeths.set_lines
|
||||
local line_count = curbufmeths.line_count
|
||||
|
||||
it('fails correctly when input is not valid', function()
|
||||
eq(1, curbufmeths.get_number())
|
||||
local err, emsg = pcall(bufmeths.set_lines, 1, 1, 2, false, {'b\na'})
|
||||
eq(false, err)
|
||||
local exp_emsg = 'String cannot contain newlines'
|
||||
-- Expected {filename}:{lnum}: {exp_emsg}
|
||||
eq(': ' .. exp_emsg, emsg:sub(-#exp_emsg - 2))
|
||||
end)
|
||||
|
||||
it('has correct line_count when inserting and deleting', function()
|
||||
eq(1, line_count())
|
||||
set_lines(-1, -1, true, {'line'})
|
||||
|
|
|
@ -337,6 +337,17 @@ describe('api', function()
|
|||
eq('\128\253\44', helpers.nvim('replace_termcodes',
|
||||
'<LeftMouse>', true, true, true))
|
||||
end)
|
||||
|
||||
it('does not crash when transforming an empty string', function()
|
||||
-- Actually does not test anything, because current code will use NULL for
|
||||
-- an empty string.
|
||||
--
|
||||
-- Problem here is that if String argument has .data in allocated memory
|
||||
-- then `return str` in vim_replace_termcodes body will make Neovim free
|
||||
-- `str.data` twice: once when freeing arguments, then when freeing return
|
||||
-- value.
|
||||
eq('', meths.replace_termcodes('', true, true, true))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('nvim_feedkeys', function()
|
||||
|
|
|
@ -9,6 +9,7 @@ local funcs = helpers.funcs
|
|||
local request = helpers.request
|
||||
local NIL = helpers.NIL
|
||||
local meth_pcall = helpers.meth_pcall
|
||||
local meths = helpers.meths
|
||||
local command = helpers.command
|
||||
|
||||
-- check if str is visible at the beginning of some line
|
||||
|
@ -55,6 +56,12 @@ describe('api/win', function()
|
|||
eq('typing\n some dumb text', curbuf_contents())
|
||||
end)
|
||||
|
||||
it('does not leak memory when using invalid window ID with invalid pos',
|
||||
function()
|
||||
eq({false, 'Invalid window id'},
|
||||
meth_pcall(meths.win_set_cursor, 1, {"b\na"}))
|
||||
end)
|
||||
|
||||
it('updates the screen, and also when the window is unfocused', function()
|
||||
insert("prologue")
|
||||
feed('100o<esc>')
|
||||
|
|
|
@ -566,7 +566,7 @@ local function get_pathsep()
|
|||
return funcs.fnamemodify('.', ':p'):sub(-1)
|
||||
end
|
||||
|
||||
local M = {
|
||||
local module = {
|
||||
prepend_argv = prepend_argv,
|
||||
clear = clear,
|
||||
connect = connect,
|
||||
|
@ -641,5 +641,5 @@ return function(after_each)
|
|||
check_cores('build/bin/nvim')
|
||||
end)
|
||||
end
|
||||
return M
|
||||
return module
|
||||
end
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
-- Test suite for testing interactions with API bindings
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
|
||||
local exc_exec = helpers.exc_exec
|
||||
local funcs = helpers.funcs
|
||||
local clear = helpers.clear
|
||||
local eval = helpers.eval
|
||||
local NIL = helpers.NIL
|
||||
local eq = helpers.eq
|
||||
|
||||
before_each(clear)
|
||||
|
||||
describe('luaeval(vim.api.…)', function()
|
||||
describe('with channel_id and buffer handle', function()
|
||||
describe('nvim_buf_get_lines', function()
|
||||
it('works', function()
|
||||
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
|
||||
eq({{_TYPE={}, _VAL={'a\nb'}}},
|
||||
funcs.luaeval('vim.api.nvim_buf_get_lines(1, 2, 3, false)'))
|
||||
end)
|
||||
end)
|
||||
describe('nvim_buf_set_lines', function()
|
||||
it('works', function()
|
||||
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
|
||||
eq(NIL, funcs.luaeval('vim.api.nvim_buf_set_lines(1, 1, 2, false, {"b\\0a"})'))
|
||||
eq({'abc', {_TYPE={}, _VAL={'b\na'}}, {_TYPE={}, _VAL={'a\nb'}}, 'ttt'},
|
||||
funcs.luaeval('vim.api.nvim_buf_get_lines(1, 0, 4, false)'))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('with errors', function()
|
||||
it('transforms API error from nvim_buf_set_lines into lua error', function()
|
||||
funcs.setline(1, {"abc", "def", "a\nb", "ttt"})
|
||||
eq({false, 'String cannot contain newlines'},
|
||||
funcs.luaeval('{pcall(vim.api.nvim_buf_set_lines, 1, 1, 2, false, {"b\\na"})}'))
|
||||
end)
|
||||
|
||||
it('transforms API error from nvim_win_set_cursor into lua error', function()
|
||||
eq({false, 'Argument "pos" must be a [row, col] array'},
|
||||
funcs.luaeval('{pcall(vim.api.nvim_win_set_cursor, 0, {1, 2, 3})}'))
|
||||
-- Used to produce a memory leak due to a bug in nvim_win_set_cursor
|
||||
eq({false, 'Invalid window id'},
|
||||
funcs.luaeval('{pcall(vim.api.nvim_win_set_cursor, -1, {1, 2, 3})}'))
|
||||
end)
|
||||
|
||||
it('transforms API error from nvim_win_set_cursor + same array as in first test into lua error',
|
||||
function()
|
||||
eq({false, 'Argument "pos" must be a [row, col] array'},
|
||||
funcs.luaeval('{pcall(vim.api.nvim_win_set_cursor, 0, {"b\\na"})}'))
|
||||
end)
|
||||
end)
|
||||
|
||||
it('correctly evaluates API code which calls luaeval', function()
|
||||
local str = (([===[vim.api.nvim_eval([==[
|
||||
luaeval('vim.api.nvim_eval([=[
|
||||
luaeval("vim.api.nvim_eval([[
|
||||
luaeval(1)
|
||||
]])")
|
||||
]=])')
|
||||
]==])]===]):gsub('\n', ' '))
|
||||
eq(1, funcs.luaeval(str))
|
||||
end)
|
||||
|
||||
it('correctly converts from API objects', function()
|
||||
eq(1, funcs.luaeval('vim.api.nvim_eval("1")'))
|
||||
eq('1', funcs.luaeval([[vim.api.nvim_eval('"1"')]]))
|
||||
eq({}, funcs.luaeval('vim.api.nvim_eval("[]")'))
|
||||
eq({}, funcs.luaeval('vim.api.nvim_eval("{}")'))
|
||||
eq(1, funcs.luaeval('vim.api.nvim_eval("1.0")'))
|
||||
eq(true, funcs.luaeval('vim.api.nvim_eval("v:true")'))
|
||||
eq(false, funcs.luaeval('vim.api.nvim_eval("v:false")'))
|
||||
eq(NIL, funcs.luaeval('vim.api.nvim_eval("v:null")'))
|
||||
|
||||
eq(0, eval([[type(luaeval('vim.api.nvim_eval("1")'))]]))
|
||||
eq(1, eval([[type(luaeval('vim.api.nvim_eval("''1''")'))]]))
|
||||
eq(3, eval([[type(luaeval('vim.api.nvim_eval("[]")'))]]))
|
||||
eq(4, eval([[type(luaeval('vim.api.nvim_eval("{}")'))]]))
|
||||
eq(5, eval([[type(luaeval('vim.api.nvim_eval("1.0")'))]]))
|
||||
eq(6, eval([[type(luaeval('vim.api.nvim_eval("v:true")'))]]))
|
||||
eq(6, eval([[type(luaeval('vim.api.nvim_eval("v:false")'))]]))
|
||||
eq(7, eval([[type(luaeval('vim.api.nvim_eval("v:null")'))]]))
|
||||
|
||||
eq({foo=42}, funcs.luaeval([[vim.api.nvim_eval('{"foo": 42}')]]))
|
||||
eq({42}, funcs.luaeval([[vim.api.nvim_eval('[42]')]]))
|
||||
|
||||
eq({foo={bar=42}, baz=50}, funcs.luaeval([[vim.api.nvim_eval('{"foo": {"bar": 42}, "baz": 50}')]]))
|
||||
eq({{42}, {}}, funcs.luaeval([=[vim.api.nvim_eval('[[42], []]')]=]))
|
||||
end)
|
||||
|
||||
it('correctly converts to API objects', function()
|
||||
eq(1, funcs.luaeval('vim.api.nvim__id(1)'))
|
||||
eq('1', funcs.luaeval('vim.api.nvim__id("1")'))
|
||||
eq({1}, funcs.luaeval('vim.api.nvim__id({1})'))
|
||||
eq({foo=1}, funcs.luaeval('vim.api.nvim__id({foo=1})'))
|
||||
eq(1.5, funcs.luaeval('vim.api.nvim__id(1.5)'))
|
||||
eq(true, funcs.luaeval('vim.api.nvim__id(true)'))
|
||||
eq(false, funcs.luaeval('vim.api.nvim__id(false)'))
|
||||
eq(NIL, funcs.luaeval('vim.api.nvim__id(nil)'))
|
||||
|
||||
eq(0, eval([[type(luaeval('vim.api.nvim__id(1)'))]]))
|
||||
eq(1, eval([[type(luaeval('vim.api.nvim__id("1")'))]]))
|
||||
eq(3, eval([[type(luaeval('vim.api.nvim__id({1})'))]]))
|
||||
eq(4, eval([[type(luaeval('vim.api.nvim__id({foo=1})'))]]))
|
||||
eq(5, eval([[type(luaeval('vim.api.nvim__id(1.5)'))]]))
|
||||
eq(6, eval([[type(luaeval('vim.api.nvim__id(true)'))]]))
|
||||
eq(6, eval([[type(luaeval('vim.api.nvim__id(false)'))]]))
|
||||
eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]]))
|
||||
|
||||
eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})'))
|
||||
end)
|
||||
|
||||
it('correctly converts container objects with type_idx to API objects', function()
|
||||
eq(5, eval('type(luaeval("vim.api.nvim__id({[vim.type_idx]=vim.types.float, [vim.val_idx]=0})"))'))
|
||||
eq(4, eval([[type(luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.dictionary})'))]]))
|
||||
eq(3, eval([[type(luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array})'))]]))
|
||||
|
||||
eq({}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array})'))
|
||||
|
||||
-- Presence of type_idx makes Vim ignore some keys
|
||||
eq({42}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
|
||||
eq({foo=2}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
|
||||
eq(10, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
|
||||
eq({}, funcs.luaeval('vim.api.nvim__id({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})'))
|
||||
end)
|
||||
|
||||
it('correctly converts arrays with type_idx to API objects', function()
|
||||
eq(3, eval([[type(luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array})'))]]))
|
||||
|
||||
eq({}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array})'))
|
||||
|
||||
eq({42}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
|
||||
eq({{foo=2}}, funcs.luaeval('vim.api.nvim__id_array({{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
|
||||
eq({10}, funcs.luaeval('vim.api.nvim__id_array({{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
|
||||
eq({}, funcs.luaeval('vim.api.nvim__id_array({[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2})'))
|
||||
|
||||
eq({}, funcs.luaeval('vim.api.nvim__id_array({})'))
|
||||
eq(3, eval([[type(luaeval('vim.api.nvim__id_array({})'))]]))
|
||||
end)
|
||||
|
||||
it('correctly converts dictionaries with type_idx to API objects', function()
|
||||
eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary})'))]]))
|
||||
|
||||
eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary})'))
|
||||
|
||||
eq({v={42}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
|
||||
eq({foo=2}, funcs.luaeval('vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42})'))
|
||||
eq({v=10}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}})'))
|
||||
eq({v={}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})'))
|
||||
|
||||
-- If API requests dictionary, then empty table will be the one. This is not
|
||||
-- the case normally because empty table is an empty arrray.
|
||||
eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({})'))
|
||||
eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({})'))]]))
|
||||
end)
|
||||
|
||||
it('errors out correctly when working with API', function()
|
||||
-- Conversion errors
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Cannot convert given lua type',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id(vim.api.nvim__id)")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Cannot convert given lua table',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Cannot convert given lua type',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id({42, vim.api.nvim__id})")]]))
|
||||
-- Errors in number of arguments
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected 1 argument',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id()")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected 1 argument',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id(1, 2)")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected 2 arguments',
|
||||
exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]]))
|
||||
-- Error in argument types
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua string',
|
||||
exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2)")]]))
|
||||
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua number',
|
||||
exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Number is not integral',
|
||||
exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]]))
|
||||
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua table',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Unexpected type',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id_float({[vim.type_idx]=vim.types.dictionary})")]]))
|
||||
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua table',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id_array(1)")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Unexpected type',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id_array({[vim.type_idx]=vim.types.dictionary})")]]))
|
||||
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua table',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Unexpected type',
|
||||
exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]]))
|
||||
-- TODO: check for errors with Tabpage argument
|
||||
-- TODO: check for errors with Window argument
|
||||
-- TODO: check for errors with Buffer argument
|
||||
end)
|
||||
|
||||
it('accepts any value as API Boolean', function()
|
||||
eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", vim, false, nil)'))
|
||||
eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", 0, 1.5, "test")'))
|
||||
eq('', funcs.luaeval('vim.api.nvim_replace_termcodes("", true, {}, {[vim.type_idx]=vim.types.array})'))
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,164 @@
|
|||
-- Test suite for checking :lua* commands
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
|
||||
local eq = helpers.eq
|
||||
local NIL = helpers.NIL
|
||||
local clear = helpers.clear
|
||||
local meths = helpers.meths
|
||||
local funcs = helpers.funcs
|
||||
local source = helpers.source
|
||||
local dedent = helpers.dedent
|
||||
local exc_exec = helpers.exc_exec
|
||||
local write_file = helpers.write_file
|
||||
local redir_exec = helpers.redir_exec
|
||||
local curbufmeths = helpers.curbufmeths
|
||||
|
||||
before_each(clear)
|
||||
|
||||
describe(':lua command', function()
|
||||
it('works', function()
|
||||
eq('', redir_exec(
|
||||
'lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TEST"})'))
|
||||
eq({'', 'TEST'}, curbufmeths.get_lines(0, 100, false))
|
||||
source(dedent([[
|
||||
lua << EOF
|
||||
vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TSET"})
|
||||
EOF]]))
|
||||
eq({'', 'TSET'}, curbufmeths.get_lines(0, 100, false))
|
||||
source(dedent([[
|
||||
lua << EOF
|
||||
vim.api.nvim_buf_set_lines(1, 1, 2, false, {"SETT"})]]))
|
||||
eq({'', 'SETT'}, curbufmeths.get_lines(0, 100, false))
|
||||
source(dedent([[
|
||||
lua << EOF
|
||||
vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})
|
||||
vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"})
|
||||
vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"})
|
||||
EOF]]))
|
||||
eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false))
|
||||
end)
|
||||
it('throws catchable errors', function()
|
||||
eq([[Vim(lua):E5104: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unexpected symbol near ')']],
|
||||
exc_exec('lua ()'))
|
||||
eq([[Vim(lua):E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: TEST]],
|
||||
exc_exec('lua error("TEST")'))
|
||||
eq([[Vim(lua):E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: Invalid buffer id]],
|
||||
exc_exec('lua vim.api.nvim_buf_set_lines(-10, 1, 1, false, {"TEST"})'))
|
||||
eq({''}, curbufmeths.get_lines(0, 100, false))
|
||||
end)
|
||||
it('works with NULL errors', function()
|
||||
eq([=[Vim(lua):E5105: Error while calling lua chunk: [NULL]]=],
|
||||
exc_exec('lua error(nil)'))
|
||||
end)
|
||||
it('accepts embedded NLs without heredoc', function()
|
||||
-- Such code is usually used for `:execute 'lua' {generated_string}`:
|
||||
-- heredocs do not work in this case.
|
||||
meths.command([[
|
||||
lua
|
||||
vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})
|
||||
vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"})
|
||||
vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"})
|
||||
]])
|
||||
eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false))
|
||||
end)
|
||||
it('preserves global and not preserves local variables', function()
|
||||
eq('', redir_exec('lua gvar = 42'))
|
||||
eq('', redir_exec('lua local lvar = 100500'))
|
||||
eq(NIL, funcs.luaeval('lvar'))
|
||||
eq(42, funcs.luaeval('gvar'))
|
||||
end)
|
||||
it('works with long strings', function()
|
||||
local s = ('x'):rep(100500)
|
||||
|
||||
eq('\nE5104: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unfinished string near \'<eof>\'', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s})'):format(s)))
|
||||
eq({''}, curbufmeths.get_lines(0, -1, false))
|
||||
|
||||
eq('', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s)))
|
||||
eq({'', s}, curbufmeths.get_lines(0, -1, false))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':luado command', function()
|
||||
it('works', function()
|
||||
curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"})
|
||||
eq('', redir_exec('luado lines = (lines or {}) lines[#lines + 1] = {linenr, line}'))
|
||||
eq({'ABC', 'def', 'gHi'}, curbufmeths.get_lines(0, -1, false))
|
||||
eq({{1, 'ABC'}, {2, 'def'}, {3, 'gHi'}}, funcs.luaeval('lines'))
|
||||
|
||||
-- Automatic transformation of numbers
|
||||
eq('', redir_exec('luado return linenr'))
|
||||
eq({'1', '2', '3'}, curbufmeths.get_lines(0, -1, false))
|
||||
|
||||
eq('', redir_exec('luado return ("<%02x>"):format(line:byte())'))
|
||||
eq({'<31>', '<32>', '<33>'}, curbufmeths.get_lines(0, -1, false))
|
||||
end)
|
||||
it('stops processing lines when suddenly out of lines', function()
|
||||
curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"})
|
||||
eq('', redir_exec('2,$luado runs = ((runs or 0) + 1) vim.api.nvim_command("%d")'))
|
||||
eq({''}, curbufmeths.get_lines(0, -1, false))
|
||||
eq(1, funcs.luaeval('runs'))
|
||||
end)
|
||||
it('works correctly when changing lines out of range', function()
|
||||
curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"})
|
||||
eq('\nE322: line number out of range: 1 past the end\nE320: Cannot find line 2',
|
||||
redir_exec('2,$luado vim.api.nvim_command("%d") return linenr'))
|
||||
eq({''}, curbufmeths.get_lines(0, -1, false))
|
||||
end)
|
||||
it('fails on errors', function()
|
||||
eq([[Vim(luado):E5109: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unexpected symbol near ')']],
|
||||
exc_exec('luado ()'))
|
||||
eq([[Vim(luado):E5111: Error while calling lua function: [string "<VimL compiled string>"]:1: attempt to perform arithmetic on global 'liness' (a nil value)]],
|
||||
exc_exec('luado return liness + 1'))
|
||||
end)
|
||||
it('works with NULL errors', function()
|
||||
eq([=[Vim(luado):E5111: Error while calling lua function: [NULL]]=],
|
||||
exc_exec('luado error(nil)'))
|
||||
end)
|
||||
it('fails in sandbox when needed', function()
|
||||
curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"})
|
||||
eq('\nE48: Not allowed in sandbox: sandbox luado runs = (runs or 0) + 1',
|
||||
redir_exec('sandbox luado runs = (runs or 0) + 1'))
|
||||
eq(NIL, funcs.luaeval('runs'))
|
||||
end)
|
||||
it('works with long strings', function()
|
||||
local s = ('x'):rep(100500)
|
||||
|
||||
eq('\nE5109: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unfinished string near \'<eof>\'', redir_exec(('luado return "%s'):format(s)))
|
||||
eq({''}, curbufmeths.get_lines(0, -1, false))
|
||||
|
||||
eq('', redir_exec(('luado return "%s"'):format(s)))
|
||||
eq({s}, curbufmeths.get_lines(0, -1, false))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':luafile', function()
|
||||
local fname = 'Xtest-functional-lua-commands-luafile'
|
||||
|
||||
after_each(function()
|
||||
os.remove(fname)
|
||||
end)
|
||||
|
||||
it('works', function()
|
||||
write_file(fname, [[
|
||||
vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})
|
||||
vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"})
|
||||
vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"})
|
||||
]])
|
||||
eq('', redir_exec('luafile ' .. fname))
|
||||
eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false))
|
||||
end)
|
||||
|
||||
it('correctly errors out', function()
|
||||
write_file(fname, '()')
|
||||
eq(("Vim(luafile):E5112: Error while creating lua chunk: %s:1: unexpected symbol near ')'"):format(fname),
|
||||
exc_exec('luafile ' .. fname))
|
||||
write_file(fname, 'vimm.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})')
|
||||
eq(("Vim(luafile):E5113: Error while calling lua chunk: %s:1: attempt to index global 'vimm' (a nil value)"):format(fname),
|
||||
exc_exec('luafile ' .. fname))
|
||||
end)
|
||||
it('works with NULL errors', function()
|
||||
write_file(fname, 'error(nil)')
|
||||
eq([=[Vim(luafile):E5113: Error while calling lua chunk: [NULL]]=],
|
||||
exc_exec('luafile ' .. fname))
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,255 @@
|
|||
-- Test suite for testing luaeval() function
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
|
||||
local redir_exec = helpers.redir_exec
|
||||
local exc_exec = helpers.exc_exec
|
||||
local command = helpers.command
|
||||
local meths = helpers.meths
|
||||
local funcs = helpers.funcs
|
||||
local clear = helpers.clear
|
||||
local eval = helpers.eval
|
||||
local NIL = helpers.NIL
|
||||
local eq = helpers.eq
|
||||
|
||||
before_each(clear)
|
||||
|
||||
local function startswith(expected, actual)
|
||||
eq(expected, actual:sub(1, #expected))
|
||||
end
|
||||
|
||||
describe('luaeval()', function()
|
||||
local nested_by_level = {}
|
||||
local nested = {}
|
||||
local nested_s = '{}'
|
||||
for i=1,100 do
|
||||
if i % 2 == 0 then
|
||||
nested = {nested}
|
||||
nested_s = '{' .. nested_s .. '}'
|
||||
else
|
||||
nested = {nested=nested}
|
||||
nested_s = '{nested=' .. nested_s .. '}'
|
||||
end
|
||||
nested_by_level[i] = {o=nested, s=nested_s}
|
||||
end
|
||||
|
||||
describe('second argument', function()
|
||||
it('is successfully received', function()
|
||||
local t = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}}
|
||||
eq(t, funcs.luaeval("_A", t))
|
||||
-- Not tested: nil, funcrefs, returned object identity: behaviour will
|
||||
-- most likely change.
|
||||
end)
|
||||
end)
|
||||
describe('lua values', function()
|
||||
it('are successfully transformed', function()
|
||||
eq({n=1, f=1.5, s='string', l={4, 2}},
|
||||
funcs.luaeval('{n=1, f=1.5, s="string", l={4, 2}}'))
|
||||
-- Not tested: nil inside containers: behaviour will most likely change.
|
||||
eq(NIL, funcs.luaeval('nil'))
|
||||
eq({['']=1}, funcs.luaeval('{[""]=1}'))
|
||||
end)
|
||||
end)
|
||||
describe('recursive lua values', function()
|
||||
it('are successfully transformed', function()
|
||||
funcs.luaeval('rawset(_G, "d", {})')
|
||||
funcs.luaeval('rawset(d, "d", d)')
|
||||
eq('\n{\'d\': {...@0}}', funcs.execute('echo luaeval("d")'))
|
||||
|
||||
funcs.luaeval('rawset(_G, "l", {})')
|
||||
funcs.luaeval('table.insert(l, l)')
|
||||
eq('\n[[...@0]]', funcs.execute('echo luaeval("l")'))
|
||||
end)
|
||||
end)
|
||||
describe('strings', function()
|
||||
it('are successfully converted to special dictionaries', function()
|
||||
command([[let s = luaeval('"\0"')]])
|
||||
eq({_TYPE={}, _VAL={'\n'}}, meths.get_var('s'))
|
||||
eq(1, funcs.eval('s._TYPE is v:msgpack_types.binary'))
|
||||
end)
|
||||
it('are successfully converted to special dictionaries in table keys',
|
||||
function()
|
||||
command([[let d = luaeval('{["\0"]=1}')]])
|
||||
eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n'}}, 1}}}, meths.get_var('d'))
|
||||
eq(1, funcs.eval('d._TYPE is v:msgpack_types.map'))
|
||||
eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string'))
|
||||
end)
|
||||
it('are successfully converted to special dictionaries from a list',
|
||||
function()
|
||||
command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]])
|
||||
eq({'abc', {_TYPE={}, _VAL={'a\nb'}}, {_TYPE={}, _VAL={'c\nd'}}, 'def'},
|
||||
meths.get_var('l'))
|
||||
eq(1, funcs.eval('l[1]._TYPE is v:msgpack_types.binary'))
|
||||
eq(1, funcs.eval('l[2]._TYPE is v:msgpack_types.binary'))
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Not checked: funcrefs converted to NIL. To be altered to something more
|
||||
-- meaningful later.
|
||||
|
||||
it('correctly evaluates scalars', function()
|
||||
eq(1, funcs.luaeval('1'))
|
||||
eq(0, eval('type(luaeval("1"))'))
|
||||
|
||||
eq(1.5, funcs.luaeval('1.5'))
|
||||
eq(5, eval('type(luaeval("1.5"))'))
|
||||
|
||||
eq("test", funcs.luaeval('"test"'))
|
||||
eq(1, eval('type(luaeval("\'test\'"))'))
|
||||
|
||||
eq('', funcs.luaeval('""'))
|
||||
eq({_TYPE={}, _VAL={'\n'}}, funcs.luaeval([['\0']]))
|
||||
eq({_TYPE={}, _VAL={'\n', '\n'}}, funcs.luaeval([['\0\n\0']]))
|
||||
eq(1, eval([[luaeval('"\0\n\0"')._TYPE is v:msgpack_types.binary]]))
|
||||
|
||||
eq(true, funcs.luaeval('true'))
|
||||
eq(false, funcs.luaeval('false'))
|
||||
eq(NIL, funcs.luaeval('nil'))
|
||||
end)
|
||||
|
||||
it('correctly evaluates containers', function()
|
||||
eq({}, funcs.luaeval('{}'))
|
||||
eq(3, eval('type(luaeval("{}"))'))
|
||||
|
||||
eq({test=1, foo=2}, funcs.luaeval('{test=1, foo=2}'))
|
||||
eq(4, eval('type(luaeval("{test=1, foo=2}"))'))
|
||||
|
||||
eq({4, 2}, funcs.luaeval('{4, 2}'))
|
||||
eq(3, eval('type(luaeval("{4, 2}"))'))
|
||||
|
||||
local level = 30
|
||||
eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s))
|
||||
|
||||
eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}},
|
||||
funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]]))
|
||||
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]]))
|
||||
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]]))
|
||||
eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][1]._TYPE is v:msgpack_types.binary]]))
|
||||
eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}}},
|
||||
funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]]))
|
||||
end)
|
||||
|
||||
it('correctly passes scalars as argument', function()
|
||||
eq(1, funcs.luaeval('_A', 1))
|
||||
eq(1.5, funcs.luaeval('_A', 1.5))
|
||||
eq('', funcs.luaeval('_A', ''))
|
||||
eq('test', funcs.luaeval('_A', 'test'))
|
||||
eq(NIL, funcs.luaeval('_A', NIL))
|
||||
eq(true, funcs.luaeval('_A', true))
|
||||
eq(false, funcs.luaeval('_A', false))
|
||||
end)
|
||||
|
||||
it('correctly passes containers as argument', function()
|
||||
eq({}, funcs.luaeval('_A', {}))
|
||||
eq({test=1}, funcs.luaeval('_A', {test=1}))
|
||||
eq({4, 2}, funcs.luaeval('_A', {4, 2}))
|
||||
local level = 28
|
||||
eq(nested_by_level[level].o, funcs.luaeval('_A', nested_by_level[level].o))
|
||||
end)
|
||||
|
||||
local function sp(typ, val)
|
||||
return ('{"_TYPE": v:msgpack_types.%s, "_VAL": %s}'):format(typ, val)
|
||||
end
|
||||
local function mapsp(...)
|
||||
local val = ''
|
||||
for i=1,(select('#', ...)/2) do
|
||||
val = ('%s[%s,%s],'):format(val, select(i * 2 - 1, ...),
|
||||
select(i * 2, ...))
|
||||
end
|
||||
return sp('map', '[' .. val .. ']')
|
||||
end
|
||||
local function luaevalarg(argexpr, expr)
|
||||
return eval(([=[
|
||||
[
|
||||
extend(g:, {'_ret': luaeval(%s, %s)})._ret,
|
||||
type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE')
|
||||
? [
|
||||
get(keys(filter(copy(v:msgpack_types), 'v:val is g:_ret._TYPE')), 0,
|
||||
g:_ret._TYPE),
|
||||
get(g:_ret, '_VAL', g:_ret)
|
||||
]
|
||||
: [0, g:_ret]][1]
|
||||
]=]):format(expr or '"_A"', argexpr):gsub('\n', ''))
|
||||
end
|
||||
|
||||
it('correctly passes special dictionaries', function()
|
||||
eq({'binary', {'\n', '\n'}}, luaevalarg(sp('binary', '["\\n", "\\n"]')))
|
||||
eq({'binary', {'\n', '\n'}}, luaevalarg(sp('string', '["\\n", "\\n"]')))
|
||||
eq({0, true}, luaevalarg(sp('boolean', 1)))
|
||||
eq({0, false}, luaevalarg(sp('boolean', 0)))
|
||||
eq({0, NIL}, luaevalarg(sp('nil', 0)))
|
||||
eq({0, {[""]=""}}, luaevalarg(mapsp(sp('binary', '[""]'), '""')))
|
||||
eq({0, {[""]=""}}, luaevalarg(mapsp(sp('string', '[""]'), '""')))
|
||||
end)
|
||||
|
||||
it('issues an error in some cases', function()
|
||||
eq("Vim(call):E5100: Cannot convert given lua table: table should either have a sequence of positive integer keys or contain only string keys",
|
||||
exc_exec('call luaeval("{1, foo=2}")'))
|
||||
eq("Vim(call):E5101: Cannot convert given lua type",
|
||||
exc_exec('call luaeval("vim.api.nvim_buf_get_lines")'))
|
||||
startswith("Vim(call):E5107: Error while creating lua chunk for luaeval(): ",
|
||||
exc_exec('call luaeval("1, 2, 3")'))
|
||||
startswith("Vim(call):E5108: Error while calling lua chunk for luaeval(): ",
|
||||
exc_exec('call luaeval("(nil)()")'))
|
||||
eq("Vim(call):E5101: Cannot convert given lua type",
|
||||
exc_exec('call luaeval("{42, vim.api}")'))
|
||||
eq("Vim(call):E5101: Cannot convert given lua type",
|
||||
exc_exec('call luaeval("{foo=42, baz=vim.api}")'))
|
||||
|
||||
-- The following should not crash: conversion error happens inside
|
||||
eq("Vim(call):E5101: Cannot convert given lua type",
|
||||
exc_exec('call luaeval("vim.api")'))
|
||||
-- The following should not show internal error
|
||||
eq("\nE5101: Cannot convert given lua type\n0",
|
||||
redir_exec('echo luaeval("vim.api")'))
|
||||
end)
|
||||
|
||||
it('correctly converts containers with type_idx', function()
|
||||
eq(5, eval('type(luaeval("{[vim.type_idx]=vim.types.float, [vim.val_idx]=0}"))'))
|
||||
eq(4, eval([[type(luaeval('{[vim.type_idx]=vim.types.dictionary}'))]]))
|
||||
eq(3, eval([[type(luaeval('{[vim.type_idx]=vim.types.array}'))]]))
|
||||
|
||||
eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.array}'))
|
||||
|
||||
-- Presence of type_idx makes Vim ignore some keys
|
||||
eq({42}, funcs.luaeval('{[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}'))
|
||||
eq({foo=2}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}'))
|
||||
eq(10, funcs.luaeval('{[vim.type_idx]=vim.types.float, [vim.val_idx]=10, [5]=1, foo=2, [1]=42}'))
|
||||
|
||||
-- The following should not crash
|
||||
eq({}, funcs.luaeval('{[vim.type_idx]=vim.types.dictionary}'))
|
||||
end)
|
||||
|
||||
it('correctly converts self-containing containers', function()
|
||||
meths.set_var('l', {})
|
||||
eval('add(l, l)')
|
||||
eq(true, eval('luaeval("_A == _A[1]", l)'))
|
||||
eq(true, eval('luaeval("_A[1] == _A[1][1]", [l])'))
|
||||
eq(true, eval('luaeval("_A.d == _A.d[1]", {"d": l})'))
|
||||
eq(true, eval('luaeval("_A ~= _A[1]", [l])'))
|
||||
|
||||
meths.set_var('d', {foo=42})
|
||||
eval('extend(d, {"d": d})')
|
||||
eq(true, eval('luaeval("_A == _A.d", d)'))
|
||||
eq(true, eval('luaeval("_A[1] == _A[1].d", [d])'))
|
||||
eq(true, eval('luaeval("_A.d == _A.d.d", {"d": d})'))
|
||||
eq(true, eval('luaeval("_A ~= _A.d", {"d": d})'))
|
||||
end)
|
||||
|
||||
it('errors out correctly when doing incorrect things in lua', function()
|
||||
-- Conversion errors
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)',
|
||||
exc_exec([[call luaeval("vim.xxx_nonexistent_key_xxx()")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: ERROR',
|
||||
exc_exec([[call luaeval("error('ERROR')")]]))
|
||||
eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [NULL]',
|
||||
exc_exec([[call luaeval("error(nil)")]]))
|
||||
end)
|
||||
|
||||
it('does not leak memory when called with too long line',
|
||||
function()
|
||||
local s = ('x'):rep(65536)
|
||||
eq('Vim(call):E5107: Error while creating lua chunk for luaeval(): [string "<VimL compiled string>"]:1: unexpected symbol near \')\'',
|
||||
exc_exec([[call luaeval("(']] .. s ..[[' + )")]]))
|
||||
eq(s, funcs.luaeval('"' .. s .. '"'))
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,175 @@
|
|||
-- Test for Vim overrides of lua built-ins
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local eq = helpers.eq
|
||||
local NIL = helpers.NIL
|
||||
local feed = helpers.feed
|
||||
local clear = helpers.clear
|
||||
local funcs = helpers.funcs
|
||||
local meths = helpers.meths
|
||||
local command = helpers.command
|
||||
local write_file = helpers.write_file
|
||||
local redir_exec = helpers.redir_exec
|
||||
|
||||
local screen
|
||||
|
||||
local fname = 'Xtest-functional-lua-overrides-luafile'
|
||||
|
||||
before_each(clear)
|
||||
|
||||
after_each(function()
|
||||
os.remove(fname)
|
||||
end)
|
||||
|
||||
describe('print', function()
|
||||
it('returns nothing', function()
|
||||
eq(NIL, funcs.luaeval('print("abc")'))
|
||||
eq(0, funcs.luaeval('select("#", print("abc"))'))
|
||||
end)
|
||||
it('allows catching printed text with :execute', function()
|
||||
eq('\nabc', funcs.execute('lua print("abc")'))
|
||||
eq('\nabc', funcs.execute('luado print("abc")'))
|
||||
eq('\nabc', funcs.execute('call luaeval("print(\'abc\')")'))
|
||||
write_file(fname, 'print("abc")')
|
||||
eq('\nabc', funcs.execute('luafile ' .. fname))
|
||||
|
||||
eq('\nabc', redir_exec('lua print("abc")'))
|
||||
eq('\nabc', redir_exec('luado print("abc")'))
|
||||
eq('\nabc', redir_exec('call luaeval("print(\'abc\')")'))
|
||||
write_file(fname, 'print("abc")')
|
||||
eq('\nabc', redir_exec('luafile ' .. fname))
|
||||
end)
|
||||
it('handles errors in __tostring', function()
|
||||
write_file(fname, [[
|
||||
local meta_nilerr = { __tostring = function() error(nil) end }
|
||||
local meta_abcerr = { __tostring = function() error("abc") end }
|
||||
local meta_tblout = { __tostring = function() return {"TEST"} end }
|
||||
v_nilerr = setmetatable({}, meta_nilerr)
|
||||
v_abcerr = setmetatable({}, meta_abcerr)
|
||||
v_tblout = setmetatable({}, meta_tblout)
|
||||
]])
|
||||
eq('', redir_exec('luafile ' .. fname))
|
||||
eq('\nE5114: Error while converting print argument #2: [NULL]',
|
||||
redir_exec('lua print("foo", v_nilerr, "bar")'))
|
||||
eq('\nE5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
|
||||
redir_exec('lua print("foo", v_abcerr, "bar")'))
|
||||
eq('\nE5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
|
||||
redir_exec('lua print("foo", v_tblout, "bar")'))
|
||||
end)
|
||||
it('prints strings with NULs and NLs correctly', function()
|
||||
meths.set_option('more', true)
|
||||
eq('\nabc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT\n',
|
||||
redir_exec([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\n")]]))
|
||||
eq('\nabc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT^@',
|
||||
redir_exec([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\0")]]))
|
||||
eq('\nT^@', redir_exec([[lua print("T\0")]]))
|
||||
eq('\nT\n', redir_exec([[lua print("T\n")]]))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('debug.debug', function()
|
||||
before_each(function()
|
||||
screen = Screen.new()
|
||||
screen:attach()
|
||||
screen:set_default_attr_ids({
|
||||
[0] = {bold=true, foreground=255},
|
||||
E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||
cr = {bold = true, foreground = Screen.colors.SeaGreen4},
|
||||
})
|
||||
end)
|
||||
it('works', function()
|
||||
command([[lua
|
||||
function Test(a)
|
||||
print(a)
|
||||
debug.debug()
|
||||
print(a * 100)
|
||||
end
|
||||
]])
|
||||
feed(':lua Test()\n')
|
||||
screen:expect([[
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
nil |
|
||||
lua_debug> ^ |
|
||||
]])
|
||||
feed('print("TEST")\n')
|
||||
screen:expect([[
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
nil |
|
||||
lua_debug> print("TEST") |
|
||||
TEST |
|
||||
lua_debug> ^ |
|
||||
]])
|
||||
feed('<C-c>')
|
||||
screen:expect([[
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
nil |
|
||||
lua_debug> print("TEST") |
|
||||
TEST |
|
||||
|
|
||||
{E:E5105: Error while calling lua chunk: [string "<VimL }|
|
||||
{E:compiled string>"]:5: attempt to perform arithmetic o}|
|
||||
{E:n local 'a' (a nil value)} |
|
||||
Interrupt: {cr:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
feed('<C-l>:lua Test()\n')
|
||||
screen:expect([[
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
nil |
|
||||
lua_debug> ^ |
|
||||
]])
|
||||
feed('\n')
|
||||
screen:expect([[
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
{0:~ }|
|
||||
nil |
|
||||
lua_debug> |
|
||||
{E:E5105: Error while calling lua chunk: [string "<VimL }|
|
||||
{E:compiled string>"]:5: attempt to perform arithmetic o}|
|
||||
{E:n local 'a' (a nil value)} |
|
||||
{cr:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
end)
|
||||
end)
|
|
@ -263,6 +263,14 @@ local function which(exe)
|
|||
end
|
||||
end
|
||||
|
||||
local function shallowcopy(orig)
|
||||
local copy = {}
|
||||
for orig_key, orig_value in pairs(orig) do
|
||||
copy[orig_key] = orig_value
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
local function concat_tables(...)
|
||||
local ret = {}
|
||||
for i = 1, select('#', ...) do
|
||||
|
@ -311,6 +319,7 @@ return {
|
|||
check_cores = check_cores,
|
||||
hasenv = hasenv,
|
||||
which = which,
|
||||
shallowcopy = shallowcopy,
|
||||
concat_tables = concat_tables,
|
||||
dedent = dedent,
|
||||
}
|
||||
|
|
|
@ -51,19 +51,32 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
set(LUA_CFLAGS "-O0 -g3 -fPIC")
|
||||
set(LUA_LDFLAGS "")
|
||||
|
||||
if(CLANG_ASAN_UBSAN)
|
||||
set(LUA_CFLAGS "${LUA_CFLAGS} -fsanitize=address")
|
||||
set(LUA_CFLAGS "${LUA_CFLAGS} -fno-omit-frame-pointer")
|
||||
set(LUA_CFLAGS "${LUA_CFLAGS} -fno-optimize-sibling-calls")
|
||||
|
||||
set(LUA_LDFLAGS "${LUA_LDFLAGS} -fsanitize=address")
|
||||
endif()
|
||||
|
||||
set(LUA_CONFIGURE_COMMAND
|
||||
sed -e "/^CC/s@gcc@${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}@"
|
||||
-e "/^CFLAGS/s@-O2@-g3@"
|
||||
-e "/^CFLAGS/s@-O2@${LUA_CFLAGS}@"
|
||||
-e "/^MYLDFLAGS/s@$@${LUA_LDFLAGS}@"
|
||||
-e "s@-lreadline@@g"
|
||||
-e "s@-lhistory@@g"
|
||||
-e "s@-lncurses@@g"
|
||||
-i ${DEPS_BUILD_DIR}/src/lua/src/Makefile &&
|
||||
sed -e "/#define LUA_USE_READLINE/d"
|
||||
-i ${DEPS_BUILD_DIR}/src/lua/src/luaconf.h)
|
||||
set(LUA_INSTALL_TOP_ARG "INSTALL_TOP=${DEPS_INSTALL_DIR}")
|
||||
set(LUA_BUILD_COMMAND
|
||||
${MAKE_PRG} ${LUA_TARGET})
|
||||
${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} ${LUA_TARGET})
|
||||
set(LUA_INSTALL_COMMAND
|
||||
${MAKE_PRG} INSTALL_TOP=${DEPS_INSTALL_DIR} install)
|
||||
${MAKE_PRG} ${LUA_INSTALL_TOP_ARG} install)
|
||||
|
||||
message(STATUS "Lua target is ${LUA_TARGET}")
|
||||
|
||||
|
|
Loading…
Reference in New Issue