Improved flow
This commit is contained in:
449
docs/guides/snippets.md
Normal file
449
docs/guides/snippets.md
Normal file
@@ -0,0 +1,449 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user