# Custom Snippets Guide This guide explains how to create and use custom code snippets with LuaSnip. ## Overview Snippets are code templates that expand when you type a trigger and press ``. Your config supports: - **Lua snippets**: Full power of Lua for dynamic snippets - **VS Code snippets**: JSON format in `snippets/vscode/` ## Snippet Locations ``` ~/.config/nvim/snippets/ ├── all.lua # Global snippets (all filetypes) ├── typescript.lua # TypeScript/JavaScript ├── python.lua # Python ├── go.lua # Go └── vscode/ # VS Code format snippets (optional) ``` ## Quick Start ### 1. Open Snippet File ```lua -- For TypeScript snippets: nvim ~/.config/nvim/snippets/typescript.lua ``` ### 2. Add a Simple Snippet ```lua local ls = require("luasnip") local s = ls.snippet local t = ls.text_node local i = ls.insert_node return { s("cl", { t("console.log("), i(1), t(")") }), } ``` ### 3. Reload Snippets ``` cS Reload all snippets ``` ### 4. Use Snippet Type `cl` and press `` to expand. ## Snippet Anatomy ### Basic Structure ```lua s(trigger, nodes, opts) ``` - **trigger**: What you type to activate - **nodes**: The content (text, placeholders, etc.) - **opts**: Optional settings ### Node Types | Node | Function | Purpose | |------|----------|---------| | `t(text)` | Text node | Static text | | `i(index, default)` | Insert node | Tab stop/placeholder | | `c(index, choices)` | Choice node | Multiple options | | `f(func, args)` | Function node | Dynamic content | | `d(index, func, args)` | Dynamic node | Complex dynamic content | | `rep(index)` | Repeat node | Mirror another node | ## Examples by Complexity ### 1. Simple Text Replacement ```lua -- Expands "todo" to "// TODO: " s("todo", { t("// TODO: "), i(0) }) ``` ### 2. Multiple Tab Stops ```lua -- Arrow function with parameters and body s("af", { t("const "), i(1, "name"), t(" = ("), i(2), t(") => {"), t({"", " "}), i(0), t({"", "}"}), }) ``` Tab order: name → parameters → body ### 3. Using fmt() for Cleaner Syntax ```lua local fmt = require("luasnip.extras.fmt").fmt s("fn", fmt([[ function {}({}) {{ {} }} ]], { i(1, "name"), i(2), i(0) })) ``` Note: `{{` and `}}` escape braces in fmt. ### 4. Choice Node ```lua -- Choose between const, let, var s("var", { c(1, { t("const"), t("let"), t("var"), }), t(" "), i(2, "name"), t(" = "), i(0), }) ``` Press `` / `` to cycle choices. ### 5. Function Node (Dynamic) ```lua -- Automatically generate return type s("fn", { t("function "), i(1, "name"), t("("), i(2), t("): "), f(function(args) -- Generate return type based on function name local name = args[1][1] if name:match("^is") then return "boolean" end if name:match("^get") then return "string" end return "void" end, {1}), t({" {", " "}), i(0), t({"", "}"}), }) ``` ### 6. Repeat Node ```lua -- Class with constructor that uses the class name s("class", { t("class "), i(1, "Name"), t({" {", " constructor("}), i(2), t({") {", " "}), i(0), t({"", " }", "", "}", ""}), t("export default "), rep(1), -- Repeats the class name t(";"), }) ``` ### 7. Dynamic Node ```lua -- Generate multiple parameters dynamically s("params", { t("function("), d(1, function() -- Could read from clipboard, analyze code, etc. return sn(nil, { i(1, "param1"), t(", "), i(2, "param2") }) end), t(")"), }) ``` ## Language-Specific Examples ### TypeScript ```lua -- snippets/typescript.lua local ls = require("luasnip") local s = ls.snippet local t = ls.text_node local i = ls.insert_node local c = ls.choice_node local fmt = require("luasnip.extras.fmt").fmt return { -- Console log s("cl", { t("console.log("), i(1), t(")") }), -- Console log with label s("cll", fmt('console.log("{}: ", {})', { i(1, "label"), rep(1) })), -- Arrow function s("af", fmt("const {} = ({}) => {{\n {}\n}}", { i(1, "name"), i(2), i(0) })), -- React functional component s("rfc", fmt([[ export function {}({}: {}) {{ return (
{}
) }} ]], { i(1, "Component"), i(2, "props"), i(3, "Props"), i(0) })), -- useState hook s("us", fmt("const [{}, set{}] = useState({})", { i(1, "state"), f(function(args) local name = args[1][1] return name:sub(1,1):upper() .. name:sub(2) end, {1}), i(2, "initialValue"), })), -- useEffect hook s("ue", fmt([[ useEffect(() => {{ {} }}, [{}]) ]], { i(1), i(2) })), -- Try-catch s("tc", fmt([[ try {{ {} }} catch (error) {{ {} }} ]], { i(1), i(2, "console.error(error)") })), } ``` ### Python ```lua -- snippets/python.lua local ls = require("luasnip") local s = ls.snippet local t = ls.text_node local i = ls.insert_node local fmt = require("luasnip.extras.fmt").fmt return { -- Main block s("main", fmt([[ if __name__ == "__main__": {} ]], { i(0) })), -- Function with docstring s("def", fmt([[ def {}({}): """{}""" {} ]], { i(1, "name"), i(2), i(3, "Description"), i(0) })), -- Class s("class", fmt([[ class {}: """{}""" def __init__(self, {}): {} ]], { i(1, "Name"), i(2, "Description"), i(3), i(0) })), -- Async function s("adef", fmt([[ async def {}({}): {} ]], { i(1, "name"), i(2), i(0) })), -- Try-except s("try", fmt([[ try: {} except {} as e: {} ]], { i(1), i(2, "Exception"), i(3, "raise") })), -- Context manager s("with", fmt([[ with {}({}) as {}: {} ]], { i(1, "open"), i(2), i(3, "f"), i(0) })), } ``` ### Go ```lua -- snippets/go.lua local ls = require("luasnip") local s = ls.snippet local t = ls.text_node local i = ls.insert_node local c = ls.choice_node local fmt = require("luasnip.extras.fmt").fmt return { -- Error handling s("iferr", fmt([[ if err != nil {{ return {} }} ]], { c(1, { t("err"), i(nil, "nil, err") }) })), -- Function s("fn", fmt([[ func {}({}) {} {{ {} }} ]], { i(1, "name"), i(2), i(3, "error"), i(0) })), -- Method s("meth", fmt([[ func ({} *{}) {}({}) {} {{ {} }} ]], { i(1, "r"), i(2, "Receiver"), i(3, "Method"), i(4), i(5, "error"), i(0) })), -- Struct s("st", fmt([[ type {} struct {{ {} }} ]], { i(1, "Name"), i(0) })), -- Interface s("iface", fmt([[ type {} interface {{ {} }} ]], { i(1, "Name"), i(0) })), -- Test function s("test", fmt([[ func Test{}(t *testing.T) {{ {} }} ]], { i(1, "Name"), i(0) })), -- Table-driven test s("ttest", fmt([[ func Test{}(t *testing.T) {{ tests := []struct {{ name string {} }}{{ {{}}, }} for _, tt := range tests {{ t.Run(tt.name, func(t *testing.T) {{ {} }}) }} }} ]], { i(1, "Name"), i(2, "// fields"), i(0) })), } ``` ## VS Code Format Snippets You can also use JSON snippets in `snippets/vscode/`: ```json // snippets/vscode/typescript.json { "Console Log": { "prefix": "cl", "body": ["console.log($1)"], "description": "Console log" }, "Arrow Function": { "prefix": "af", "body": [ "const ${1:name} = (${2:params}) => {", " $0", "}" ], "description": "Arrow function" } } ``` ## Tips ### Trigger Naming Use short, memorable triggers: - `cl` → console.log - `fn` → function - `iferr` → if err != nil ### Tab Stop Order - `i(1)` → First tab stop - `i(2)` → Second tab stop - `i(0)` → Final cursor position (always last) ### Default Values ```lua i(1, "default") -- Shows "default", selected for replacement ``` ### Testing Snippets 1. Edit snippet file 2. `cS` - Reload 3. Open file of that type 4. Type trigger + `` ### Debugging Snippets If snippet doesn't work: 1. Check Lua syntax (`:luafile %`) 2. Verify filetype matches 3. Check trigger isn't conflicting ## Reloading ``` cS Reload all custom snippets ``` Changes take effect immediately after reload.