mirror of
https://github.com/neovim/neovim.git
synced 2025-09-29 14:38:32 +00:00
Merge #4411 from ZyX-I/luaviml'/lua
This commit is contained in:
@@ -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
|
||||
|
1186
src/nvim/lua/converter.c
Normal file
1186
src/nvim/lua/converter.c
Normal file
File diff suppressed because it is too large
Load Diff
15
src/nvim/lua/converter.h
Normal file
15
src/nvim/lua/converter.h
Normal file
@@ -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
|
576
src/nvim/lua/executor.c
Normal file
576
src/nvim/lua/executor.c
Normal file
@@ -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);
|
||||
}
|
25
src/nvim/lua/executor.h
Normal file
25
src/nvim/lua/executor.h
Normal file
@@ -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
|
2
src/nvim/lua/vim.lua
Normal file
2
src/nvim/lua/vim.lua
Normal file
@@ -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
|
||||
|
Reference in New Issue
Block a user