LuaSnip icon indicating copy to clipboard operation
LuaSnip copied to clipboard

Inconsistent indentation in multiline strings inserted by functionNode

Open wjs20 opened this issue 1 year ago • 1 comments

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?

wjs20 avatar Mar 15 '24 15:03 wjs20

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 👍

wjs20 avatar Mar 16 '24 17:03 wjs20

Ah, always happy to see usecases for isn :)

L3MON4D3 avatar Apr 01 '24 19:04 L3MON4D3