Skip to content

Instantly share code, notes, and snippets.

@davidatsurge
Last active January 15, 2023 13:22
Show Gist options
  • Save davidatsurge/9873d9cb1781f1a37c0f25d24cb1b3ab to your computer and use it in GitHub Desktop.
Save davidatsurge/9873d9cb1781f1a37c0f25d24cb1b3ab to your computer and use it in GitHub Desktop.
luasnip+treesitter snippet that fills in prop names when creating new react component
local ls = require("luasnip")
local fmt = require("luasnip.extras.fmt").fmt
local s = ls.snippet
local i = ls.insert_node
local f = ls.function_node
local d = ls.dynamic_node
local sn = ls.snippet_node
local rep = require("luasnip.extras").rep
-- Get a list of the property names given an `interface_declaration`
-- treesitter *tsx* node.
-- Ie, if the treesitter node represents:
-- interface {
-- prop1: string;
-- prop2: number;
-- }
-- Then this function would return `{"prop1", "prop2"}
---@param id_node {} Stands for "interface declaration node"
---@return string[]
local function get_prop_names(id_node)
local object_type_node = id_node:child(2)
if object_type_node:type() ~= "object_type" then
return {}
end
local prop_names = {}
for prop_signature in object_type_node:iter_children() do
if prop_signature:type() == "property_signature" then
local prop_iden = prop_signature:child(0)
local prop_name = vim.treesitter.query.get_node_text(prop_iden, 0)
prop_names[#prop_names + 1] = prop_name
end
end
return prop_names
end
ls.add_snippets("typescriptreact", {
s(
"c",
fmt(
[[
{}interface {}Props {{
{}
}}
{}function {}({{{}}}: {}Props) {{
{}
}}
]],
{
i(1, "export "),
-- Initialize component name to file name
d(2, function(_, snip)
return sn(nil, {
i(1, vim.fn.substitute(snip.env.TM_FILENAME, "\\..*$", "", "g")),
})
end, { 1 }),
i(3, "// props"),
rep(1),
rep(2),
f(function(_, snip, _)
local pos_begin = snip.nodes[6].mark:pos_begin()
local pos_end = snip.nodes[6].mark:pos_end()
local parser = vim.treesitter.get_parser(0, "tsx")
local tstree = parser:parse()
local node = tstree[1]
:root()
:named_descendant_for_range(pos_begin[1], pos_begin[2], pos_end[1], pos_end[2])
while node ~= nil and node:type() ~= "interface_declaration" do
node = node:parent()
end
if node == nil then
return ""
end
-- `node` is now surely of type "interface_declaration"
local prop_names = get_prop_names(node)
-- Does this lua->vimscript->lua thing cause a slow down? Dunno.
return vim.fn.join(prop_names, ", ")
end, { 3 }),
rep(2),
i(5, "return <div></div>"),
}
)
),
})
@davidatsurge
Copy link
Author

@Decodetalkers
Copy link

Undefined golbal P

@Decodetalkers
Copy link

What does P means?

@davidatsurge
Copy link
Author

@Decodetalkers , sorry! I left a debugging line in there, which I have now removed. I had defined P(x) to be print(vim.inspect(x)).

@Decodetalkers
Copy link

Decodetalkers commented May 21, 2022

It seems ts_utils.get_node_text is deprecation...

However , when it change to
vim.treesitter.query.get_node_text
it will not work anymore.
Can you help?

@davidatsurge
Copy link
Author

Thanks for pointing that out @Decodetalkers ! I've updated the gist to use vim.treesitter.query.get_node_text now.

@Decodetalkers
Copy link

Decodetalkers commented May 21, 2022

Thanks for pointing that out @Decodetalkers ! I've updated the gist to use vim.treesitter.query.get_node_text now.

And you also have not state ls to "luasnip" and some other variables ,like fmt lol

@davidatsurge
Copy link
Author

Thank you @Decodetalkers ! What would I do without you 😂? I've added those imports.

@Decodetalkers
Copy link

Thank you @Decodetalkers ! What would I do without you joy? I've added those imports.

lol , and you have miss the last one

local sn = ls.snippet_node

@davidatsurge
Copy link
Author

davidatsurge commented May 21, 2022

This is genuinely embarrassing 😂! Thanks @Decodetalkers, it's fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment