From 48a79b7f810fb2fcf4f1822587fdd31b6b8b04ef Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Tue, 8 Mar 2022 12:52:39 +0100 Subject: [PATCH] Breaking Change: split ext_opts into node_ext_opts and child_ext_opts. The former is applied to this specific node, the latter to children of that snippetNode. Also add merge_{node,child}_ext_opts for control merge-behaviour of eg. a nodes ext_opts with that of the surrounding snippet (both default to true). This is a breaking change because function/dynamicNode no longer have the variable number of user_args as their last arg. That is now a table, `opts`, which may contain the `user_args`. ```lua d(pos, func, argnodes, user_arg1, user_arg2) -- becomes d(pos, func, argnodes, {user_args = {user_arg1, user_arg2}}) -- and likewise for functionNode. ``` --- lua/luasnip/config.lua | 11 +- lua/luasnip/nodes/choiceNode.lua | 12 +-- lua/luasnip/nodes/dynamicNode.lua | 36 +++---- lua/luasnip/nodes/functionNode.lua | 8 +- lua/luasnip/nodes/insertNode.lua | 10 +- lua/luasnip/nodes/node.lua | 53 ++++++++-- lua/luasnip/nodes/restoreNode.lua | 21 ++-- lua/luasnip/nodes/snippet.lua | 152 +++++++++++++++++---------- lua/luasnip/nodes/snippetProxy.lua | 12 ++- lua/luasnip/nodes/textNode.lua | 4 +- lua/luasnip/nodes/util.lua | 25 ++++- lua/luasnip/util/ext_opts.lua | 161 +++++++++++++++++------------ 12 files changed, 321 insertions(+), 184 deletions(-) diff --git a/lua/luasnip/config.lua b/lua/luasnip/config.lua index 8bca77682..d2ffb6e89 100644 --- a/lua/luasnip/config.lua +++ b/lua/luasnip/config.lua @@ -71,7 +71,7 @@ local defaults = { }, }, ext_base_prio = 200, - ext_prio_increase = 7, + ext_prio_increase = 9, enable_autosnippets = false, -- default applied in util.parser, requires iNode, cNode -- (Dependency cycle if here). @@ -90,10 +90,11 @@ c = { -- remove unused highlights from default-ext_opts. ext_util.clear_invalid(conf.ext_opts) - ext_util.complete(conf.ext_opts) - user_config.ext_opts = user_config.ext_opts or {} - ext_util.complete(user_config.ext_opts) - ext_util.extend(user_config.ext_opts, conf.ext_opts) + conf.ext_opts = ext_util.child_complete(conf.ext_opts) + user_config.ext_opts = ext_util.child_complete( + user_config.ext_opts or {} + ) + ext_util.child_extend(user_config.ext_opts, conf.ext_opts) for k, v in pairs(user_config) do conf[k] = v diff --git a/lua/luasnip/nodes/choiceNode.lua b/lua/luasnip/nodes/choiceNode.lua index 1e1552ab6..3c2a7fdef 100644 --- a/lua/luasnip/nodes/choiceNode.lua +++ b/lua/luasnip/nodes/choiceNode.lua @@ -69,7 +69,7 @@ local function C(pos, choices, opts) dependents = {}, -- default to true. restore_cursor = opts.restore_cursor, - }) + }, opts) c:init_nodes() return c end @@ -112,7 +112,7 @@ function ChoiceNode:put_initial(pos) local mark_opts = vim.tbl_extend("keep", { right_gravity = false, end_right_gravity = false, - }, self.parent.ext_opts[self.active_choice.type].passive) + }, self.active_choice.ext_opts.passive) self.active_choice.mark = mark(old_pos, pos, mark_opts) self.visible = true @@ -131,7 +131,7 @@ function ChoiceNode:expand_tabs(tabwidth) end function ChoiceNode:input_enter() - self.mark:update_opts(self.parent.ext_opts[self.type].active) + self.mark:update_opts(self.ext_opts.active) self.parent:enter_node(self.indx) self.prev_choice_node = session.active_choice_node @@ -144,7 +144,7 @@ end function ChoiceNode:input_leave() self:event(events.leave) - self.mark:update_opts(self.parent.ext_opts[self.type].passive) + self.mark:update_opts(self.ext_opts.passive) self:update_dependents() session.active_choice_node = self.prev_choice_node self.active = false @@ -240,7 +240,7 @@ function ChoiceNode:set_choice(choice, current_node) self.active_choice = choice self.active_choice.mark = self.mark:copy_pos_gravs( - vim.deepcopy(self.parent.ext_opts[self.active_choice.type].passive) + vim.deepcopy(self.active_choice.ext_opts.passive) ) self.active_choice:put_initial(self.mark:pos_begin_raw()) @@ -322,7 +322,7 @@ function ChoiceNode:set_mark_rgrav(rgrav_beg, rgrav_end) end function ChoiceNode:set_ext_opts(name) - self.mark:update_opts(self.parent.ext_opts[self.type][name]) + self.mark:update_opts(self.ext_opts[name]) self.active_choice:set_ext_opts(name) end diff --git a/lua/luasnip/nodes/dynamicNode.lua b/lua/luasnip/nodes/dynamicNode.lua index cf6d9e00d..84fb72288 100644 --- a/lua/luasnip/nodes/dynamicNode.lua +++ b/lua/luasnip/nodes/dynamicNode.lua @@ -9,22 +9,24 @@ local conf = require("luasnip.config") local FunctionNode = require("luasnip.nodes.functionNode").FunctionNode local SnippetNode = require("luasnip.nodes.snippet").SN -local function D(pos, fn, args, ...) +local function D(pos, fn, args, opts) + opts = opts or {} + return DynamicNode:new({ pos = pos, fn = fn, args = node_util.wrap_args(args), type = types.dynamicNode, mark = nil, - user_args = { ... }, + user_args = opts.user_args or {}, dependents = {}, active = false, - }) + }, opts) end function DynamicNode:input_enter() self.active = true - self.mark:update_opts(self.parent.ext_opts[self.type].active) + self.mark:update_opts(self.ext_opts.active) self:event(events.enter) end @@ -34,7 +36,7 @@ function DynamicNode:input_leave() self:update_dependents() self.active = false - self.mark:update_opts(self.parent.ext_opts[self.type].passive) + self.mark:update_opts(self.ext_opts.passive) end local function snip_init(self, snip) @@ -159,23 +161,19 @@ function DynamicNode:update() tmp.next = self tmp.prev = self - tmp.ext_opts = tmp.ext_opts - or ext_util.set_abs_prio( - vim.deepcopy(self.parent.ext_opts), - conf.config.ext_prio_increase - ) tmp.snippet = self.parent.snippet - tmp.mark = self.mark:copy_pos_gravs( - vim.deepcopy(self.parent.ext_opts[types.snippetNode].passive) - ) + + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() + tmp:subsnip_init() + + tmp.mark = self.mark:copy_pos_gravs(vim.deepcopy(tmp.ext_opts.passive)) tmp.dynamicNode = self tmp.update_dependents = function(node) node:_update_dependents() node.dynamicNode:update_dependents() end - tmp:subsnip_init() - tmp:init_positions(self.snip_absolute_position) tmp:init_insert_positions(self.snip_absolute_insert_position) @@ -275,6 +273,8 @@ function DynamicNode:update_static() node.dynamicNode:update_dependents_static() end + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() tmp:subsnip_init() tmp:init_positions(self.snip_absolute_position) @@ -317,7 +317,7 @@ function DynamicNode:exit() end function DynamicNode:set_ext_opts(name) - self.mark:update_opts(self.parent.ext_opts[self.type][name]) + self.mark:update_opts(self.ext_opts[name]) -- might not have been generated (missing nodes). if self.snip then self.snip:set_ext_opts(name) @@ -336,9 +336,7 @@ function DynamicNode:update_restore() -- prevent entering the uninitialized snip in enter_node in a few lines. local tmp = self.stored_snip - tmp.mark = self.mark:copy_pos_gravs( - vim.deepcopy(self.parent.ext_opts[types.snippetNode].passive) - ) + tmp.mark = self.mark:copy_pos_gravs(vim.deepcopy(tmp.ext_opts.passive)) self.parent:enter_node(self.indx) tmp:put_initial(self.mark:pos_begin_raw()) tmp:update_restore() diff --git a/lua/luasnip/nodes/functionNode.lua b/lua/luasnip/nodes/functionNode.lua index 5ace1fd7a..43024ea5e 100644 --- a/lua/luasnip/nodes/functionNode.lua +++ b/lua/luasnip/nodes/functionNode.lua @@ -6,14 +6,16 @@ local types = require("luasnip.util.types") local events = require("luasnip.util.events") local tNode = require("luasnip.nodes.textNode").textNode -local function F(fn, args, ...) +local function F(fn, args, opts) + opts = opts or {} + return FunctionNode:new({ fn = fn, args = node_util.wrap_args(args), type = types.functionNode, mark = nil, - user_args = { ... }, - }) + user_args = opts.user_args or {}, + }, opts) end FunctionNode.input_enter = tNode.input_enter diff --git a/lua/luasnip/nodes/insertNode.lua b/lua/luasnip/nodes/insertNode.lua index e144251d7..8002288d4 100644 --- a/lua/luasnip/nodes/insertNode.lua +++ b/lua/luasnip/nodes/insertNode.lua @@ -5,7 +5,7 @@ local config = require("luasnip.config") local types = require("luasnip.util.types") local events = require("luasnip.util.events") -local function I(pos, static_text) +local function I(pos, static_text, opts) static_text = util.wrap_value(static_text) if pos == 0 then return ExitNode:new({ @@ -16,7 +16,7 @@ local function I(pos, static_text) type = types.exitNode, -- will only be needed for 0-node, -1-node isn't set with this. ext_gravities_active = { false, false }, - }) + }, opts) else return InsertNode:new({ pos = pos, @@ -25,7 +25,7 @@ local function I(pos, static_text) dependents = {}, type = types.insertNode, inner_active = false, - }) + }, opts) end end @@ -90,7 +90,7 @@ function ExitNode:update_dependents_static() end function ExitNode:update_all_dependents_static() end function InsertNode:input_enter(no_move) - self.mark:update_opts(self.parent.ext_opts[self.type].active) + self.mark:update_opts(self.ext_opts.active) if not no_move then self.parent:enter_node(self.indx) @@ -186,7 +186,7 @@ function InsertNode:input_leave() self:event(events.leave) self:update_dependents() - self.mark:update_opts(self.parent.ext_opts[self.type].passive) + self.mark:update_opts(self.ext_opts.passive) end function InsertNode:exit() diff --git a/lua/luasnip/nodes/node.lua b/lua/luasnip/nodes/node.lua index 2a856490a..128483fde 100644 --- a/lua/luasnip/nodes/node.lua +++ b/lua/luasnip/nodes/node.lua @@ -1,14 +1,16 @@ local session = require("luasnip.session") local util = require("luasnip.util.util") +local types = require("luasnip.util.types") local node_util = require("luasnip.nodes.util") +local ext_util = require("luasnip.util.ext_opts") local events = require("luasnip.util.events") +local conf = require("luasnip.config") local Node = {} -function Node:new(o) +function Node:new(o, opts) o = o or {} - setmetatable(o, self) - self.__index = self + -- visible is true if the node is visible on-screen, during normal -- expansion, static_visible is needed for eg. get_static_text, where -- argnodes in inactive choices will happily provide their static text, @@ -16,6 +18,13 @@ function Node:new(o) o.visible = false o.static_visible = false o.old_text = {} + -- override existing keys, might be necessary due to double-init from + -- snippetProxy, but shouldn't hurt. + o = vim.tbl_extend("force", o, node_util.init_node_opts(opts or {})) + + setmetatable(o, self) + self.__index = self + return o end @@ -57,7 +66,7 @@ function Node:put_initial(pos) end function Node:input_enter(_) - self.mark:update_opts(self.parent.ext_opts[self.type].active) + self.mark:update_opts(self.ext_opts.active) self:event(events.enter) end @@ -137,7 +146,7 @@ end function Node:input_leave() self:event(events.leave) - self.mark:update_opts(self.parent.ext_opts[self.type].passive) + self.mark:update_opts(self.ext_opts.passive) end local function find_dependents(position_self, dict) @@ -257,7 +266,7 @@ function Node:get_static_args() end function Node:set_ext_opts(name) - self.mark:update_opts(self.parent.ext_opts[self.type][name]) + self.mark:update_opts(self.ext_opts[name]) end -- for insert,functionNode. @@ -307,6 +316,38 @@ function Node:static_init() self.static_visible = true end +-- resolve_*node*_ext_opts because snippet(Node)s have child_ext_opts, which +-- also have to be resolved. +-- This function generates a nodes ext_opts (those actually used in highlighting). +function Node:resolve_node_ext_opts(base_prio, parent_ext_opts) + -- if self.parent then + -- local ok, res = pcall(function() + -- local e = self.parent.effective_child_ext_opts[self.type] + -- end) + -- if not ok then + -- print(self.parent.type == types.snippetNode) + -- Insp(self.parent.child_ext_opts) + -- print(self.parent.snippet) + -- print(self.parent.absolute_position) + -- end + -- end + + if self.merge_node_ext_opts then + self.ext_opts = ext_util.extend( + vim.deepcopy(self.node_ext_opts), + parent_ext_opts or self.parent.effective_child_ext_opts[self.type] + ) + else + self.ext_opts = self.node_ext_opts + end + + ext_util.set_abs_prio( + self.ext_opts, + (base_prio or self.parent.ext_opts.base_prio) + + conf.config.ext_prio_increase + ) +end + return { Node = Node, } diff --git a/lua/luasnip/nodes/restoreNode.lua b/lua/luasnip/nodes/restoreNode.lua index b46dc0f53..863eba2ca 100644 --- a/lua/luasnip/nodes/restoreNode.lua +++ b/lua/luasnip/nodes/restoreNode.lua @@ -11,7 +11,7 @@ local ext_util = require("luasnip.util.ext_opts") local conf = require("luasnip.config") local mark = require("luasnip.util.mark").mark -local function R(pos, key, nodes) +local function R(pos, key, nodes, opts) -- don't create nested snippetNodes, unnecessary. nodes = nodes and wrap_nodes_in_snippetNode(nodes) @@ -24,7 +24,7 @@ local function R(pos, key, nodes) dependents = {}, -- TODO: find out why it's necessary only for this node. active = false, - }) + }, opts) end function RestoreNode:exit() @@ -41,7 +41,7 @@ end function RestoreNode:input_enter() self.active = true - self.mark:update_opts(self.parent.ext_opts[self.type].active) + self.mark:update_opts(self.ext_opts.active) self:event(events.enter) end @@ -51,7 +51,7 @@ function RestoreNode:input_leave() self:update_dependents() self.active = false - self.mark:update_opts(self.parent.ext_opts[self.type].passive) + self.mark:update_opts(self.ext_opts.passive) end -- set snippetNode for this key here. @@ -81,11 +81,6 @@ function RestoreNode:put_initial(pos) tmp.next = self tmp.prev = self - tmp.ext_opts = tmp.ext_opts - or ext_util.set_abs_prio( - vim.deepcopy(self.parent.ext_opts), - conf.config.ext_prio_increase - ) tmp.snippet = self.parent.snippet tmp.restore_node = self @@ -95,6 +90,8 @@ function RestoreNode:put_initial(pos) node.restore_node:update_dependents() end + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() tmp:subsnip_init() tmp:init_positions(self.snip_absolute_position) @@ -114,7 +111,7 @@ function RestoreNode:put_initial(pos) local mark_opts = vim.tbl_extend("keep", { right_gravity = false, end_right_gravity = false, - }, self.parent.ext_opts[types.snippetNode].passive) + }, tmp.ext_opts.passive) local old_pos = vim.deepcopy(pos) tmp:put_initial(pos) @@ -142,7 +139,7 @@ function RestoreNode:jump_into(dir, no_move) end function RestoreNode:set_ext_opts(name) - self.mark:update_opts(self.parent.ext_opts[self.type][name]) + self.mark:update_opts(self.ext_opts[name]) self.snip:set_ext_opts(name) end @@ -164,6 +161,8 @@ local function snip_init(self, snip) snip.snippet = self.parent.snippet snip.pos = self.pos + snip:resolve_child_ext_opts() + snip:resolve_node_ext_opts() snip:subsnip_init() snip:init_positions(self.snip_absolute_position) diff --git a/lua/luasnip/nodes/snippet.lua b/lua/luasnip/nodes/snippet.lua index 521ccb7ae..7d1b418fe 100644 --- a/lua/luasnip/nodes/snippet.lua +++ b/lua/luasnip/nodes/snippet.lua @@ -121,31 +121,42 @@ local function wrap_nodes_in_snippetNode(nodes) end end -local function init_snippet_opts(opts) +local function init_snippetNode_opts(opts) + local in_node = {} + opts = opts or {} - opts.callbacks = opts.callbacks or {} + in_node.child_ext_opts = ext_util.child_complete(opts.child_ext_opts or {}) + + if opts.merge_child_ext_opts == nil then + in_node.merge_child_ext_opts = true + end + + in_node.callbacks = opts.callbacks or {} -- return empty table for non-specified callbacks. - setmetatable(opts.callbacks, callbacks_mt) + setmetatable(in_node.callbacks, callbacks_mt) - opts.condition = opts.condition or true_func + return in_node +end - opts.show_condition = opts.show_condition or true_func +local function init_snippet_opts(opts) + local in_node = {} + + opts = opts or {} + + in_node.condition = opts.condition or true_func + + in_node.show_condition = opts.show_condition or true_func -- return sn(t("")) for so-far-undefined keys. - opts.stored = setmetatable(opts.stored or {}, stored_mt) + in_node.stored = setmetatable(opts.stored or {}, stored_mt) -- wrap non-snippetNode in snippetNode. - for key, nodes in pairs(opts.stored) do - opts.stored[key] = wrap_nodes_in_snippetNode(nodes) + for key, nodes in pairs(in_node.stored) do + in_node.stored[key] = wrap_nodes_in_snippetNode(nodes) end - if opts.ext_opts then - ext_util.complete(opts.ext_opts) - ext_util.merge(opts.ext_opts, conf.config.ext_opts) - end - - return opts + return vim.tbl_extend("error", in_node, init_snippetNode_opts(opts)) end local function init_snippet_context(context) @@ -193,20 +204,23 @@ end -- Create snippet without initializing opts+context. -- this might be called from snippetProxy. -local function _S(snip, nodes) +local function _S(snip, nodes, opts) nodes = util.wrap_nodes(nodes) -- tbl_extend creates a new table! Important with Proxy, metatable of snip -- will be changed later. - snip = Snippet:new(vim.tbl_extend("error", snip, { - nodes = nodes, - insert_nodes = {}, - current_insert = 0, - mark = nil, - dependents = {}, - active = false, - type = types.snippet, - dependents_dict = dict.new(), - })) + snip = Snippet:new( + vim.tbl_extend("error", snip, { + nodes = nodes, + insert_nodes = {}, + current_insert = 0, + mark = nil, + dependents = {}, + active = false, + type = types.snippet, + dependents_dict = dict.new(), + }), + opts + ) -- is propagated to all subsnippets, used to quickly find the outer snippet snip.snippet = snip @@ -234,23 +248,23 @@ local function S(context, nodes, opts) local snip = init_snippet_context(context) snip = vim.tbl_extend("error", snip, init_snippet_opts(opts)) - return _S(snip, nodes) + return _S(snip, nodes, opts) end function SN(pos, nodes, opts) - opts = init_snippet_opts(opts) - - local snip = Snippet:new({ - pos = pos, - nodes = util.wrap_nodes(nodes), - insert_nodes = {}, - current_insert = 0, - callbacks = opts.callbacks, - mark = nil, - dependents = {}, - active = false, - type = types.snippetNode, - }) + local snip = Snippet:new( + vim.tbl_extend("error", { + pos = pos, + nodes = util.wrap_nodes(nodes), + insert_nodes = {}, + current_insert = 0, + mark = nil, + dependents = {}, + active = false, + type = types.snippetNode, + }, init_snippetNode_opts(opts)), + opts + ) snip:init_nodes() return snip @@ -360,24 +374,34 @@ function Snippet:trigger_expand(current_node, pos) end self:indent(util.line_chars_before(pos):match("^%s*")) - -- keep (possibly) user-set opts. - if not self.ext_opts then - self.ext_opts = vim.deepcopy(conf.config.ext_opts) - end - + local parent_ext_opts + local parent_ext_base_prio -- if inside another snippet, increase priority accordingly. -- for now do a check for .indx. - -- TODO: maybe allow expand only inside insertNodes. if current_node and (current_node.indx and current_node.indx > 1) then - ext_util.set_abs_prio( - self.ext_opts, - current_node.parent.ext_opts.increased_by - + conf.config.ext_prio_increase + parent_ext_base_prio = current_node.parent.ext_opts.base_prio + parent_ext_opts = current_node.parent.effective_child_ext_opts + else + parent_ext_base_prio = conf.config.ext_base_prio + parent_ext_opts = conf.config.ext_opts + end + + -- (possibly) keep user-set opts. + if self.merge_child_ext_opts then + self.effective_child_ext_opts = ext_util.child_extend( + vim.deepcopy(self.child_ext_opts), + parent_ext_opts ) else - ext_util.set_abs_prio(self.ext_opts, conf.config.ext_base_prio) + self.effective_child_ext_opts = vim.deepcopy(self.child_ext_opts) end + -- own highlight can come from self.child_ext_opts.snippet. + self:resolve_node_ext_opts( + parent_ext_base_prio, + self.effective_child_ext_opts[self.type] + ) + self.env = Environ:new(pos) self:subsnip_init() @@ -404,7 +428,7 @@ function Snippet:trigger_expand(current_node, pos) local mark_opts = vim.tbl_extend("keep", { right_gravity = false, end_right_gravity = true, - }, self.ext_opts[types.snippet].passive) + }, self.ext_opts.passive) self.mark = mark(old_pos, pos, mark_opts) self:update() @@ -598,7 +622,7 @@ function Snippet:put_initial(pos) local mark_opts = vim.tbl_extend("keep", { right_gravity = false, end_right_gravity = false, - }, self.ext_opts[node.type].passive) + }, node.ext_opts.passive) node.mark = mark(old_pos, pos, mark_opts) end self.visible = true @@ -641,6 +665,11 @@ function Snippet:fake_expand(opts) self.ext_opts = vim.deepcopy(conf.config.ext_opts) self:indent("") + + -- ext_opts don't matter here, just use convenient values. + self.effective_child_ext_opts = self.child_ext_opts + self.ext_opts = self.node_ext_opts + self:subsnip_init() self:init_positions({}) @@ -783,9 +812,10 @@ function Snippet:input_enter() self.active = true if self.type == types.snippet then + -- set snippet-passive -> passive for all children. self:set_ext_opts("passive") end - self.mark:update_opts(self.ext_opts[self.type].active) + self.mark:update_opts(self.ext_opts.active) self:event(events.enter) end @@ -794,8 +824,10 @@ function Snippet:input_leave() self:event(events.leave) self:update_dependents() - self.mark:update_opts(self.ext_opts[self.type].passive) + -- set own ext_opts to snippet-passive, there is no passive for snippets. + self.mark:update_opts(self.ext_opts.snippet_passive) if self.type == types.snippet then + -- also override all nodes' ext_opt. self:set_ext_opts("snippet_passive") end @@ -1072,6 +1104,18 @@ function Snippet:static_init() end end +-- called only for snippetNodes! +function Snippet:resolve_child_ext_opts() + if self.merge_child_ext_opts then + self.effective_child_ext_opts = ext_util.child_extend( + vim.deepcopy(self.child_ext_opts), + self.parent.effective_child_ext_opts + ) + else + self.effective_child_ext_opts = vim.deepcopy(self.child_ext_opts) + end +end + return { Snippet = Snippet, S = S, diff --git a/lua/luasnip/nodes/snippetProxy.lua b/lua/luasnip/nodes/snippetProxy.lua index 691faf539..47c8f8c2c 100644 --- a/lua/luasnip/nodes/snippetProxy.lua +++ b/lua/luasnip/nodes/snippetProxy.lua @@ -8,6 +8,7 @@ local parse = require("luasnip.util.parser").parse_snippet local snip_mod = require("luasnip.nodes.snippet") +local node_util = require("luasnip.nodes.util") local SnippetProxy = {} @@ -43,10 +44,15 @@ end -- context and opts are the same objects as in s(contex, nodes, opts), snippet is a string representing the snippet. local function new(context, snippet, opts) - local sp = {} -- "error": there should not be duplicate keys, don't silently overwrite/keep. - sp = vim.tbl_extend("error", sp, snip_mod.init_snippet_context(context)) - sp = vim.tbl_extend("error", sp, snip_mod.init_snippet_opts(opts)) + local sp = vim.tbl_extend( + "error", + {}, + snip_mod.init_snippet_context(context), + snip_mod.init_snippet_opts(opts), + node_util.init_node_opts(opts) + ) + sp._snippet_string = snippet -- override docstring sp.docstring = snippet diff --git a/lua/luasnip/nodes/textNode.lua b/lua/luasnip/nodes/textNode.lua index 8fe21769e..17652a245 100644 --- a/lua/luasnip/nodes/textNode.lua +++ b/lua/luasnip/nodes/textNode.lua @@ -5,12 +5,12 @@ local events = require("luasnip.util.events") local TextNode = node_mod.Node:new() -local function T(static_text) +local function T(static_text, opts) return TextNode:new({ static_text = util.wrap_value(static_text) or { "" }, mark = nil, type = types.textNode, - }) + }, opts) end function TextNode:input_enter(no_move) diff --git a/lua/luasnip/nodes/util.lua b/lua/luasnip/nodes/util.lua index d8a06fd78..0d746e634 100644 --- a/lua/luasnip/nodes/util.lua +++ b/lua/luasnip/nodes/util.lua @@ -6,13 +6,10 @@ local conf = require("luasnip.config") local function subsnip_init_children(parent, children) for _, child in ipairs(children) do if child.type == types.snippetNode then - child.ext_opts = ext_util.set_abs_prio( - vim.deepcopy(parent.ext_opts), - conf.config.ext_prio_increase - ) child.snippet = parent.snippet + child:resolve_child_ext_opts() end - + child:resolve_node_ext_opts() child:subsnip_init() end end @@ -109,6 +106,23 @@ local function print_dict(dict) })) end +local function init_node_opts(opts) + local in_node = {} + if not opts then + opts = {} + end + + in_node.node_ext_opts = ext_util.complete(opts.node_ext_opts or {}) + + if opts.merge_node_ext_opts == nil then + in_node.merge_node_ext_opts = true + else + in_node.merge_node_ext_opts = opts.merge_node_ext_opts + end + + return in_node +end + return { subsnip_init_children = subsnip_init_children, init_child_positions_func = init_child_positions_func, @@ -119,4 +133,5 @@ return { enter_nodes_between = enter_nodes_between, select_node = select_node, print_dict = print_dict, + init_node_opts = init_node_opts, } diff --git a/lua/luasnip/util/ext_opts.lua b/lua/luasnip/util/ext_opts.lua index 81f82d52b..91ce4d632 100644 --- a/lua/luasnip/util/ext_opts.lua +++ b/lua/luasnip/util/ext_opts.lua @@ -1,5 +1,9 @@ -- eventually turn ext_opts into proper objects, mainly for -- default-construction eg. assured `complete`. +-- +-- child_*-functions perform the same operation as theiry non-child +-- counterparts, but on a collection (eg. +-- `{[types.insertNode={...}, [types.textNode]= {...}]}`) of ext_opts. local types = require("luasnip.util.types") @@ -7,6 +11,8 @@ local types = require("luasnip.util.types") -- always pass this empty table, which will (has to!) stay empty. local shared_empty_table = {} +-- opts: child_ext_opts, have to have hl_group set for all combinations of +-- node-type and active,passive,snippet_passive. local function clear_invalid(opts) for _, node_type in pairs(types.node_types) do local act_group, pas_group, snip_pas_group = @@ -28,73 +34,94 @@ local function clear_invalid(opts) end end +local function _complete_ext_opts(ext_opts) + if not ext_opts then + ext_opts = {} + end + ext_opts.snippet_passive = ext_opts.snippet_passive or {} + ext_opts.passive = vim.tbl_extend( + "keep", + ext_opts.passive or shared_empty_table, + ext_opts.snippet_passive or shared_empty_table + ) + ext_opts.active = vim.tbl_extend( + "keep", + ext_opts.active or shared_empty_table, + ext_opts.passive or shared_empty_table + ) + + --stylua: ignore start + if ext_opts.snippet_passive.hl_group and not + ext_opts.snippet_passive.priority then + ext_opts.snippet_passive.priority = 0 + end + + if ext_opts.passive.hl_group and not + ext_opts.passive.priority then + ext_opts.passive.priority = 0 + end + + if ext_opts.active.hl_group and not + ext_opts.active.priority then + ext_opts.active.priority = 0 + end + --stylua: ignore end + + return ext_opts +end + -- active inherits unset values from passive, which in turn inherits from -- snippet_passive. -- Also make sure that all keys have a table, and are not nil! -local function complete(ext_opts) - ext_opts.increased_by = 0 +local function child_complete(ext_opts) for _, node_type in pairs(types.node_types) do - local node_opts - if not ext_opts[node_type] then - node_opts = {} - ext_opts[node_type] = node_opts - else - node_opts = ext_opts[node_type] - end - node_opts.snippet_passive = node_opts.snippet_passive or {} - node_opts.passive = vim.tbl_extend( - "keep", - node_opts.passive or shared_empty_table, - node_opts.snippet_passive or shared_empty_table - ) - node_opts.active = vim.tbl_extend( - "keep", - node_opts.active or shared_empty_table, - node_opts.passive or shared_empty_table - ) - - --stylua: ignore start - if node_opts.snippet_passive.hl_group and not - node_opts.snippet_passive.priority then - node_opts.snippet_passive.priority = 0 - end - - if node_opts.passive.hl_group and not - node_opts.passive.priority then - node_opts.passive.priority = 0 - end - - if node_opts.active.hl_group and not - node_opts.active.priority then - node_opts.active.priority = 0 - end - --stylua: ignore end + ext_opts[node_type] = _complete_ext_opts(ext_opts[node_type]) end + ext_opts.base_prio = 0 + + return ext_opts +end + +local function complete(ext_opts) + _complete_ext_opts(ext_opts) + ext_opts.base_prio = 0 + + return ext_opts end -- in-place adds opts of b to a, doesn't override. -- a/b: completed ext_opts, not nil. local function extend(opts_a, opts_b) + opts_a.snippet_passive = vim.tbl_extend( + "keep", + opts_a.snippet_passive, + opts_b.snippet_passive + ) + opts_a.passive = vim.tbl_extend("keep", opts_a.passive, opts_b.passive) + opts_a.active = vim.tbl_extend("keep", opts_a.active, opts_b.active) + + return opts_a +end + +-- in-place adds opts of b to a, doesn't override. +-- a/b: completed child_ext_opts, not nil. +local function child_extend(opts_a, opts_b) for _, node_type in ipairs(types.node_types) do - local node_opts_a = opts_a[node_type] - local node_opts_b = opts_b[node_type] - - node_opts_a.snippet_passive = vim.tbl_extend( - "keep", - node_opts_a.snippet_passive, - node_opts_b.snippet_passive - ) - node_opts_a.passive = vim.tbl_extend( - "keep", - node_opts_a.passive, - node_opts_b.passive - ) - node_opts_a.active = vim.tbl_extend( - "keep", - node_opts_a.active, - node_opts_b.active - ) + extend(opts_a[node_type], opts_b[node_type]) end + + return opts_a +end + +local function increase_prio(opts, inc) + -- increase only if there is a priority. + opts.active.priority = opts.active.priority and (opts.active.priority + inc) + + opts.passive.priority = opts.passive.priority + and (opts.passive.priority + inc) + + opts.snippet_passive.priority = opts.snippet_passive.priority + and (opts.snippet_passive.priority + inc) end -- ext_opts-priorities are defined relative to some base-priority. @@ -105,26 +132,30 @@ end local function set_abs_prio(opts, new_base_prio) -- undo previous increase. -- base_prio is initialized with 0. - new_base_prio = new_base_prio - opts.base_prio + local prio_offset = new_base_prio - opts.base_prio opts.base_prio = new_base_prio - for _, node_type in pairs(types.node_types) do - local node_opts = opts[node_type] - node_opts.active.priority = node_opts.active.priority - and node_opts.active.priority + new_base_prio + increase_prio(opts, prio_offset) - node_opts.passive.priority = node_opts.passive.priority - and node_opts.passive.priority + new_base_prio + return opts +end - node_opts.snippet_passive.priority = node_opts.snippet_passive.priority - and node_opts.snippet_passive.priority + new_base_prio +local function child_set_abs_prio(opts, new_base_prio) + -- undo previous increase. + -- base_prio is initialized with 0. + local prio_offset = new_base_prio - opts.base_prio + opts.base_prio = new_base_prio + for _, node_type in ipairs(types.node_types) do + increase_prio(opts[node_type], prio_offset) end - -- modifies in-place, but utilizing that may be cumbersome. return opts end return { clear_invalid = clear_invalid, complete = complete, + child_complete = child_complete, extend = extend, + child_extend = child_extend, set_abs_prio = set_abs_prio, + child_set_abs_prio = child_set_abs_prio, }