mirror of
https://github.com/neovim/neovim.git
synced 2025-09-07 20:08:17 +00:00
Merge #7165 'lua: Move stricmp to vim module'
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
*if_lua.txt* Nvim
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Luis Carvalho
|
||||
NVIM REFERENCE MANUAL
|
||||
|
||||
|
||||
Lua Interface to Nvim *lua* *Lua*
|
||||
@@ -9,18 +9,18 @@ Lua Interface to Nvim *lua* *Lua*
|
||||
Type |gO| to see the table of contents.
|
||||
|
||||
==============================================================================
|
||||
1. Importing modules *lua-require*
|
||||
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`).
|
||||
Nvim automatically adjusts `package.path` and `package.cpath` according to
|
||||
effective 'runtimepath' value. Adjustment happens whenever '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:
|
||||
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. Example:
|
||||
|
||||
1. Given that
|
||||
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
|
||||
@@ -61,7 +61,7 @@ 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
|
||||
Note 2: even though adjustments happens automatically Nvim 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.
|
||||
@@ -72,7 +72,7 @@ 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*
|
||||
Example of a plugin that 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
|
||||
@@ -149,7 +149,7 @@ lua/charblob.lua: >
|
||||
}
|
||||
|
||||
==============================================================================
|
||||
2. Commands *lua-commands*
|
||||
Commands *lua-commands*
|
||||
|
||||
*:lua*
|
||||
:[range]lua {chunk}
|
||||
@@ -157,7 +157,7 @@ lua/charblob.lua: >
|
||||
|
||||
Examples:
|
||||
>
|
||||
:lua vim.api.nvim_command('echo "Hello, Neovim!"')
|
||||
:lua vim.api.nvim_command('echo "Hello, Nvim!"')
|
||||
<
|
||||
|
||||
:[range]lua << {endmarker}
|
||||
@@ -230,27 +230,80 @@ 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
|
||||
addition, Lua "print" function has its output redirected to the Nvim 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
|
||||
Lua uses the "vim" module (see |lua-vim|) to issue commands to Nvim
|
||||
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*
|
||||
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.txt|.
|
||||
Lua interfaces Nvim through the "vim" module. Currently it has the `api`
|
||||
submodule and some Nvim-specific utilities.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
vim.api.* functions
|
||||
|
||||
`vim.api` exposes the Nvim |API| as a table of Lua functions. All functions
|
||||
are available.
|
||||
|
||||
For example, to use the "nvim_get_current_line()" API function, call
|
||||
"vim.api.nvim_get_current_line()": >
|
||||
|
||||
print(tostring(vim.api.nvim_get_current_line()))
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
vim.* utility functions
|
||||
|
||||
vim.stricmp(a, b) *lua-vim.stricmp*
|
||||
Function used for case-insensitive string comparison. Takes two
|
||||
string arguments and returns 0, 1 or -1 if strings are equal, a is
|
||||
greater then b or a is lesser then b respectively.
|
||||
|
||||
vim.type_idx *lua-vim.type_idx*
|
||||
Type index for use in |lua-special-tables|. Specifying one of the
|
||||
values from |lua-vim.types| allows typing the empty table (it is
|
||||
unclear whether empty lua table represents empty list or empty array)
|
||||
and forcing integral numbers to be |Float|. See |lua-special-tbl| for
|
||||
more details.
|
||||
|
||||
vim.val_idx *lua-vim.val_idx*
|
||||
Value index for tables representing |Float|s. A table representing
|
||||
floating-point value 1.0 looks like this: >
|
||||
{
|
||||
[vim.type_idx] = vim.types.float,
|
||||
[vim.val_idx] = 1.0,
|
||||
}
|
||||
< See also |lua-vim.type_idx| and |lua-special-tbl|.
|
||||
|
||||
vim.types *lua-vim.types*
|
||||
Table with possible values for |lua-vim.type_idx|. Contains two sets
|
||||
of key-value pairs: first maps possible values for |lua-vim.type_idx|
|
||||
to human-readable strings, second maps human-readable type names to
|
||||
values for |lua-vim.type_idx|. Currently contains pairs for `float`,
|
||||
`array` and `dictionary` types.
|
||||
|
||||
Note: one must expect that values corresponding to `vim.types.float`,
|
||||
`vim.types.array` and `vim.types.dictionary` fall under only two
|
||||
following assumptions:
|
||||
1. Value may serve both as a key and as a value in a table. Given the
|
||||
properties of lua tables this basically means “value is not `nil`”.
|
||||
2. For each value in `vim.types` table `vim.types[vim.types[value]]`
|
||||
is the same as `value`.
|
||||
No other restrictions are put on types, and it is not guaranteed that
|
||||
values corresponding to `vim.types.float`, `vim.types.array` and
|
||||
`vim.types.dictionary` will not change or that `vim.types` table will
|
||||
only contain values for these three types.
|
||||
|
||||
==============================================================================
|
||||
3. The luaeval function *lua-luaeval* *lua-eval*
|
||||
The luaeval function *lua-luaeval* *lua-eval*
|
||||
*luaeval()*
|
||||
|
||||
The (dual) equivalent of "vim.eval" for passing Lua values to Vim is
|
||||
The (dual) equivalent of "vim.eval" for passing Lua values to Nvim 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:
|
||||
@@ -262,7 +315,7 @@ semantically equivalent in Lua to:
|
||||
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
|
||||
tables and booleans are converted to their respective VimL 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
|
||||
@@ -278,6 +331,7 @@ between these cases there is the following agreement:
|
||||
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|.
|
||||
*lua-special-tbl*
|
||||
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
|
||||
|
@@ -124,9 +124,40 @@ static void nlua_error(lua_State *const lstate, const char *const msg)
|
||||
/// 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);
|
||||
size_t s1_len;
|
||||
size_t s2_len;
|
||||
const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
|
||||
const char *s2 = luaL_checklstring(lstate, 2, &s2_len);
|
||||
char *nul1;
|
||||
char *nul2;
|
||||
int ret = 0;
|
||||
assert(s1[s1_len] == NUL);
|
||||
assert(s2[s2_len] == NUL);
|
||||
do {
|
||||
nul1 = memchr(s1, NUL, s1_len);
|
||||
nul2 = memchr(s2, NUL, s2_len);
|
||||
ret = STRICMP(s1, s2);
|
||||
if (ret == 0) {
|
||||
// Compare "a\0" greater then "a".
|
||||
if ((nul1 == NULL) != (nul2 == NULL)) {
|
||||
ret = ((nul1 != NULL) - (nul2 != NULL));
|
||||
break;
|
||||
}
|
||||
if (nul1 != NULL) {
|
||||
assert(nul2 != NULL);
|
||||
// Can't shift both strings by the same amount of bytes: lowercase
|
||||
// letter may have different byte-length than uppercase.
|
||||
s1_len -= (size_t)(nul1 - s1) + 1;
|
||||
s2_len -= (size_t)(nul2 - s2) + 1;
|
||||
s1 = nul1 + 1;
|
||||
s2 = nul2 + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
lua_pop(lstate, 2);
|
||||
lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0)));
|
||||
return 1;
|
||||
@@ -254,10 +285,6 @@ static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
/// 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");
|
||||
@@ -277,13 +304,17 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
nlua_add_api_functions(lstate);
|
||||
// vim.types, vim.type_idx, vim.val_idx
|
||||
nlua_init_types(lstate);
|
||||
// stricmp
|
||||
lua_pushcfunction(lstate, &nlua_stricmp);
|
||||
lua_setfield(lstate, -2, "stricmp");
|
||||
|
||||
lua_setglobal(lstate, "vim");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Initialize lua interpreter
|
||||
///
|
||||
/// Crashes NeoVim if initialization fails. Should be called once per lua
|
||||
/// Crashes Nvim if initialization fails. Should be called once per lua
|
||||
/// interpreter instance.
|
||||
///
|
||||
/// @return New lua interpreter instance.
|
||||
|
108
test/functional/lua/utility_functions_spec.lua
Normal file
108
test/functional/lua/utility_functions_spec.lua
Normal file
@@ -0,0 +1,108 @@
|
||||
-- Test suite for testing interactions with API bindings
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
|
||||
local funcs = helpers.funcs
|
||||
local clear = helpers.clear
|
||||
local eq = helpers.eq
|
||||
|
||||
before_each(clear)
|
||||
|
||||
describe('vim.stricmp', function()
|
||||
-- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has
|
||||
-- length 2 (in bytes).
|
||||
-- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has
|
||||
-- length 3 (in bytes).
|
||||
--
|
||||
-- Note: 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems.
|
||||
-- Note: Built-in Nvim comparison (on systems lacking `strcasecmp`) works
|
||||
-- only on ASCII characters.
|
||||
it('works', function()
|
||||
eq(0, funcs.luaeval('vim.stricmp("a", "A")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("A", "a")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("a", "a")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("A", "A")'))
|
||||
|
||||
eq(0, funcs.luaeval('vim.stricmp("", "")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0", "\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0\\0", "\\0\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0a")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0A")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0a")'))
|
||||
|
||||
eq(0, funcs.luaeval('vim.stricmp("a\\0", "A\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("A\\0", "a\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("a\\0", "a\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("A\\0", "A\\0")'))
|
||||
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0a", "\\0A")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0A", "\\0a")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0a", "\\0a")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0A", "\\0A")'))
|
||||
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0A\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0a\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0a\\0")'))
|
||||
eq(0, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0A\\0")'))
|
||||
|
||||
eq(-1, funcs.luaeval('vim.stricmp("a", "B")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("A", "b")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("a", "b")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("A", "B")'))
|
||||
|
||||
eq(-1, funcs.luaeval('vim.stricmp("", "\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0", "\\0\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0\\0", "\\0\\0\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0A", "\\0\\0\\0b")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0B")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0\\0\\0a", "\\0\\0\\0b")'))
|
||||
|
||||
eq(-1, funcs.luaeval('vim.stricmp("a\\0", "B\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("A\\0", "b\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("a\\0", "b\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("A\\0", "B\\0")'))
|
||||
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0a", "\\0B")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0A", "\\0b")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0a", "\\0b")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0A", "\\0B")'))
|
||||
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0B\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0b\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0a\\0", "\\0b\\0")'))
|
||||
eq(-1, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0B\\0")'))
|
||||
|
||||
eq(1, funcs.luaeval('vim.stricmp("c", "B")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("C", "b")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("c", "b")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("C", "B")'))
|
||||
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0", "")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0\\0", "\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0", "\\0\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0\\0", "\\0\\0\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0C", "\\0\\0\\0b")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0B")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0\\0\\0c", "\\0\\0\\0b")'))
|
||||
|
||||
eq(1, funcs.luaeval('vim.stricmp("c\\0", "B\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("C\\0", "b\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("c\\0", "b\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("C\\0", "B\\0")'))
|
||||
|
||||
eq(1, funcs.luaeval('vim.stricmp("c\\0", "B")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("C\\0", "b")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("c\\0", "b")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("C\\0", "B")'))
|
||||
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0c", "\\0B")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0C", "\\0b")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0c", "\\0b")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0C", "\\0B")'))
|
||||
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0B\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0b\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0b\\0")'))
|
||||
eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")'))
|
||||
end)
|
||||
end)
|
Reference in New Issue
Block a user