trigEngine: optionally restrict length of tested trigger.

This commit is contained in:
L3MON4D3 2024-01-06 22:55:30 +01:00
parent a655485e57
commit 0bef34876a
2 changed files with 93 additions and 56 deletions

View file

@ -256,7 +256,10 @@ local function init_snippet_context(context, opts)
)
engine = trig_engines[engine_name]
end
effective_context.trig_matcher = engine(effective_context.trigger)
-- make sure to pass through nil-trigEngineOpts, they will be recognized and
-- we will get a default-version of that function instead of generating a
-- curried (?) version of it (which would waste space I think).
effective_context.trig_matcher = engine(effective_context.trigger, context.trigEngineOpts)
effective_context.resolveExpandParams = generate_resolve_expand_params_func(
context.condition or opts.condition,

View file

@ -1,55 +1,86 @@
local jsregexp_compile_safe = require("luasnip.util.jsregexp")
-- generate nil-opts-instances here, and save them.
-- This is to prevent generating 100s of the exact same function.
local default_match_pattern, default_match_plain, default_match_vim
local function apply_common_opts(line_to_cursor, opts)
if opts.max_len then
return line_to_cursor:sub(#line_to_cursor - opts.max_len + 1)
else
return line_to_cursor
end
end
-- these functions get the line up to the cursor, the trigger, and then
-- determine whether the trigger matches the current line.
-- If the trigger does not match, the functions shall return nil, otherwise
-- the matching substring and the list of captures (empty table if there aren't
-- any).
local function match_plain(line_to_cursor, trigger)
if
line_to_cursor:sub(#line_to_cursor - #trigger + 1, #line_to_cursor)
== trigger
then
-- no captures for plain trigger.
return trigger, {}
else
return nil
local function match_plain(_, opts)
if opts == nil then
return default_match_plain
end
end
local function match_pattern(line_to_cursor, trigger)
-- look for match which ends at the cursor.
-- put all results into a list, there might be many capture-groups.
local find_res = { line_to_cursor:find(trigger .. "$") }
if #find_res > 0 then
-- if there is a match, determine matching string, and the
-- capture-groups.
local captures = {}
-- find_res[1] is `from`, find_res[2] is `to` (which we already know
-- anyway).
local from = find_res[1]
local match = line_to_cursor:sub(from, #line_to_cursor)
-- collect capture-groups.
for i = 3, #find_res do
captures[i - 2] = find_res[i]
return function(line_to_cursor, trigger)
line_to_cursor = apply_common_opts(line_to_cursor, opts)
if
line_to_cursor:sub(#line_to_cursor - #trigger + 1, #line_to_cursor)
== trigger
then
-- no captures for plain trigger.
return trigger, {}
else
return nil
end
return match, captures
else
return nil
end
end
default_match_plain = match_plain(nil, {})
local function match_pattern(_, opts)
if opts == nil then
return default_match_pattern
end
return function(line_to_cursor, trigger)
line_to_cursor = apply_common_opts(line_to_cursor, opts)
-- look for match which ends at the cursor.
-- put all results into a list, there might be many capture-groups.
local find_res = { line_to_cursor:find(trigger .. "$") }
if #find_res > 0 then
-- if there is a match, determine matching string, and the
-- capture-groups.
local captures = {}
-- find_res[1] is `from`, find_res[2] is `to` (which we already know
-- anyway).
local from = find_res[1]
local match = line_to_cursor:sub(from, #line_to_cursor)
-- collect capture-groups.
for i = 3, #find_res do
captures[i - 2] = find_res[i]
end
return match, captures
else
return nil
end
end
end
default_match_pattern = match_pattern(nil, {})
local ecma_engine
if jsregexp_compile_safe then
ecma_engine = function(trig)
ecma_engine = function(trig, opts)
local trig_compiled, err_maybe = jsregexp_compile_safe(trig .. "$", "")
if not trig_compiled then
error(("Error while compiling regex: %s"):format(err_maybe))
end
return function(line_to_cursor, _)
line_to_cursor = apply_common_opts(line_to_cursor, opts)
-- get first (very likely only, since we appended the "$") match.
local match = trig_compiled(line_to_cursor)[1]
if match then
@ -61,38 +92,41 @@ if jsregexp_compile_safe then
end
end
else
ecma_engine = function()
return match_plain
ecma_engine = function(x, opts)
return match_plain(x, opts)
end
end
local function match_vim(line_to_cursor, trigger)
local matchlist = vim.fn.matchlist(line_to_cursor, trigger .. "$")
if #matchlist > 0 then
local groups = {}
for i = 2, 10 do
-- PROBLEM: vim does not differentiate between an empty ("")
-- and a missing capture.
-- Since we need to differentiate between the two (Check `:h
-- luasnip-variables-lsp-variables`), we assume, here, that an
-- empty string is an unmatched group.
groups[i - 1] = matchlist[i] ~= "" and matchlist[i] or nil
local function match_vim(_, opts)
if opts == nil then
return default_match_vim
end
return function(line_to_cursor, trigger)
line_to_cursor = apply_common_opts(line_to_cursor, opts)
local matchlist = vim.fn.matchlist(line_to_cursor, trigger .. "$")
if #matchlist > 0 then
local groups = {}
for i = 2, 10 do
-- PROBLEM: vim does not differentiate between an empty ("")
-- and a missing capture.
-- Since we need to differentiate between the two (Check `:h
-- luasnip-variables-lsp-variables`), we assume, here, that an
-- empty string is an unmatched group.
groups[i - 1] = matchlist[i] ~= "" and matchlist[i] or nil
end
return matchlist[1], groups
else
return nil
end
return matchlist[1], groups
else
return nil
end
end
default_match_vim = match_vim(nil, {})
return {
plain = function()
return match_plain
end,
pattern = function()
return match_pattern
end,
plain = match_plain,
pattern = match_pattern,
ecma = ecma_engine,
vim = function()
return match_vim
end,
vim = match_vim,
}