LuaSnip
LuaSnip copied to clipboard
Expose generic conditions for use in snippets
Hello,
I'd like to make a snippet with an insert node that defaults to env.LS_SELECT_RAW when luasnip has a selection 'in store', or something else if not.
Grepping the docs shows there is a require"luasnip.extras.conditions.show".has_selected_text 'function' I could use to check if there is a selection available, but it feels weird to use something from a module mentioning show (as if it was specific to show conditions). I would prefer importing something from luasnip.extras.conditions (generic condition objects), or have a handy simple function (not a condition object) for use in snippets.
Could also be used in statusline segment to put an indicator when there is a luasnip text selection available
When implementing these condition 'functions'/objects, we started out with luasnip.extras.conditions. The issue is that currently we have two different places where it makes sense to use these conditions, show_condition and condition (relevant for if the snippet can be expanded), which do not share the same API. Therefore we split up the module in conditions.expand and conditions.show to reflect that difference (... .expand contains everything from ... .show as the API is a superset of the show_condition API).
So the naming most probably won't change for this.
Tbh I'm still not sure what exactly your issue is (is it just the naming/organization in modules?).
In effect the condition objects from luasnip.extras.conditions.{show,expand} are just simple tables {func = function(...) ... end} (with a special metatable attached). So if you really need to, you could try accessing the underlying function via require"luasnip.extras.conditions.show".has_selected_text.func.
But be aware that the contents of that table is kinda an internal representation and might change in the future (although currently I see no reason why it should).
I'd like to make a snippet with an insert node that defaults to env.LS_SELECT_RAW when luasnip has a selection 'in store', or something else if not.
Sorry, completely forgot to answer here, with all the other stuff going on :sweat_smile: You can use a dynamicNode and check if the select-variables contain text. For example:
d(1, function(_, parent)
if #parent.snippet.env.LS_SELECT_DEDENT ~= 0 then
return sn(nil, {
t(parent.snippet.env.LS_SELECT_DEDENT)
})
else
return sn(nil, { i(1) })
end
end
Tbh I'm still not sure what exactly your issue is
I think @bew just thought that the condition-function is the only way to check whether there is a selection
note: I actually noticed that using has_selected_text from the extra show conditions module always returns false when inside a snippet for some reason.
If I instead check that snip.env.LS_SELECT_RAW isn't empty before using it that works correctly.
I think having a nice function would be better though, so my snippet code don't need to depend on what the default / empty value of LS_SELECT_RAW is when there are no selection.
note: I actually noticed that using has_selected_text from the extra show conditions module always returns false when inside a snippet for some reason.
Oh, I'll check that out
I think having a nice function would be better though, so my snippet code don't need to depend on what the default / empty value of LS_SELECT_RAW is when there are no selection.
True, hmm..
I think we should add that, but I'm not sure where :D
We don't yet have a module with functions for use inside of dynamic/functionNode, and I'm not sure where to put it/what to call it.
extras/helpers, maybe
d(1, function(_, parent) if #parent.snippet.env.LS_SELECT_DEDENT ~= 0 then return sn(nil, { t(parent.snippet.env.LS_SELECT_DEDENT) }) else return sn(nil, { i(1) }) end end
This workaround has the flaw that the inserted LS_SELECT_DEDENT won't be selected at the end.
I am looking for something in the sense of "i(1, LS_SELECT_DEDENT)" but the above solution is basically
- if
LS_SELECT_DEDENTexists thent(LS_SELECT_DEDENT) - else
i(1)
which is something different.
I'm migrating from UltiSnips and basically half my snippets use ${1:$VISUAL}}, which is a problem for me.
fyi: With the rework of how selection/restore works from #1013 (last year), the way to check for this is now:
ACTUALLY THIS IS WRONG (see below)
function has_stored_selection()
local success, _ = pcall(vim.api.nvim_buf_get_var, 0, "LUASNIP_SELECT_RAW")
return success
end
Reference: https://github.com/L3MON4D3/LuaSnip/blob/33b06d72d220aa56a7ce80a0dd6f06c70cd82b9d/lua/luasnip/util/select.lua#L8
Actually I've been working with this recently as well. With this comment you made me remember/reread this issue again.
Currently I'm using this function
local function visual_selection(_, parent, _, uarg)
if #parent.snippet.env.LS_SELECT_RAW > 0 then
return sn(nil, {t(parent.snippet.env.LS_SELECT_RAW)})
else
return sn(nil, {i(unpack(uarg.i))})
end
end
for example as follows
stop = isn(2, {d(1, visual_selection, nil, {user_args={{i={1}}}})}, "$PARENT_INDENT\t"),
while it might be worth adding some common function for dynamic nodes as some sort of utils library, I'm not sure if we can generalize them enough (the function above for example also is just for a specific usecase even though one can adjust the insertNode arbitrarily) so that it makes sense to ship those functions to every user. Also if we manage to generalize this, it might be not really easier to use compared to writing own functions for dynamicNodes (after all writing a custom function is peak generalization)
Actually the code I posted above is wrong 👀😬. Since the luasnip impl will retrieve the selection from buffer vars and immediately after clear those variable, it's no longer possible to check for that buffer variable during the snippet execution (e.g. in a dynamic node).
I think the only reliable way right now is still to check:
if snip.env.LS_SELECT_RAW and #snip.env.LS_SELECT_RAW > 0 then
-- ...
end