Open-Source Wikis

/

Neovim

/

Features

/

Snippets

neovim/neovim

Snippets

Purpose

vim.snippet parses LSP snippet grammar strings, expands them at the cursor with placeholders, and lets the user tab through the placeholders. It is invoked automatically when an LSP completion item carries insertTextFormat = Snippet, and is callable directly via vim.snippet.expand(body).

Directory layout

runtime/lua/vim/
├── snippet.lua (~22k bytes)              Public API + state machine
runtime/lua/vim/lsp/
└── _snippet_grammar.lua (~6k bytes)      Pure-Lua LSP snippet parser

Key abstractions

Function Description
vim.snippet.expand(body) Parse and insert a snippet at the cursor.
vim.snippet.active() Is a snippet currently expanded?
vim.snippet.jump(direction) Move to next or previous placeholder.
vim.snippet.exit() End the current snippet session.

The grammar

LSP snippet syntax is a restricted superset of TextMate snippets:

${1:placeholder}                     -- placeholder
${1|one,two,three|}                  -- choice
${1:nested ${2:inner}}                -- nested tabstops
$0                                   -- final cursor
\$ \\ \}                              -- escapes
${VAR_NAME}                          -- variable

The parser in lsp/_snippet_grammar.lua is a hand-rolled descent parser that returns an AST of nodes: text, tabstop, placeholder, choice, variable. It is intentionally stricter than VS Code's parser to avoid silent corruption of text that looks but isn't a snippet.

How it works

graph LR
    Body["foo(${1:arg})$0"] --> Parse[parse → AST]
    Parse --> Insert[Insert text at cursor<br/>compute placeholder ranges]
    Insert --> Mark[Place extmarks at each tabstop]
    Mark --> Sel[Select placeholder 1]
    Sel --> Type[User types]
    Type --> Sync[Sync to other tabstops with same id]
    Type --> Tab[Tab → next placeholder]
    Tab --> Sel
    Tab -.->|done| Exit[Exit at $0]

expand(body):

  1. Parses the body to an AST.
  2. Computes the initial text — the AST flattened, with placeholders rendered to their default values.
  3. Inserts the text into the buffer at the cursor (using nvim_buf_set_text).
  4. Walks the AST to extract every tabstop occurrence; places extmarks at each occurrence's start and end.
  5. Selects the first placeholder by entering Visual mode with the cursor at its end.

Subsequent jump(1) / jump(-1) moves through tabstops in order. When the user types over a placeholder, the snippet engine records the new text and propagates it to every other extmark with the same tabstop id. When the user reaches $0 (or the last tabstop and there is no $0), the snippet session ends.

State

The state lives in module-locals in snippet.lua. There is one active snippet at a time per buffer; entering an outer snippet via a placeholder of an outer snippet pushes the inner one onto the stack. Exiting an inner snippet returns to the outer.

Integration points

  • LSP completionlsp/completion.lua checks insertTextFormat; when it's Snippet, the editor calls vim.snippet.expand(item.insertText).
  • Code actions — LSP code actions can return a workspace edit with snippet text; the engine handles those identically.
  • Text objectsvim.snippet does not register text objects. The <Tab> mapping for "jump" is suggested in :help vim.snippet but not installed by default.
  • Extmarks — every tabstop is an extmark. Edits inside the placeholder are tracked via nvim_buf_attach.

Entry points for modification

  • Support a new variable. Add it to the table in snippet.lua near _resolve_variable. Variables include TM_SELECTED_TEXT, CURRENT_YEAR, CLIPBOARD, WORKSPACE_NAME, LINE_COMMENT, etc. Several are stubbed; expanding the set is a small change.
  • Change the default <Tab> behavior. It's a user mapping, not built in. The recommended map appears in :help vim.snippet.jump.
  • Tweak placeholder syncing. The propagation logic is in the autocmd installed by expand. It handles overlapping ranges via the marktree.

Key source files

File Purpose
runtime/lua/vim/snippet.lua Public API + state machine
runtime/lua/vim/lsp/_snippet_grammar.lua LSP snippet parser
runtime/lua/vim/lsp/completion.lua Caller from completion
runtime/doc/lua.txt (snippet section) User docs

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Snippets – Neovim wiki | Factory