450 lines
7.8 KiB
Markdown
450 lines
7.8 KiB
Markdown
# 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 `<Tab>`. 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
|
|
|
|
```
|
|
<leader>cS Reload all snippets
|
|
```
|
|
|
|
### 4. Use Snippet
|
|
|
|
Type `cl` and press `<Tab>` 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 `<C-n>` / `<C-p>` 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 (
|
|
<div>
|
|
{}
|
|
</div>
|
|
)
|
|
}}
|
|
]], { 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. `<leader>cS` - Reload
|
|
3. Open file of that type
|
|
4. Type trigger + `<Tab>`
|
|
|
|
### Debugging Snippets
|
|
|
|
If snippet doesn't work:
|
|
1. Check Lua syntax (`:luafile %`)
|
|
2. Verify filetype matches
|
|
3. Check trigger isn't conflicting
|
|
|
|
## Reloading
|
|
|
|
```
|
|
<leader>cS Reload all custom snippets
|
|
```
|
|
|
|
Changes take effect immediately after reload.
|