aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2017-10-22 16:29:36 +0200
committerJustin M. Keyes <justinkz@gmail.com>2017-10-22 16:29:36 +0200
commit42b80c3acfbb0f813a62c382b947d7d0d5477009 (patch)
tree41fd13950d9e2c410f97815c27836fd89514131d
parent9db42d4ce99c5b5747e9d83045b37e93d55c4249 (diff)
parentdd1943a3a77793d3c1b8488ca70a09fefed6fb42 (diff)
downloadrneovim-42b80c3acfbb0f813a62c382b947d7d0d5477009.tar.gz
rneovim-42b80c3acfbb0f813a62c382b947d7d0d5477009.tar.bz2
rneovim-42b80c3acfbb0f813a62c382b947d7d0d5477009.zip
Merge #7165 'lua: Move stricmp to vim module'
-rw-r--r--runtime/doc/if_lua.txt102
-rw-r--r--src/nvim/lua/executor.c47
-rw-r--r--test/functional/lua/utility_functions_spec.lua108
3 files changed, 225 insertions, 32 deletions
diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt
index d1b8cd4856..a123041866 100644
--- a/runtime/doc/if_lua.txt
+++ b/runtime/doc/if_lua.txt
@@ -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
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index eb821f7831..16bb4169c4 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -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.
diff --git a/test/functional/lua/utility_functions_spec.lua b/test/functional/lua/utility_functions_spec.lua
new file mode 100644
index 0000000000..d5756e134d
--- /dev/null
+++ b/test/functional/lua/utility_functions_spec.lua
@@ -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)