large number of choices, better choose dynamic node over static #1365
-
I have developed a plugin to manage code snippets, many of which include multiple choice options—often a large number. I opted for a dynamic approach over a static one, primarily because I thought using dynamic nodes avoids keeping large tables in memory, which seemed more efficient given the size of the data. Despite choosing the dynamic approach for its memory efficiency, I’ve noticed during benchmarking that Neovim’s memory usage still increases over time and remains nearly comparable to the static version. However, I observed that using the static approach frequently leads to stack overflow errors, which I find difficult to understand. Static-- versions.lua
local ls = require("luasnip")
local t = ls.text_node
local i = ls.insert_node
return {
t("master"),
t("master-local"),
-- 1000 lines more
} -- resources.lua
local ls = require("luasnip")
local t = ls.text_node
local i = ls.insert_node
return {
t("accountaccount"),
t("accountaccountslist"),
--- 500 lines more
} -- snippets.lua
local versions = require("versions")
local resources = require("resources")
s(
"schema-kube",
fmt("# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{}/{}.json", {
c(1, versions),
c(2, resources),
})
) Dynamic-- versions.lua
local ls = require("luasnip")
local t = ls.text_node
local i = ls.insert_node
return function
return {
t("master"),
t("master-local"),
-- 1000 lines more
}
end -- resources.lua
local ls = require("luasnip")
local t = ls.text_node
local i = ls.insert_node
return function
return {
t("accountaccount"),
t("accountaccountslist"),
--- 500 lines more
}
end -- snippets.lua
s(
"schema-kube",
fmt("# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{}/{}.json", {
d(1, function()
local versions = require("versions")()
return sn(nil, { c(1, versions) })
end),
d(2, function()
local resources = require("resources")()
return sn(nil, { c(1, resources) })
end),
})
) What do you think of two of those approaches? And do you have any suggestions? Here is my repo I'm working on and facing that problem https://github.com/KevinNitroG/kubernetes-schema-snippets.nvim/ |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Hi! I think of these two, the dynamic approach is a tiny bit better because we essentially deepcopy the snippet on expand, and the smaller it is, the better (dynamic version does not contain the large table). I can't reproduce the overflow, but it seems like every expansion adds about 10MiB on my machine. I can think of one optimisation: don't use a -- these contain only the strings, like `return {"master", "master-local"}`
local versions_raw = loadfile("versions_raw.lua")()
local resources_raw = loadfile("resources_raw.lua")()
s("schema-kube2", fmt(
"# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{}/{}.json", {
d(1, function(_, parent)
-- initialise the index into the version-table.
if not parent.v_idx then
parent.v_idx = 1
end
-- create the "active choice".
return sn(nil, { i(1), t(versions_raw[parent.v_idx]) })
end, {}, {user_args = {
-- these functions increase/decrease the index, and wrap around to
-- make sure the index stays valid.
function(parent)
local new_idx = parent.v_idx + 1
if new_idx > #versions_raw then
new_idx = 1
end
parent.v_idx = new_idx
end,
function(parent)
local new_idx = parent.v_idx - 1
if new_idx == 0 then
new_idx = #versions_raw
end
parent.v_idx = new_idx
end
}}),
d(2, function(_, parent)
if not parent.r_idx then
parent.r_idx = 1
end
return sn(nil, { i(1), t(resources_raw[parent.r_idx]) })
end, {}, {user_args = {
function(parent)
local new_idx = parent.r_idx + 1
if new_idx > #resources_raw then
new_idx = 1
end
parent.r_idx = new_idx
end,
function(parent)
local new_idx = parent.r_idx - 1
if new_idx == 0 then
new_idx = #resources_raw
end
parent.r_idx = new_idx
end
}})
}
)) The general idea is that you register a keybinding which calls some user-defined function and then updates the snippet. In this case, this can be used to create a kind of virtual choiceNode, ie. only the active choice is ever in memory, and all snippets look into the same tables, which should fix these memory-related issues. Check the wiki-article I linked, you'd have to add |
Beta Was this translation helpful? Give feedback.
Hi!
I think of these two, the dynamic approach is a tiny bit better because we essentially deepcopy the snippet on expand, and the smaller it is, the better (dynamic version does not contain the large table).
I can't reproduce the overflow, but it seems like every expansion adds about 10MiB on my machine.
I can think of one optimisation: don't use a
choiceNode
, but anexternal-update-dynamicNode
(which isn't really a node, but a trick to update a dynamicNode by pressing some keys/means other than adding text to the buffer):