LuaSnip
LuaSnip copied to clipboard
Inconsistent indentation in multiline strings inserted by functionNode
I am trying to create a snippet that will automatically generate the python class attribute assignment statements based on the init function arguments. i.e.
class MyClass:
def __init__(self, arg1, arg2):
self.arg1 = arg1 # inserted by functionNode
self.arg2 = arg2 # inserted by functionNode
This is my current implementation
require("luasnip.session.snippet_collection").clear_snippets "python"
function collect_class_attrs(input)
local input = input[1][1]
if not string.find(input, ",") then
return "self." .. input .. " = " .. input
end
local attributes = {}
for value in string.gmatch(input, "([^,]+)") do
value = string.gsub(value, "^%s*(.-)%s*$", "%1") -- Trim whitespace
table.insert(attributes, 'self.' .. value .. ' = ' .. value)
end
return attributes
end
return {
s("myclass", fmt([[
class {}():
def __init__(self, {}):
{}
{}
]], {
i(1, "classname"),
i(2, "initargs"),
f(collect_class_attrs, 2),
i(0)
})),
}
The problem is that all the elements after of the first element of the table that is returned by the collect_class_attrs callback function are indented at the same level as the parent class itself. Like this.
class classname():
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
What I would like is this
class classname():
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
Looking at the docs, it says of the callback function:
fn shall return a string, which will be inserted as is, or a table of strings
for multiline strings, where all lines following the first will be prefixed
with the snippets’ indentation.
So I am assuming that every element of the returned table after the first one is being given the indentation level of the parent snippet which is the class itself?
Can anyone suggest some ways I could acheive the correct level of indentation in this example?
Found a solution based on some of the same solutions raised in issue #296
This is the implementation of my callback function
function collect_instance_attrs(argnode_text, parent)
local instance_attrs = argnode_text[1][1]
if not string.find(instance_attrs, ",") then
return "self." .. instance_attrs .. " = " .. instance_attrs
end
local attributes = {}
for value in string.gmatch(instance_attrs, "([^,]+)") do
value = string.gsub(value, "^%s*(.-)%s*$", "%1") -- Trim whitespace
table.insert(attributes, 'self.' .. value .. ' = ' .. value)
end
return attributes
end
And this is the implementation of my snippet.
s("myclass", fmt([[
class {}:
def __init__(self, {}):
{}
{}
]], {
i(1, "ClassName"),
i(2, "args", {key = "args-key"}),
isn(nil, { f(collect_instance_attrs, k("args-key") ) }, "$PARENT_INDENT\t\t"),
i(0),
}))
By wrapping the functionNode inside an indentSnippetNode, I get the indentation I want. This works at any level of indentation as its relative to the parent. I'm happy for this to be marked as closed 👍
Ah, always happy to see usecases for isn :)