From ef6641ba6952fd868d3efafb2a23234ac9bd0d3f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 15 Aug 2017 16:30:32 +0300 Subject: [PATCH 1/5] lua/executor: Make stricmp function work with strings with NULs --- src/nvim/lua/executor.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index eb821f7831..4ed477ea36 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -124,9 +124,36 @@ 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); + // Compare "a\0" greater then "a". + if (ret == 0 && (nul1 == NULL) != (nul2 == NULL)) { + ret = ((nul1 != NULL) - (nul2 != NULL)); + break; + } + if (nul1 != NULL) { + assert(nul2 != NULL); + // Due to lowercase letter having possibly different byte length then + // uppercase letter can’t shift both strings by the same amount of bytes. + s1_len -= (size_t)(nul1 - s1) + 1; + s2_len -= (size_t)(nul2 - s2) + 1; + s1 = nul1 + 1; + s2 = nul2 + 1; + } else { + break; + } + } while (ret == 0); lua_pop(lstate, 2); lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0))); return 1; From 96b1600bc822d72f5a1defa1dafda562ff208b7e Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 15 Aug 2017 16:31:11 +0300 Subject: [PATCH 2/5] functests: Add test for stricmp --- .../functional/lua/utility_functions_spec.lua | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 test/functional/lua/utility_functions_spec.lua diff --git a/test/functional/lua/utility_functions_spec.lua b/test/functional/lua/utility_functions_spec.lua new file mode 100644 index 0000000000..c89a21a968 --- /dev/null +++ b/test/functional/lua/utility_functions_spec.lua @@ -0,0 +1,102 @@ +-- 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('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). + -- For some reason 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems. Also built-in + -- Neovim comparison (i.e. when there is no strcasecmp) works only on ASCII + -- characters. + it('works', function() + eq(0, funcs.luaeval('stricmp("a", "A")')) + eq(0, funcs.luaeval('stricmp("A", "a")')) + eq(0, funcs.luaeval('stricmp("a", "a")')) + eq(0, funcs.luaeval('stricmp("A", "A")')) + + eq(0, funcs.luaeval('stricmp("", "")')) + eq(0, funcs.luaeval('stricmp("\\0", "\\0")')) + eq(0, funcs.luaeval('stricmp("\\0\\0", "\\0\\0")')) + eq(0, funcs.luaeval('stricmp("\\0\\0\\0", "\\0\\0\\0")')) + eq(0, funcs.luaeval('stricmp("\\0\\0\\0A", "\\0\\0\\0a")')) + eq(0, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0A")')) + eq(0, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0a")')) + + eq(0, funcs.luaeval('stricmp("a\\0", "A\\0")')) + eq(0, funcs.luaeval('stricmp("A\\0", "a\\0")')) + eq(0, funcs.luaeval('stricmp("a\\0", "a\\0")')) + eq(0, funcs.luaeval('stricmp("A\\0", "A\\0")')) + + eq(0, funcs.luaeval('stricmp("\\0a", "\\0A")')) + eq(0, funcs.luaeval('stricmp("\\0A", "\\0a")')) + eq(0, funcs.luaeval('stricmp("\\0a", "\\0a")')) + eq(0, funcs.luaeval('stricmp("\\0A", "\\0A")')) + + eq(0, funcs.luaeval('stricmp("\\0a\\0", "\\0A\\0")')) + eq(0, funcs.luaeval('stricmp("\\0A\\0", "\\0a\\0")')) + eq(0, funcs.luaeval('stricmp("\\0a\\0", "\\0a\\0")')) + eq(0, funcs.luaeval('stricmp("\\0A\\0", "\\0A\\0")')) + + eq(-1, funcs.luaeval('stricmp("a", "B")')) + eq(-1, funcs.luaeval('stricmp("A", "b")')) + eq(-1, funcs.luaeval('stricmp("a", "b")')) + eq(-1, funcs.luaeval('stricmp("A", "B")')) + + eq(-1, funcs.luaeval('stricmp("", "\\0")')) + eq(-1, funcs.luaeval('stricmp("\\0", "\\0\\0")')) + eq(-1, funcs.luaeval('stricmp("\\0\\0", "\\0\\0\\0")')) + eq(-1, funcs.luaeval('stricmp("\\0\\0\\0A", "\\0\\0\\0b")')) + eq(-1, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0B")')) + eq(-1, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0b")')) + + eq(-1, funcs.luaeval('stricmp("a\\0", "B\\0")')) + eq(-1, funcs.luaeval('stricmp("A\\0", "b\\0")')) + eq(-1, funcs.luaeval('stricmp("a\\0", "b\\0")')) + eq(-1, funcs.luaeval('stricmp("A\\0", "B\\0")')) + + eq(-1, funcs.luaeval('stricmp("\\0a", "\\0B")')) + eq(-1, funcs.luaeval('stricmp("\\0A", "\\0b")')) + eq(-1, funcs.luaeval('stricmp("\\0a", "\\0b")')) + eq(-1, funcs.luaeval('stricmp("\\0A", "\\0B")')) + + eq(-1, funcs.luaeval('stricmp("\\0a\\0", "\\0B\\0")')) + eq(-1, funcs.luaeval('stricmp("\\0A\\0", "\\0b\\0")')) + eq(-1, funcs.luaeval('stricmp("\\0a\\0", "\\0b\\0")')) + eq(-1, funcs.luaeval('stricmp("\\0A\\0", "\\0B\\0")')) + + eq(1, funcs.luaeval('stricmp("c", "B")')) + eq(1, funcs.luaeval('stricmp("C", "b")')) + eq(1, funcs.luaeval('stricmp("c", "b")')) + eq(1, funcs.luaeval('stricmp("C", "B")')) + + eq(1, funcs.luaeval('stricmp("\\0", "")')) + eq(1, funcs.luaeval('stricmp("\\0\\0", "\\0")')) + eq(1, funcs.luaeval('stricmp("\\0\\0\\0", "\\0\\0")')) + eq(1, funcs.luaeval('stricmp("\\0\\0\\0\\0", "\\0\\0\\0")')) + eq(1, funcs.luaeval('stricmp("\\0\\0\\0C", "\\0\\0\\0b")')) + eq(1, funcs.luaeval('stricmp("\\0\\0\\0c", "\\0\\0\\0B")')) + eq(1, funcs.luaeval('stricmp("\\0\\0\\0c", "\\0\\0\\0b")')) + + eq(1, funcs.luaeval('stricmp("c\\0", "B\\0")')) + eq(1, funcs.luaeval('stricmp("C\\0", "b\\0")')) + eq(1, funcs.luaeval('stricmp("c\\0", "b\\0")')) + eq(1, funcs.luaeval('stricmp("C\\0", "B\\0")')) + + eq(1, funcs.luaeval('stricmp("\\0c", "\\0B")')) + eq(1, funcs.luaeval('stricmp("\\0C", "\\0b")')) + eq(1, funcs.luaeval('stricmp("\\0c", "\\0b")')) + eq(1, funcs.luaeval('stricmp("\\0C", "\\0B")')) + + eq(1, funcs.luaeval('stricmp("\\0c\\0", "\\0B\\0")')) + eq(1, funcs.luaeval('stricmp("\\0C\\0", "\\0b\\0")')) + eq(1, funcs.luaeval('stricmp("\\0c\\0", "\\0b\\0")')) + eq(1, funcs.luaeval('stricmp("\\0C\\0", "\\0B\\0")')) + end) +end) From 93ef823f5e5347e685b4a69fff487278d0b4ed87 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 15 Aug 2017 16:32:06 +0300 Subject: [PATCH 3/5] =?UTF-8?q?lua/executor:=20Move=20stricmp=20to=20vim?= =?UTF-8?q?=20=E2=80=9Cmodule=E2=80=9D=20and=20document=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/doc/if_lua.txt | 5 + src/nvim/lua/executor.c | 8 +- .../functional/lua/utility_functions_spec.lua | 138 +++++++++--------- 3 files changed, 78 insertions(+), 73 deletions(-) diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index c4efd57b45..515864555b 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -246,6 +246,11 @@ 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|. +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. + ============================================================================== 3. The luaeval function *lua-luaeval* *lua-eval* *luaeval()* diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 4ed477ea36..90333be541 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -281,10 +281,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"); @@ -304,6 +300,10 @@ 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; } diff --git a/test/functional/lua/utility_functions_spec.lua b/test/functional/lua/utility_functions_spec.lua index c89a21a968..c2c61299b1 100644 --- a/test/functional/lua/utility_functions_spec.lua +++ b/test/functional/lua/utility_functions_spec.lua @@ -7,7 +7,7 @@ local eq = helpers.eq before_each(clear) -describe('stricmp', function() +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 @@ -16,87 +16,87 @@ describe('stricmp', function() -- Neovim comparison (i.e. when there is no strcasecmp) works only on ASCII -- characters. it('works', function() - eq(0, funcs.luaeval('stricmp("a", "A")')) - eq(0, funcs.luaeval('stricmp("A", "a")')) - eq(0, funcs.luaeval('stricmp("a", "a")')) - eq(0, funcs.luaeval('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("A", "A")')) - eq(0, funcs.luaeval('stricmp("", "")')) - eq(0, funcs.luaeval('stricmp("\\0", "\\0")')) - eq(0, funcs.luaeval('stricmp("\\0\\0", "\\0\\0")')) - eq(0, funcs.luaeval('stricmp("\\0\\0\\0", "\\0\\0\\0")')) - eq(0, funcs.luaeval('stricmp("\\0\\0\\0A", "\\0\\0\\0a")')) - eq(0, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0A")')) - eq(0, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0a")')) + 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('stricmp("a\\0", "A\\0")')) - eq(0, funcs.luaeval('stricmp("A\\0", "a\\0")')) - eq(0, funcs.luaeval('stricmp("a\\0", "a\\0")')) - eq(0, funcs.luaeval('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("A\\0", "A\\0")')) - eq(0, funcs.luaeval('stricmp("\\0a", "\\0A")')) - eq(0, funcs.luaeval('stricmp("\\0A", "\\0a")')) - eq(0, funcs.luaeval('stricmp("\\0a", "\\0a")')) - eq(0, funcs.luaeval('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", "\\0A")')) - eq(0, funcs.luaeval('stricmp("\\0a\\0", "\\0A\\0")')) - eq(0, funcs.luaeval('stricmp("\\0A\\0", "\\0a\\0")')) - eq(0, funcs.luaeval('stricmp("\\0a\\0", "\\0a\\0")')) - eq(0, funcs.luaeval('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(0, funcs.luaeval('vim.stricmp("\\0A\\0", "\\0A\\0")')) - eq(-1, funcs.luaeval('stricmp("a", "B")')) - eq(-1, funcs.luaeval('stricmp("A", "b")')) - eq(-1, funcs.luaeval('stricmp("a", "b")')) - eq(-1, funcs.luaeval('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("A", "B")')) - eq(-1, funcs.luaeval('stricmp("", "\\0")')) - eq(-1, funcs.luaeval('stricmp("\\0", "\\0\\0")')) - eq(-1, funcs.luaeval('stricmp("\\0\\0", "\\0\\0\\0")')) - eq(-1, funcs.luaeval('stricmp("\\0\\0\\0A", "\\0\\0\\0b")')) - eq(-1, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0B")')) - eq(-1, funcs.luaeval('stricmp("\\0\\0\\0a", "\\0\\0\\0b")')) + 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('stricmp("a\\0", "B\\0")')) - eq(-1, funcs.luaeval('stricmp("A\\0", "b\\0")')) - eq(-1, funcs.luaeval('stricmp("a\\0", "b\\0")')) - eq(-1, funcs.luaeval('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("A\\0", "B\\0")')) - eq(-1, funcs.luaeval('stricmp("\\0a", "\\0B")')) - eq(-1, funcs.luaeval('stricmp("\\0A", "\\0b")')) - eq(-1, funcs.luaeval('stricmp("\\0a", "\\0b")')) - eq(-1, funcs.luaeval('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", "\\0B")')) - eq(-1, funcs.luaeval('stricmp("\\0a\\0", "\\0B\\0")')) - eq(-1, funcs.luaeval('stricmp("\\0A\\0", "\\0b\\0")')) - eq(-1, funcs.luaeval('stricmp("\\0a\\0", "\\0b\\0")')) - eq(-1, funcs.luaeval('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("\\0A\\0", "\\0B\\0")')) - eq(1, funcs.luaeval('stricmp("c", "B")')) - eq(1, funcs.luaeval('stricmp("C", "b")')) - eq(1, funcs.luaeval('stricmp("c", "b")')) - eq(1, funcs.luaeval('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("C", "B")')) - eq(1, funcs.luaeval('stricmp("\\0", "")')) - eq(1, funcs.luaeval('stricmp("\\0\\0", "\\0")')) - eq(1, funcs.luaeval('stricmp("\\0\\0\\0", "\\0\\0")')) - eq(1, funcs.luaeval('stricmp("\\0\\0\\0\\0", "\\0\\0\\0")')) - eq(1, funcs.luaeval('stricmp("\\0\\0\\0C", "\\0\\0\\0b")')) - eq(1, funcs.luaeval('stricmp("\\0\\0\\0c", "\\0\\0\\0B")')) - eq(1, funcs.luaeval('stricmp("\\0\\0\\0c", "\\0\\0\\0b")')) + 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('stricmp("c\\0", "B\\0")')) - eq(1, funcs.luaeval('stricmp("C\\0", "b\\0")')) - eq(1, funcs.luaeval('stricmp("c\\0", "b\\0")')) - eq(1, funcs.luaeval('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\\0")')) - eq(1, funcs.luaeval('stricmp("\\0c", "\\0B")')) - eq(1, funcs.luaeval('stricmp("\\0C", "\\0b")')) - eq(1, funcs.luaeval('stricmp("\\0c", "\\0b")')) - eq(1, funcs.luaeval('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", "\\0B")')) - eq(1, funcs.luaeval('stricmp("\\0c\\0", "\\0B\\0")')) - eq(1, funcs.luaeval('stricmp("\\0C\\0", "\\0b\\0")')) - eq(1, funcs.luaeval('stricmp("\\0c\\0", "\\0b\\0")')) - eq(1, funcs.luaeval('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")')) + eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")')) end) end) From b1a8dcefeee0bb999a350de2cc38fc08df679bf6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 15 Aug 2017 16:41:43 +0300 Subject: [PATCH 4/5] lua/executor: Fix crash when first string contains NUL and second not --- src/nvim/lua/executor.c | 33 +++++++++++-------- .../functional/lua/utility_functions_spec.lua | 5 +++ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 90333be541..54973a6e94 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -137,23 +137,28 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL nul1 = memchr(s1, NUL, s1_len); nul2 = memchr(s2, NUL, s2_len); ret = STRICMP(s1, s2); - // Compare "a\0" greater then "a". - if (ret == 0 && (nul1 == NULL) != (nul2 == NULL)) { - ret = ((nul1 != NULL) - (nul2 != NULL)); - break; - } - if (nul1 != NULL) { - assert(nul2 != NULL); - // Due to lowercase letter having possibly different byte length then - // uppercase letter can’t shift both strings by the same amount of bytes. - s1_len -= (size_t)(nul1 - s1) + 1; - s2_len -= (size_t)(nul2 - s2) + 1; - s1 = nul1 + 1; - s2 = nul2 + 1; + 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); + // Due to lowercase letter having possibly different byte length then + // uppercase letter can’t shift both strings by the same amount of + // bytes. + 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 (ret == 0); + } while (true); lua_pop(lstate, 2); lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0))); return 1; diff --git a/test/functional/lua/utility_functions_spec.lua b/test/functional/lua/utility_functions_spec.lua index c2c61299b1..9855a05e8c 100644 --- a/test/functional/lua/utility_functions_spec.lua +++ b/test/functional/lua/utility_functions_spec.lua @@ -89,6 +89,11 @@ describe('vim.stricmp', function() 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")')) From dd1943a3a77793d3c1b8488ca70a09fefed6fb42 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 16 Aug 2017 00:44:52 +0300 Subject: [PATCH 5/5] =?UTF-8?q?doc:=20Describe=20everything=20what=20is=20?= =?UTF-8?q?in=20`vim`=20lua=20=E2=80=9Cmodule=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ci skip] --- runtime/doc/if_lua.txt | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 515864555b..49b4c6dce1 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -243,14 +243,50 @@ position are restricted when the command is executed in the |sandbox|. 2. The vim module *lua-vim* Lua interfaces Vim through the "vim" module. Currently it only has `api` -submodule which is a table with all API functions. Descriptions of these -functions may be found in |api.txt|. +submodule and some Neovim-specific utility values which is a table with all +API functions. Descriptions of the API functions may be found in |api.txt|. +Description of other utilities is below: 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* *luaeval()* @@ -283,6 +319,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