mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 03:05:11 -07:00
Merge #6789 from ZyX-I/lua-path
lua: Add paths from &runtimepath to package.path and package.cpath
This commit is contained in:
commit
f34befe74c
@ -600,9 +600,26 @@ if(LUACHECK_PRG)
|
||||
add_custom_target(testlint
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DLUACHECK_PRG=${LUACHECK_PRG}
|
||||
-DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-DLUAFILES_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-DIGNORE_PATTERN="*/preload.lua"
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
|
||||
-P ${PROJECT_SOURCE_DIR}/cmake/RunTestsLint.cmake)
|
||||
-P ${PROJECT_SOURCE_DIR}/cmake/RunLuacheck.cmake)
|
||||
|
||||
add_custom_target(
|
||||
blobcodelint
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
-DLUACHECK_PRG=${LUACHECK_PRG}
|
||||
-DLUAFILES_DIR=${CMAKE_CURRENT_SOURCE_DIR}/src/nvim/lua
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
|
||||
-DREAD_GLOBALS=vim
|
||||
-P ${PROJECT_SOURCE_DIR}/cmake/RunLuacheck.cmake
|
||||
)
|
||||
# TODO(ZyX-I): Run linter for all lua code in src
|
||||
add_custom_target(
|
||||
lualint
|
||||
DEPENDS blobcodelint
|
||||
)
|
||||
endif()
|
||||
|
||||
set(CPACK_PACKAGE_NAME "Neovim")
|
||||
|
7
Makefile
7
Makefile
@ -107,6 +107,9 @@ functionaltest-lua: | nvim
|
||||
testlint: | build/.ran-cmake deps
|
||||
$(BUILD_CMD) -C build testlint
|
||||
|
||||
lualint: | build/.ran-cmake deps
|
||||
$(BUILD_CMD) -C build lualint
|
||||
|
||||
unittest: | nvim
|
||||
+$(BUILD_CMD) -C build unittest
|
||||
|
||||
@ -138,6 +141,6 @@ check-single-includes: build/.ran-cmake
|
||||
appimage:
|
||||
bash scripts/genappimage.sh
|
||||
|
||||
lint: check-single-includes clint testlint
|
||||
lint: check-single-includes clint testlint lualint
|
||||
|
||||
.PHONY: test testlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage
|
||||
.PHONY: test testlint lualint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage
|
||||
|
@ -20,6 +20,12 @@ run_test 'top_make testlint' testlint
|
||||
|
||||
exit_suite --continue
|
||||
|
||||
enter_suite 'lualint'
|
||||
|
||||
run_test 'top_make lualint' lualint
|
||||
|
||||
exit_suite --continue
|
||||
|
||||
enter_suite single-includes
|
||||
|
||||
CLICOLOR_FORCE=1 run_test_wd \
|
||||
|
22
cmake/RunLuacheck.cmake
Normal file
22
cmake/RunLuacheck.cmake
Normal file
@ -0,0 +1,22 @@
|
||||
set(LUACHECK_ARGS -q "${LUAFILES_DIR}")
|
||||
if(DEFINED IGNORE_PATTERN)
|
||||
list(APPEND LUACHECK_ARGS --exclude-files "${LUAFILES_DIR}/${IGNORE_PATTERN}")
|
||||
endif()
|
||||
if(DEFINED CHECK_PATTERN)
|
||||
list(APPEND LUACHECK_ARGS --include-files "${LUAFILES_DIR}/${CHECK_PATTERN}")
|
||||
endif()
|
||||
if(DEFINED READ_GLOBALS)
|
||||
list(APPEND LUACHECK_ARGS --read-globals "${READ_GLOBALS}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${LUACHECK_PRG}" ${LUACHECK_ARGS}
|
||||
WORKING_DIRECTORY "${LUAFILES_DIR}"
|
||||
ERROR_VARIABLE err
|
||||
RESULT_VARIABLE res
|
||||
)
|
||||
|
||||
if(NOT res EQUAL 0)
|
||||
message(STATUS "Output to stderr:\n${err}")
|
||||
message(FATAL_ERROR "Linting tests failed with error: ${res}.")
|
||||
endif()
|
@ -1,13 +0,0 @@
|
||||
set(IGNORE_FILES "${TEST_DIR}/*/preload.lua")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${LUACHECK_PRG} -q ${TEST_DIR} --exclude-files ${IGNORE_FILES}
|
||||
WORKING_DIRECTORY ${TEST_DIR}
|
||||
ERROR_VARIABLE err
|
||||
RESULT_VARIABLE res
|
||||
${EXTRA_ARGS})
|
||||
|
||||
if(NOT res EQUAL 0)
|
||||
message(STATUS "Output to stderr:\n${err}")
|
||||
message(FATAL_ERROR "Linting tests failed with error: ${res}.")
|
||||
endif()
|
@ -9,7 +9,147 @@ Lua Interface to Nvim *lua* *Lua*
|
||||
Type <M-]> to see the table of contents.
|
||||
|
||||
==============================================================================
|
||||
1. Commands *lua-commands*
|
||||
1. Importing modules *lua-require*
|
||||
|
||||
Neovim lua interface automatically adjusts `package.path` and `package.cpath`
|
||||
according to effective &runtimepath value. Adjustment happens after
|
||||
'runtimepath' is changed. `package.path` is adjusted by simply appending
|
||||
`/lua/?.lua` and `/lua/?/init.lua` to each directory from 'runtimepath' (`/`
|
||||
is actually the first character of `package.config`).
|
||||
|
||||
Similarly to `package.path`, modified directories from `runtimepath` are also
|
||||
added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and
|
||||
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
|
||||
the existing `package.cpath` are used. Here is an example:
|
||||
|
||||
1. Given that
|
||||
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
|
||||
- initial (defined at compile time or derived from
|
||||
`$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains
|
||||
`./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
|
||||
2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
|
||||
order: parts of the path starting from the first path component containing
|
||||
question mark and preceding path separator.
|
||||
3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as it’s the same
|
||||
as the suffix of the first path from `package.path` (i.e. `./?.so`). Which
|
||||
leaves `/?.so` and `/a?d/j/g.elf`, in this order.
|
||||
4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
|
||||
second one contains semicolon which is a paths separator so it is out,
|
||||
leaving only `/foo/bar` and `/abc`, in order.
|
||||
5. The cartesian product of paths from 4. and suffixes from 3. is taken,
|
||||
giving four variants. In each variant `/lua` path segment is inserted
|
||||
between path and suffix, leaving
|
||||
|
||||
- `/foo/bar/lua/?.so`
|
||||
- `/foo/bar/lua/a?d/j/g.elf`
|
||||
- `/abc/lua/?.so`
|
||||
- `/abc/lua/a?d/j/g.elf`
|
||||
|
||||
6. New paths are prepended to the original `package.cpath`.
|
||||
|
||||
The result will look like this:
|
||||
|
||||
`/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath')
|
||||
× `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`)
|
||||
|
||||
= `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`
|
||||
|
||||
Note: to keep up with 'runtimepath' updates paths added at previous update are
|
||||
remembered and removed at the next update, while all paths derived from the
|
||||
new 'runtimepath' are prepended as described above. This allows removing
|
||||
paths when path is removed from 'runtimepath', adding paths when they are
|
||||
added and reordering `package.path`/`package.cpath` content if 'runtimepath'
|
||||
was reordered.
|
||||
|
||||
Note 2: even though adjustments happens automatically Neovim does not track
|
||||
current values of `package.path` or `package.cpath`. If you happened to
|
||||
delete some paths from there you need to reset 'runtimepath' to make them
|
||||
readded. Just running `let &runtimepath = &runtimepath` should work.
|
||||
|
||||
Note 3: skipping paths from 'runtimepath' which contain semicolons applies
|
||||
both to `package.path` and `package.cpath`. Given that there is a number of
|
||||
badly written plugins using shell which will not work with paths containing
|
||||
semicolons it is better to not have them in 'runtimepath' at all.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
1.1. Example of the plugin which uses lua modules: *lua-require-example*
|
||||
|
||||
The following example plugin adds a command `:MakeCharBlob` which transforms
|
||||
current buffer into a long `unsigned char` array. Lua contains transformation
|
||||
function in a module `lua/charblob.lua` which is imported in
|
||||
`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed
|
||||
to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in
|
||||
this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`).
|
||||
|
||||
autoload/charblob.vim: >
|
||||
|
||||
function charblob#encode_buffer()
|
||||
call setline(1, luaeval(
|
||||
\ 'require("charblob").encode(unpack(_A))',
|
||||
\ [getline(1, '$'), &textwidth, ' ']))
|
||||
endfunction
|
||||
|
||||
plugin/charblob.vim: >
|
||||
|
||||
if exists('g:charblob_loaded')
|
||||
finish
|
||||
endif
|
||||
let g:charblob_loaded = 1
|
||||
|
||||
command MakeCharBlob :call charblob#encode_buffer()
|
||||
|
||||
lua/charblob.lua: >
|
||||
|
||||
local function charblob_bytes_iter(lines)
|
||||
local init_s = {
|
||||
next_line_idx = 1,
|
||||
next_byte_idx = 1,
|
||||
lines = lines,
|
||||
}
|
||||
local function next(s, _)
|
||||
if lines[s.next_line_idx] == nil then
|
||||
return nil
|
||||
end
|
||||
if s.next_byte_idx > #(lines[s.next_line_idx]) then
|
||||
s.next_line_idx = s.next_line_idx + 1
|
||||
s.next_byte_idx = 1
|
||||
return ('\n'):byte()
|
||||
end
|
||||
local ret = lines[s.next_line_idx]:byte(s.next_byte_idx)
|
||||
if ret == ('\n'):byte() then
|
||||
ret = 0 -- See :h NL-used-for-NUL.
|
||||
end
|
||||
s.next_byte_idx = s.next_byte_idx + 1
|
||||
return ret
|
||||
end
|
||||
return next, init_s, nil
|
||||
end
|
||||
|
||||
local function charblob_encode(lines, textwidth, indent)
|
||||
local ret = {
|
||||
'const unsigned char blob[] = {',
|
||||
indent,
|
||||
}
|
||||
for byte in charblob_bytes_iter(lines) do
|
||||
-- .- space + number (width 3) + comma
|
||||
if #(ret[#ret]) + 5 > textwidth then
|
||||
ret[#ret + 1] = indent
|
||||
else
|
||||
ret[#ret] = ret[#ret] .. ' '
|
||||
end
|
||||
ret[#ret] = ret[#ret] .. (('%3u,'):format(byte))
|
||||
end
|
||||
ret[#ret + 1] = '};'
|
||||
return ret
|
||||
end
|
||||
|
||||
return {
|
||||
bytes_iter = charblob_bytes_iter,
|
||||
encode = charblob_encode,
|
||||
}
|
||||
|
||||
==============================================================================
|
||||
2. Commands *lua-commands*
|
||||
|
||||
*:lua*
|
||||
:[range]lua {chunk}
|
||||
|
@ -244,6 +244,8 @@ Lua interface (|if_lua.txt|):
|
||||
while calling lua chunk: [string "<VimL compiled string>"]:1: TEST” in
|
||||
Neovim.
|
||||
- Lua has direct access to Nvim |API| via `vim.api`.
|
||||
- Lua package.path and package.cpath are automatically updated according to
|
||||
'runtimepath': |lua-require|.
|
||||
- Currently, most legacy Vim features are missing.
|
||||
|
||||
|input()| and |inputdialog()| gained support for each other’s features (return
|
||||
|
@ -300,7 +300,7 @@ ArrayOf(String) nvim_list_runtime_paths(void)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
uint8_t *rtp = p_rtp;
|
||||
char_u *rtp = p_rtp;
|
||||
|
||||
if (*rtp == NUL) {
|
||||
// No paths
|
||||
@ -314,13 +314,14 @@ ArrayOf(String) nvim_list_runtime_paths(void)
|
||||
}
|
||||
rtp++;
|
||||
}
|
||||
rv.size++;
|
||||
|
||||
// Allocate memory for the copies
|
||||
rv.items = xmalloc(sizeof(Object) * rv.size);
|
||||
rv.items = xmalloc(sizeof(*rv.items) * rv.size);
|
||||
// Reset the position
|
||||
rtp = p_rtp;
|
||||
// Start copying
|
||||
for (size_t i = 0; i < rv.size && *rtp != NUL; i++) {
|
||||
for (size_t i = 0; i < rv.size; i++) {
|
||||
rv.items[i].type = kObjectTypeString;
|
||||
rv.items[i].data.string.data = xmalloc(MAXPATHL);
|
||||
// Copy the path from 'runtimepath' to rv.items[i]
|
||||
@ -709,7 +710,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
|
||||
Integer nvim_get_color_by_name(String name)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
return name_to_color((uint8_t *)name.data);
|
||||
return name_to_color((char_u *)name.data);
|
||||
}
|
||||
|
||||
Dictionary nvim_get_color_map(void)
|
||||
@ -871,7 +872,7 @@ static void write_msg(String message, bool to_err)
|
||||
#define PUSH_CHAR(i, pos, line_buf, msg) \
|
||||
if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \
|
||||
line_buf[pos] = NUL; \
|
||||
msg((uint8_t *)line_buf); \
|
||||
msg((char_u *)line_buf); \
|
||||
pos = 0; \
|
||||
continue; \
|
||||
} \
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nvim/api/vim.h"
|
||||
#include "nvim/vim.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/ex_cmds2.h"
|
||||
#include "nvim/message.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
@ -284,7 +285,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
///
|
||||
/// Crashes NeoVim if initialization fails. Should be called once per lua
|
||||
/// interpreter instance.
|
||||
static lua_State *init_lua(void)
|
||||
///
|
||||
/// @return New lua interpreter instance.
|
||||
static lua_State *nlua_init(void)
|
||||
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
lua_State *lstate = luaL_newstate();
|
||||
@ -297,7 +300,43 @@ static lua_State *init_lua(void)
|
||||
return lstate;
|
||||
}
|
||||
|
||||
/// Enter lua interpreter
|
||||
///
|
||||
/// Calls nlua_init() if needed. Is responsible for pre-lua call initalization
|
||||
/// like updating `package.[c]path` with directories derived from &runtimepath.
|
||||
///
|
||||
/// @return Interprter instance to use. Will either be initialized now or taken
|
||||
/// from previous initalization.
|
||||
static lua_State *nlua_enter(void)
|
||||
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
static lua_State *global_lstate = NULL;
|
||||
if (global_lstate == NULL) {
|
||||
global_lstate = nlua_init();
|
||||
}
|
||||
lua_State *const lstate = global_lstate;
|
||||
// Last used p_rtp value. Must not be dereferenced because value pointed to
|
||||
// may already be freed. Used to check whether &runtimepath option value
|
||||
// changed.
|
||||
static const void *last_p_rtp = NULL;
|
||||
if (last_p_rtp != (const void *)p_rtp) {
|
||||
// stack: (empty)
|
||||
lua_getglobal(lstate, "vim");
|
||||
// stack: vim
|
||||
lua_getfield(lstate, -1, "_update_package_paths");
|
||||
// stack: vim, vim._update_package_paths
|
||||
if (lua_pcall(lstate, 0, 0, 0)) {
|
||||
// stack: vim, error
|
||||
nlua_error(lstate, _("E5117: Error while updating package paths: %.*s"));
|
||||
// stack: vim
|
||||
}
|
||||
// stack: vim
|
||||
lua_pop(lstate, 1);
|
||||
// stack: (empty)
|
||||
last_p_rtp = (const void *)p_rtp;
|
||||
}
|
||||
return lstate;
|
||||
}
|
||||
|
||||
/// Execute lua string
|
||||
///
|
||||
@ -308,11 +347,7 @@ static lua_State *global_lstate = NULL;
|
||||
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,
|
||||
NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_lua_string, 0,
|
||||
(void *)&str, ret_tv);
|
||||
}
|
||||
|
||||
@ -551,11 +586,7 @@ 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,
|
||||
NLUA_CALL_C_FUNCTION_3(nlua_enter(), nlua_eval_lua_string, 0,
|
||||
(void *)&str, arg, ret_tv);
|
||||
}
|
||||
|
||||
@ -570,12 +601,8 @@ void executor_eval_lua(const String str, typval_T *const arg,
|
||||
/// @return Return value of the execution.
|
||||
Object executor_exec_lua_api(const String str, const Array args, Error *err)
|
||||
{
|
||||
if (global_lstate == NULL) {
|
||||
global_lstate = init_lua();
|
||||
}
|
||||
|
||||
Object retval = NIL;
|
||||
NLUA_CALL_C_FUNCTION_4(global_lstate, nlua_exec_lua_string_api, 0,
|
||||
NLUA_CALL_C_FUNCTION_4(nlua_enter(), nlua_exec_lua_string_api, 0,
|
||||
(void *)&str, (void *)&args, &retval, err);
|
||||
return retval;
|
||||
}
|
||||
@ -609,9 +636,6 @@ void ex_lua(exarg_T *const eap)
|
||||
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;
|
||||
@ -621,7 +645,7 @@ void ex_luado(exarg_T *const eap)
|
||||
.data = (char *)eap->arg,
|
||||
};
|
||||
const linenr_T range[] = { eap->line1, eap->line2 };
|
||||
NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0,
|
||||
NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_luado_string, 0,
|
||||
(void *)&cmd, (void *)range);
|
||||
}
|
||||
|
||||
@ -633,9 +657,6 @@ void ex_luado(exarg_T *const eap)
|
||||
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,
|
||||
NLUA_CALL_C_FUNCTION_1(nlua_enter(), nlua_exec_lua_file, 0,
|
||||
(void *)eap->arg);
|
||||
}
|
||||
|
@ -1,2 +1,64 @@
|
||||
-- TODO(ZyX-I): Create compatibility layer.
|
||||
return {}
|
||||
--{{{1 package.path updater function
|
||||
-- Last inserted paths. Used to clear out items from package.[c]path when they
|
||||
-- are no longer in &runtimepath.
|
||||
local last_nvim_paths = {}
|
||||
local function _update_package_paths()
|
||||
local cur_nvim_paths = {}
|
||||
local rtps = vim.api.nvim_list_runtime_paths()
|
||||
local sep = package.config:sub(1, 1)
|
||||
for _, key in ipairs({'path', 'cpath'}) do
|
||||
local orig_str = package[key] .. ';'
|
||||
local pathtrails_ordered = {}
|
||||
local orig = {}
|
||||
-- Note: ignores trailing item without trailing `;`. Not using something
|
||||
-- simpler in order to preserve empty items (stand for default path).
|
||||
for s in orig_str:gmatch('[^;]*;') do
|
||||
s = s:sub(1, -2) -- Strip trailing semicolon
|
||||
orig[#orig + 1] = s
|
||||
end
|
||||
if key == 'path' then
|
||||
-- /?.lua and /?/init.lua
|
||||
pathtrails_ordered = {sep .. '?.lua', sep .. '?' .. sep .. 'init.lua'}
|
||||
else
|
||||
local pathtrails = {}
|
||||
for _, s in ipairs(orig) do
|
||||
-- Find out path patterns. pathtrail should contain something like
|
||||
-- /?.so, \?.dll. This allows not to bother determining what correct
|
||||
-- suffixes are.
|
||||
local pathtrail = s:match('[/\\][^/\\]*%?.*$')
|
||||
if pathtrail and not pathtrails[pathtrail] then
|
||||
pathtrails[pathtrail] = true
|
||||
pathtrails_ordered[#pathtrails_ordered + 1] = pathtrail
|
||||
end
|
||||
end
|
||||
end
|
||||
local new = {}
|
||||
for _, rtp in ipairs(rtps) do
|
||||
if not rtp:match(';') then
|
||||
for _, pathtrail in pairs(pathtrails_ordered) do
|
||||
local new_path = rtp .. sep .. 'lua' .. pathtrail
|
||||
-- Always keep paths from &runtimepath at the start:
|
||||
-- append them here disregarding orig possibly containing one of them.
|
||||
new[#new + 1] = new_path
|
||||
cur_nvim_paths[new_path] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, orig_path in ipairs(orig) do
|
||||
-- Handle removing obsolete paths originating from &runtimepath: such
|
||||
-- paths either belong to cur_nvim_paths and were already added above or
|
||||
-- to last_nvim_paths and should not be added at all if corresponding
|
||||
-- entry was removed from &runtimepath list.
|
||||
if not (cur_nvim_paths[orig_path] or last_nvim_paths[orig_path]) then
|
||||
new[#new + 1] = orig_path
|
||||
end
|
||||
end
|
||||
package[key] = table.concat(new, ';')
|
||||
end
|
||||
last_nvim_paths = cur_nvim_paths
|
||||
end
|
||||
--{{{1 Module definition
|
||||
return {
|
||||
_update_package_paths = _update_package_paths,
|
||||
}
|
||||
|
@ -588,6 +588,36 @@ describe('api', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('list_runtime_paths', function()
|
||||
it('returns nothing with empty &runtimepath', function()
|
||||
meths.set_option('runtimepath', '')
|
||||
eq({}, meths.list_runtime_paths())
|
||||
end)
|
||||
it('returns single runtimepath', function()
|
||||
meths.set_option('runtimepath', 'a')
|
||||
eq({'a'}, meths.list_runtime_paths())
|
||||
end)
|
||||
it('returns two runtimepaths', function()
|
||||
meths.set_option('runtimepath', 'a,b')
|
||||
eq({'a', 'b'}, meths.list_runtime_paths())
|
||||
end)
|
||||
it('returns empty strings when appropriate', function()
|
||||
meths.set_option('runtimepath', 'a,,b')
|
||||
eq({'a', '', 'b'}, meths.list_runtime_paths())
|
||||
meths.set_option('runtimepath', ',a,b')
|
||||
eq({'', 'a', 'b'}, meths.list_runtime_paths())
|
||||
meths.set_option('runtimepath', 'a,b,')
|
||||
eq({'a', 'b', ''}, meths.list_runtime_paths())
|
||||
end)
|
||||
it('truncates too long paths', function()
|
||||
local long_path = ('/a'):rep(8192)
|
||||
meths.set_option('runtimepath', long_path)
|
||||
local paths_list = meths.list_runtime_paths()
|
||||
neq({long_path}, paths_list)
|
||||
eq({long_path:sub(1, #(paths_list[1]))}, paths_list)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('can throw exceptions', function()
|
||||
local status, err = pcall(nvim, 'get_option', 'invalid-option')
|
||||
eq(false, status)
|
||||
|
@ -581,6 +581,24 @@ local function missing_provider(provider)
|
||||
end
|
||||
end
|
||||
|
||||
local function alter_slashes(obj)
|
||||
if not iswin() then
|
||||
return obj
|
||||
end
|
||||
if type(obj) == 'string' then
|
||||
local ret = obj:gsub('/', '\\')
|
||||
return ret
|
||||
elseif type(obj) == 'table' then
|
||||
local ret = {}
|
||||
for k, v in pairs(obj) do
|
||||
ret[k] = alter_slashes(v)
|
||||
end
|
||||
return ret
|
||||
else
|
||||
assert(false, 'Could only alter slashes for tables of strings and strings')
|
||||
end
|
||||
end
|
||||
|
||||
local module = {
|
||||
prepend_argv = prepend_argv,
|
||||
clear = clear,
|
||||
@ -649,6 +667,7 @@ local module = {
|
||||
NIL = mpack.NIL,
|
||||
get_pathsep = get_pathsep,
|
||||
missing_provider = missing_provider,
|
||||
alter_slashes = alter_slashes,
|
||||
}
|
||||
|
||||
return function(after_each)
|
||||
|
@ -3,14 +3,17 @@ local helpers = require('test.functional.helpers')(after_each)
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local eq = helpers.eq
|
||||
local neq = helpers.neq
|
||||
local NIL = helpers.NIL
|
||||
local feed = helpers.feed
|
||||
local clear = helpers.clear
|
||||
local funcs = helpers.funcs
|
||||
local meths = helpers.meths
|
||||
local iswin = helpers.iswin
|
||||
local command = helpers.command
|
||||
local write_file = helpers.write_file
|
||||
local redir_exec = helpers.redir_exec
|
||||
local alter_slashes = helpers.alter_slashes
|
||||
|
||||
local screen
|
||||
|
||||
@ -173,3 +176,119 @@ describe('debug.debug', function()
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('package.path/package.cpath', function()
|
||||
local sl = alter_slashes
|
||||
|
||||
local function get_new_paths(sufs, runtimepaths)
|
||||
runtimepaths = runtimepaths or meths.list_runtime_paths()
|
||||
local new_paths = {}
|
||||
local sep = package.config:sub(1, 1)
|
||||
for _, v in ipairs(runtimepaths) do
|
||||
for _, suf in ipairs(sufs) do
|
||||
new_paths[#new_paths + 1] = v .. sep .. 'lua' .. suf
|
||||
end
|
||||
end
|
||||
return new_paths
|
||||
end
|
||||
local function execute_lua(cmd, ...)
|
||||
return meths.execute_lua(cmd, {...})
|
||||
end
|
||||
local function eval_lua(expr, ...)
|
||||
return meths.execute_lua('return ' .. expr, {...})
|
||||
end
|
||||
local function set_path(which, value)
|
||||
return execute_lua('package[select(1, ...)] = select(2, ...)', which, value)
|
||||
end
|
||||
|
||||
it('contains directories from &runtimepath on first invocation', function()
|
||||
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
|
||||
|
||||
local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'})
|
||||
local new_cpaths_str = table.concat(new_cpaths, ';')
|
||||
eq(new_cpaths_str, eval_lua('package.cpath'):sub(1, #new_cpaths_str))
|
||||
end)
|
||||
it('puts directories from &runtimepath always at the start', function()
|
||||
meths.set_option('runtimepath', 'a,b')
|
||||
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b'})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
|
||||
|
||||
set_path('path', sl'foo/?.lua;foo/?/init.lua;' .. new_paths_str)
|
||||
|
||||
neq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
|
||||
|
||||
command('set runtimepath+=c')
|
||||
new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b', 'c'})
|
||||
new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
|
||||
end)
|
||||
it('understands uncommon suffixes', function()
|
||||
set_path('cpath', './?/foo/bar/baz/x.nlua')
|
||||
meths.set_option('runtimepath', 'a')
|
||||
local new_paths = get_new_paths({'/?/foo/bar/baz/x.nlua'}, {'a'})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
|
||||
|
||||
set_path('cpath', './yyy?zzz/x')
|
||||
meths.set_option('runtimepath', 'b')
|
||||
new_paths = get_new_paths({'/yyy?zzz/x'}, {'b'})
|
||||
new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
|
||||
|
||||
set_path('cpath', './yyy?zzz/123?ghi/x')
|
||||
meths.set_option('runtimepath', 'b')
|
||||
new_paths = get_new_paths({'/yyy?zzz/123?ghi/x'}, {'b'})
|
||||
new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
|
||||
end)
|
||||
it('preserves empty items', function()
|
||||
local many_empty_path = ';;;;;;'
|
||||
local many_empty_cpath = ';;;;;;./?.luaso'
|
||||
set_path('path', many_empty_path)
|
||||
set_path('cpath', many_empty_cpath)
|
||||
meths.set_option('runtimepath', 'a')
|
||||
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str .. ';' .. many_empty_path, eval_lua('package.path'))
|
||||
local new_cpaths = get_new_paths({'/?.luaso'}, {'a'})
|
||||
local new_cpaths_str = table.concat(new_cpaths, ';')
|
||||
eq(new_cpaths_str .. ';' .. many_empty_cpath, eval_lua('package.cpath'))
|
||||
end)
|
||||
it('preserves empty value', function()
|
||||
set_path('path', '')
|
||||
meths.set_option('runtimepath', 'a')
|
||||
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str .. ';', eval_lua('package.path'))
|
||||
end)
|
||||
it('purges out all additions if runtimepath is set to empty', function()
|
||||
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
local path = eval_lua('package.path')
|
||||
eq(new_paths_str, path:sub(1, #new_paths_str))
|
||||
|
||||
local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'})
|
||||
local new_cpaths_str = table.concat(new_cpaths, ';')
|
||||
local cpath = eval_lua('package.cpath')
|
||||
eq(new_cpaths_str, cpath:sub(1, #new_cpaths_str))
|
||||
|
||||
meths.set_option('runtimepath', '')
|
||||
eq(path:sub(#new_paths_str + 2, -1), eval_lua('package.path'))
|
||||
eq(cpath:sub(#new_cpaths_str + 2, -1), eval_lua('package.cpath'))
|
||||
end)
|
||||
it('works with paths with escaped commas', function()
|
||||
meths.set_option('runtimepath', '\\,')
|
||||
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
|
||||
end)
|
||||
it('ignores paths with semicolons', function()
|
||||
meths.set_option('runtimepath', 'foo;bar,\\,')
|
||||
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','})
|
||||
local new_paths_str = table.concat(new_paths, ';')
|
||||
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user