From e449e6e325672aa786bb876fc44e514014e19758 Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Wed, 29 Nov 2023 23:01:28 +0100 Subject: [PATCH] update jsregexp to 0.0.6. Still continues to work with 0.0.5 too, but also support 0.0.6, which, despite having the same API, is a bit harder to set up (see `util/jsregexp.lua`) --- .github/workflows/luarocks.yml | 2 +- .gitignore | 2 + .gitmodules | 5 +- Makefile | 22 +++++--- deps/jsregexp | 2 +- deps/jsregexp005 | 1 + lua/luasnip/nodes/util/trig_engines.lua | 6 +-- lua/luasnip/util/jsregexp.lua | 40 ++++++++++++++ lua/luasnip/util/parser/ast_utils.lua | 66 ++++++++++++----------- lua/luasnip/util/util.lua | 7 --- tests/helpers.lua | 52 ++++++++++++++++-- tests/integration/parser_spec.lua | 34 ++++-------- tests/integration/session_spec.lua | 1 - tests/integration/snippet_basics_spec.lua | 1 - 14 files changed, 161 insertions(+), 80 deletions(-) create mode 160000 deps/jsregexp005 create mode 100644 lua/luasnip/util/jsregexp.lua diff --git a/.github/workflows/luarocks.yml b/.github/workflows/luarocks.yml index 6ac0894..10c4df3 100644 --- a/.github/workflows/luarocks.yml +++ b/.github/workflows/luarocks.yml @@ -15,4 +15,4 @@ jobs: LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} with: dependencies: | - jsregexp == 0.0.5 + jsregexp >= 0.0.5, <= 0.0.6 diff --git a/.gitignore b/.gitignore index 10938ed..a3dff68 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /deps/nvim_multiversion /doc/tags /lua/luasnip-jsregexp.so +/deps/luasnip-jsregexp.so +/lua/luasnip-jsregexp.lua diff --git a/.gitmodules b/.gitmodules index b9bfa32..8fc43e9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "deps/jsregexp"] path = deps/jsregexp - url = ../../kmarius/jsregexp/ + url = ../../kmarius/jsregexp +[submodule "deps/jsregexp005"] + path = deps/jsregexp005 + url = ../../kmarius/jsregexp diff --git a/Makefile b/Makefile index 0b5e2aa..7526b1b 100644 --- a/Makefile +++ b/Makefile @@ -40,32 +40,42 @@ ifeq ($(LUASNIP_DETECTED_OS),Darwin) # remove -bundle, should be equivalent to the -shared hardcoded by jsregexp. LUA_LDLIBS=-undefined dynamic_lookup -all_load endif + JSREGEXP_PATH=deps/jsregexp +JSREGEXP005_PATH=deps/jsregexp005 jsregexp: git submodule init git submodule update make "INCLUDE_DIR=-I$(shell pwd)/deps/lua51_include/" LDLIBS="${LUA_LDLIBS}" -C ${JSREGEXP_PATH} + make "INCLUDE_DIR=-I$(shell pwd)/deps/lua51_include/" LDLIBS="${LUA_LDLIBS}" -C ${JSREGEXP005_PATH} install_jsregexp: jsregexp - # access via require("luasnip-jsregexp") - # The hyphen must be used here, otherwise the luaopen_*-call will fail. - # See the package.loaders-section [here](https://www.lua.org/manual/5.1/manual.html#pdf-require) - cp "$(shell pwd)/${JSREGEXP_PATH}/jsregexp.so" "$(shell pwd)/lua/luasnip-jsregexp.so" + # remove old binary. + rm "$(shell pwd)/lua/luasnip-jsregexp.so" || true + # there is some additional trickery to make this work with jsregexp-0.0.6 in + # util/jsregexp.lua. + cp "$(shell pwd)/${JSREGEXP_PATH}/jsregexp.lua" "$(shell pwd)/lua/luasnip-jsregexp.lua" + # just move out of jsregexp-directory, so it is not accidentially deleted. + cp "$(shell pwd)/${JSREGEXP_PATH}/jsregexp.so" "$(shell pwd)/deps/luasnip-jsregexp.so" uninstall_jsregexp: + # also remove binaries of older version. rm "$(shell pwd)/lua/luasnip-jsregexp.so" + rm "$(shell pwd)/lua/deps/luasnip-jsregexp.so" + rm "$(shell pwd)/lua/luasnip-jsregexp.lua" TEST_07?=true TEST_09?=true TEST_MASTER?=true # Expects to be run from repo-location (eg. via `make -C path/to/luasnip`). -test: nvim jsregexp +test: nvim install_jsregexp # unset PATH and CPATH to prevent system-env leaking into the neovim-build, # add our helper-functions to lpath. # exit as soon as an error occurs. unset LUA_PATH LUA_CPATH; \ export LUASNIP_SOURCE=$(shell pwd); \ - export JSREGEXP_PATH=$(shell pwd)/${JSREGEXP_PATH}; \ + export JSREGEXP_ABS_PATH=$(shell pwd)/${JSREGEXP_PATH}; \ + export JSREGEXP005_ABS_PATH=$(shell pwd)/${JSREGEXP005_PATH}; \ export TEST_FILE=$(realpath ${TEST_FILE}); \ export BUSTED_ARGS=--lpath=$(shell pwd)/tests/?.lua; \ set -e; \ diff --git a/deps/jsregexp b/deps/jsregexp index c3e4732..b5a81e2 160000 --- a/deps/jsregexp +++ b/deps/jsregexp @@ -1 +1 @@ -Subproject commit c3e473240eebb65a8870abebafeff83b6c9e7f16 +Subproject commit b5a81e21d0875667ba2458ac8ae903afd5568698 diff --git a/deps/jsregexp005 b/deps/jsregexp005 new file mode 160000 index 0000000..dd65498 --- /dev/null +++ b/deps/jsregexp005 @@ -0,0 +1 @@ +Subproject commit dd65498ae2c29b882d6c02c0a30577b08d660b94 diff --git a/lua/luasnip/nodes/util/trig_engines.lua b/lua/luasnip/nodes/util/trig_engines.lua index e00f381..a340b30 100644 --- a/lua/luasnip/nodes/util/trig_engines.lua +++ b/lua/luasnip/nodes/util/trig_engines.lua @@ -1,4 +1,4 @@ -local jsregexp = require("luasnip.util.util").jsregexp +local jsregexp_compile_safe = require("luasnip.util.jsregexp") -- these functions get the line up to the cursor, the trigger, and then -- determine whether the trigger matches the current line. @@ -42,9 +42,9 @@ local function match_pattern(line_to_cursor, trigger) end local ecma_engine -if jsregexp then +if jsregexp_compile_safe then ecma_engine = function(trig) - local trig_compiled, err_maybe = jsregexp.compile(trig .. "$", "") + local trig_compiled, err_maybe = jsregexp_compile_safe(trig .. "$", "") if not trig_compiled then error(("Error while compiling regex: %s"):format(err_maybe)) end diff --git a/lua/luasnip/util/jsregexp.lua b/lua/luasnip/util/jsregexp.lua new file mode 100644 index 0000000..8912370 --- /dev/null +++ b/lua/luasnip/util/jsregexp.lua @@ -0,0 +1,40 @@ +local Path = require("luasnip.util.path") + +-- neovim-loader does not handle module-names with dots correctly, so for +-- jsregexp-0.0.6, the call to require("jsregexp.core") in jsregexp.lua errors +-- even if the library is in rtp. +-- Resolve path to jsregexp.so manually, and loadlib it in preload (and remove +-- preload after requires are done and have failed/worked). + +-- omit "@". +local this_file = debug.getinfo(1).source:sub(2) +local repo_dir = vim.fn.fnamemodify(this_file, ":h:h:h:h") +local jsregexp_core_path = Path.join(repo_dir, "deps", "luasnip-jsregexp.so") + +-- rather gracefully, if the path does not exist, or loadlib can't do its job +-- for some other reason, the preload will be set to nil, ie not be set. +-- +-- This means we don't hinder a regularly installed 0.0.6-jsregexp-library, +-- since its `require("jsregexp.core")` will be unaffected. +package.preload["jsregexp.core"] = package.loadlib(jsregexp_core_path, "luaopen_jsregexp_core") + +-- jsregexp: first try loading the version installed by luasnip, then global ones. +local jsregexp_ok, jsregexp = pcall(require, "luasnip-jsregexp") +if not jsregexp_ok then + jsregexp_ok, jsregexp = pcall(require, "jsregexp") +end + +-- don't want to affect other requires. +package.preload["jsregexp.core"] = nil + +if not jsregexp_ok then + return false +end + +-- detect version, and return compile-function. +-- 0.0.6-compile_safe and 0.0.5-compile behave the same, ie. nil, err on error. +if jsregexp.compile_safe then + return jsregexp.compile_safe +else + return jsregexp.compile +end diff --git a/lua/luasnip/util/parser/ast_utils.lua b/lua/luasnip/util/parser/ast_utils.lua index c022e53..a577454 100644 --- a/lua/luasnip/util/parser/ast_utils.lua +++ b/lua/luasnip/util/parser/ast_utils.lua @@ -3,7 +3,7 @@ local types = Ast.node_type local util = require("luasnip.util.util") local Str = require("luasnip.util.str") local log = require("luasnip.util.log").new("parser") -local jsregexp = require("luasnip.util.util").jsregexp +local jsregexp_compile_safe = require("luasnip.util.jsregexp") local directed_graph = require("luasnip.util.directed_graph") @@ -303,40 +303,46 @@ local function apply_transform_format(nodes, captures) end function M.apply_transform(transform) - if jsregexp then - local reg_compiled = - jsregexp.compile(transform.pattern, transform.option) - -- can be passed to functionNode! - return function(lines) - -- luasnip expects+passes lines as list, but regex needs one string. - lines = table.concat(lines, "\n") - local matches = reg_compiled(lines) + if jsregexp_compile_safe then + local reg_compiled, err = + jsregexp_compile_safe(transform.pattern, transform.option) - local transformed = "" - -- index one past the end of previous match. - -- This is used to append unmatched characters to `transformed`, so - -- it's initialized such that the first append is from 1. - local prev_match_end = 0 - for _, match in ipairs(matches) do - -- begin_ind and end_ind are inclusive. - transformed = transformed - .. lines:sub(prev_match_end + 1, match.begin_ind - 1) - .. apply_transform_format(transform.format, match.groups) + if reg_compiled then + -- can be passed to functionNode! + return function(lines) + -- luasnip expects+passes lines as list, but regex needs one string. + lines = table.concat(lines, "\n") + local matches = reg_compiled(lines) - -- end-inclusive - prev_match_end = match.end_ind + local transformed = "" + -- index one past the end of previous match. + -- This is used to append unmatched characters to `transformed`, so + -- it's initialized such that the first append is from 1. + local prev_match_end = 0 + for _, match in ipairs(matches) do + -- begin_ind and end_ind are inclusive. + transformed = transformed + .. lines:sub(prev_match_end + 1, match.begin_ind - 1) + .. apply_transform_format(transform.format, match.groups) + + -- end-inclusive + prev_match_end = match.end_ind + end + transformed = transformed .. lines:sub(prev_match_end + 1, #lines) + + return vim.split(transformed, "\n") end - transformed = transformed .. lines:sub(prev_match_end + 1, #lines) - - return vim.split(transformed, "\n") + else + log.error("Failed parsing regex `%s` with options `%s`: %s", transform.pattern, transform.option, err) + -- fall through to returning identity. end - else - -- without jsregexp, we cannot properly transform whatever is supposed to - -- be transformed here. - -- Just return a function that returns the to-be-transformed string - -- unmodified. - return util.id end + + -- without jsregexp, or without a valid regex, we cannot properly transform + -- whatever is supposed to be transformed here. + -- Just return a function that returns the to-be-transformed string + -- unmodified. + return util.id end ---Variables need the text which is in front of them to determine whether they diff --git a/lua/luasnip/util/util.lua b/lua/luasnip/util/util.lua index 0d37632..e510699 100644 --- a/lua/luasnip/util/util.lua +++ b/lua/luasnip/util/util.lua @@ -1,11 +1,5 @@ local session = require("luasnip.session") --- jsregexp: first try loading the version installed by luasnip, then global ones. -local jsregexp_ok, jsregexp = pcall(require, "luasnip-jsregexp") -if not jsregexp_ok then - jsregexp_ok, jsregexp = pcall(require, "jsregexp") -end - local function get_cursor_0ind() local c = vim.api.nvim_win_get_cursor(0) c[1] = c[1] - 1 @@ -528,6 +522,5 @@ return { indx_of = indx_of, lazy_table = lazy_table, ternary = ternary, - jsregexp = jsregexp_ok and jsregexp, pos_cmp = pos_cmp, } diff --git a/tests/helpers.lua b/tests/helpers.lua index bbfd2a8..d8fbb96 100644 --- a/tests/helpers.lua +++ b/tests/helpers.lua @@ -5,11 +5,53 @@ local assert = require("luassert") local M = {} -function M.setup_jsregexp() - -- append default-path. - exec_lua( - ('package.cpath = "%s"'):format(os.getenv("JSREGEXP_PATH") .. "/?.so;;") - ) +function M.jsregexp_it(it, name, fn) + for _, version in ipairs({"005", "006", "luasnip"}) do + it(name .. " (jsregexp-" .. version .. ")", function() + exec_lua([[ + local version, jsregexp_005_path, jsregexp_path = ... + if version ~= "luasnip" then + if version == "005" then + package.preload["jsregexp"] = package.loadlib(jsregexp_005_path .. "/jsregexp.so", "luaopen_jsregexp") + + if package.preload["jsregexp"]().compile_safe then + error("wrong jsregexp-version loaded") + end + else + package.preload["jsregexp.core"] = package.loadlib(jsregexp_path .. "/jsregexp.so", "luaopen_jsregexp_core") + package.path = jsregexp_path .. "/?.lua;;" + -- populate package now, before jsregexp-core-preload is overwritten in util/jsregexp.lua. + -- also load it to check the version. + local jsregexp = require("jsregexp") + -- is actually 0.0.6. + if not jsregexp.compile_safe then + error("wrong jsregexp-version loaded") + end + end + + -- don't accidentially load luasnip-jsregexp with unknown version. + local old_require = require + require = function(modulename) + if modulename == "luasnip-jsregexp" then + error("Disabled by `prevent_jsregexp`") + end + return old_require(modulename) + end + else + -- don't accidentially load regular jsregexp. + local old_require = require + require = function(modulename) + if modulename == "jsregexp" then + error("Disabled by `prevent_jsregexp`") + end + return old_require(modulename) + end + end + ]], version, os.getenv("JSREGEXP005_ABS_PATH"), os.getenv("JSREGEXP_ABS_PATH")) + + fn() + end) + end end function M.prevent_jsregexp() diff --git a/tests/integration/parser_spec.lua b/tests/integration/parser_spec.lua index af7de22..cad82d3 100644 --- a/tests/integration/parser_spec.lua +++ b/tests/integration/parser_spec.lua @@ -205,8 +205,7 @@ describe("Parser", function() }) end) - it("can parse transformed variables.", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "can parse transformed variables", function() ls_helpers.session_setup_luasnip() local snip = '"a${TM_LINE_INDEX/(.*)/asdf $1 asdf/g}a"' @@ -244,8 +243,7 @@ describe("Parser", function() }) end) - it("can parse transformed tabstop.", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "can parse transformed tabstop.", function() ls_helpers.session_setup_luasnip() local snip = '"$1 a ${1/(.*)/asdf $1 asdf/} a"' @@ -297,8 +295,7 @@ describe("Parser", function() error("unexpected key " .. modifier .. " in expected_map") end - it("applies " .. modifier .. " correctly", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "applies " .. modifier .. " correctly", function() ls_helpers.session_setup_luasnip() local snip = ('"${1:%s} ${1/(.*)/${1:/%s}/}"'):format( text, @@ -406,12 +403,7 @@ describe("Parser", function() {2:-- SELECT --} |]], }) exec_lua("ls.jump(1)") - screen:expect({ - grid = [[ - ^q{3:wer} asdf | - {0:~ }| - {2:-- SELECT --} |]], - }) + screen:expect({ unchanged = true }) end) it("turns the correct nodes into insert/functionNode", function() @@ -450,8 +442,7 @@ describe("Parser", function() }) end) - it("can modify groups in transform.", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "can modify groups in transform.", function() ls_helpers.session_setup_luasnip() local snip = '"$1 a ${1/(.*)/asdf ${1:/upcase} asdf/} a"' @@ -474,8 +465,7 @@ describe("Parser", function() }) end) - it("handle multiple captures in transform.", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "handle multiple captures in transform.", function() ls_helpers.session_setup_luasnip() local snip = '"${1:bbb} a ${1/(.)b(.)/${1:/upcase} $2/g} a"' @@ -703,8 +693,7 @@ describe("Parser", function() }) end) - it("Applies transform to empty variable.", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "Applies transform to empty variable.", function() ls_helpers.session_setup_luasnip() local snip = "${TM_SELECTED_TEXT/(.*)/ asd /}" @@ -718,8 +707,7 @@ describe("Parser", function() }) end) - it("correctly transforms multiline-values.", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "correctly transforms multiline-values.", function() ls_helpers.session_setup_luasnip() local snip = "${TM_SELECTED_TEXT/([^]*)/a ${1} a/}" @@ -737,10 +725,9 @@ describe("Parser", function() }) end) - it( + ls_helpers.jsregexp_it(it, "correctly transforms if the match does not include the first character.", function() - ls_helpers.setup_jsregexp() ls_helpers.session_setup_luasnip() local snip = "${1:asdf.asdf} ${1/[\\.]/-/g}" @@ -872,8 +859,7 @@ describe("Parser", function() }) end) - it("Correctly parses unescaped characters.", function() - ls_helpers.setup_jsregexp() + ls_helpers.jsregexp_it(it, "Correctly parses unescaped characters.", function() ls_helpers.session_setup_luasnip() local snip = "${} asdf" diff --git a/tests/integration/session_spec.lua b/tests/integration/session_spec.lua index 3cb3022..a7a7155 100644 --- a/tests/integration/session_spec.lua +++ b/tests/integration/session_spec.lua @@ -20,7 +20,6 @@ describe("session", function() before_each(function() helpers.clear() - ls_helpers.setup_jsregexp() ls_helpers.session_setup_luasnip({ hl_choiceNode = true }) -- add a rather complicated snippet. diff --git a/tests/integration/snippet_basics_spec.lua b/tests/integration/snippet_basics_spec.lua index bdb55c0..bb6c851 100644 --- a/tests/integration/snippet_basics_spec.lua +++ b/tests/integration/snippet_basics_spec.lua @@ -8,7 +8,6 @@ describe("snippets_basic", function() before_each(function() helpers.clear() - ls_helpers.setup_jsregexp() ls_helpers.session_setup_luasnip() screen = Screen.new(50, 3)