or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/coc.nvim@0.0.x

docs

advanced

commands.mdcursor-navigation.mdmulti-cursor.mdrpc-lsp.mdsnippets.md
index.md
tile.json

tessl/npm-coc-nvim

tessl install tessl/npm-coc-nvim@0.0.0

LSP based intellisense engine for neovim & vim8.

snippets.mddocs/advanced/

Snippets [Intermediate]

Complexity: Intermediate | Category: Advanced | Keywords: snippets, expand, jump, placeholder, ultisnips

Common Tasks: Expand snippets | Navigate placeholders | Check snippet state | Configure snippet keys

Code snippet expansion and navigation with support for placeholders and variables.

Common Tasks

Check if snippet can be expanded

if coc#expandable()
  call coc#rpc#request('doKeymap', ['snippets-expand'])
endif

Verify cursor is on expandable snippet.

Navigate to next placeholder

inoremap <silent><expr> <C-j> coc#snippet#next()

Jump forward through snippet placeholders.

Smart Tab key for snippets

inoremap <silent><expr> <TAB>
  \ coc#expandableOrJumpable() ? "\<C-r>=coc#snippet#next()\<CR>" :
  \ "\<TAB>"

Expand or jump with single key.

Check if snippet is active

if get(b:, 'coc_snippet_active', 0)
  echo "Snippet mode active"
endif

Query snippet state in buffer.

Core Functions

coc#expandable()

coc#expandable()
" Returns: boolean

Check if a snippet can be expanded at the cursor position.

Returns: 1 if snippet is expandable, 0 otherwise.

Example:

if coc#expandable()
  call coc#rpc#request('doKeymap', ['snippets-expand'])
endif

coc#jumpable()

coc#jumpable()
" Returns: boolean

Check if can jump to next snippet placeholder.

Returns: 1 if jumpable, 0 otherwise.

Example:

if coc#jumpable()
  call coc#snippet#next()
endif

coc#expandableOrJumpable()

coc#expandableOrJumpable()
" Returns: boolean

Check if can expand snippet or jump to placeholder.

Returns: 1 if expandable or jumpable, 0 otherwise.

Example:

inoremap <silent><expr> <TAB>
  \ coc#expandableOrJumpable() ? "\<C-r>=coc#snippet#next()\<CR>" :
  \ "\<TAB>"

Snippet Functions

coc#snippet#next()

coc#snippet#next()

Jump to next snippet placeholder.

Example:

inoremap <silent><expr> <C-j> coc#snippet#next()

coc#snippet#prev()

coc#snippet#prev()

Jump to previous snippet placeholder.

Example:

inoremap <silent><expr> <C-k> coc#snippet#prev()

coc#snippet#jump()

coc#snippet#jump(direction, complete)

Jump placeholder in direction.

Parameters:

  • direction: 1 for next, -1 for previous (number)
  • complete: Whether to trigger completion after jump (boolean)

Example:

" Jump forward
call coc#snippet#jump(1, 0)

" Jump backward
call coc#snippet#jump(-1, 0)

coc#snippet#enable()

/**
 * Enable snippet mode with keybindings
 * Sets up buffer-local insert and select mode mappings for snippet navigation
 * @param complete - Optional: 1 to trigger completion on jump, 0 otherwise (default: 0)
 */
coc#snippet#enable(...)

Enable snippet session. Sets up keybindings for next/prev placeholder navigation using g:coc_snippet_next and g:coc_snippet_prev keys. Also configures select mode mappings for snippet placeholders.

Parameters:

  • complete (optional): 1 to trigger completion when jumping forward, 0 otherwise

Internal Use: Typically called automatically by coc.nvim when entering snippet mode.

coc#snippet#disable()

/**
 * Disable snippet mode and remove keybindings
 * Unmaps buffer-local snippet navigation keys
 */
coc#snippet#disable()

Disable snippet session and remove buffer-local keybindings for snippet navigation.

Example:

call coc#snippet#disable()

coc#snippet#select()

/**
 * Select text range in snippet
 * Handles visual selection in snippet placeholders
 * @param start - Start position {line, character}
 * @param end - End position {line, character}
 * @param text - Text content to select
 */
coc#snippet#select(start, end, text)

Select snippet range with proper visual mode handling. Closes popup menu if visible and enters select mode for the specified text range.

Parameters:

  • start: Start position object with {line, character} properties (LSP format, 0-indexed)
  • end: End position object with {line, character} properties (LSP format, 0-indexed)
  • text: Text content to be selected

Internal Use: Called by coc.nvim for placeholder selection.

coc#snippet#move()

/**
 * Move cursor to snippet position and enter insert mode
 * @param position - Target position {line, character}
 */
coc#snippet#move(position)

Move cursor to snippet position. Closes popup menu if visible, exits select mode if active, then moves to the specified position and enters insert mode.

Parameters:

  • position: Position object with {line, character} properties (LSP format, 0-indexed)

Internal Use: Used for snippet placeholder navigation.

coc#snippet#to_cursor()

/**
 * Convert LSP position to Vim cursor position
 * @param position - LSP position {line, character}
 * @returns [line, col] - Vim cursor position (1-indexed)
 */
coc#snippet#to_cursor(position)

Convert LSP position (0-indexed line and character) to Vim cursor position (1-indexed line and byte column). Handles multibyte characters correctly.

Parameters:

  • position: Position object with line and character properties (0-indexed)

Returns: [line, col] array where line and col are 1-indexed

Example:

let lsp_pos = {'line': 5, 'character': 10}
let [line, col] = coc#snippet#to_cursor(lsp_pos)
call cursor(line, col)

coc#snippet#show_choices()

/**
 * Show completion menu for snippet choice placeholder
 * @param lnum - Line number (1-indexed)
 * @param col - Column position (1-indexed)
 * @param len - Length of placeholder text
 * @param values - Array of choice values to display
 */
coc#snippet#show_choices(lnum, col, len, values)

Display completion menu for snippet choice placeholders. Positions cursor and triggers coc's completion menu with the provided choices.

Parameters:

  • lnum: Line number (1-indexed)
  • col: Column position (1-indexed)
  • len: Length of the placeholder text (number)
  • values: Array of string values for user to choose from

Internal Use: Called automatically by coc.nvim for choice placeholders like ${1|option1,option2,option3|}.

Global Variables

g:coc_snippet_next

let g:coc_snippet_next = '<C-j>'

Key binding for jumping to next placeholder.

Type: String Default: '<C-j>'

Example:

let g:coc_snippet_next = '<Tab>'

g:coc_snippet_prev

let g:coc_snippet_prev = '<C-k>'

Key binding for jumping to previous placeholder.

Type: String Default: '<C-k>'

Example:

let g:coc_snippet_prev = '<S-Tab>'

g:coc_selectmode_mapping

let g:coc_selectmode_mapping = 1

Enable select mode mappings for snippet placeholders.

Type: Number Default: 1

Example:

" Disable select mode mappings
let g:coc_selectmode_mapping = 0

Buffer Variables

b:coc_snippet_active

b:coc_snippet_active

Indicates if snippet session is active in buffer.

Type: Number (read-only) Values: 1 when active, 0 otherwise

Example:

if get(b:, 'coc_snippet_active', 0)
  echo "Snippet is active"
endif

Autocmd Events

User CocJumpPlaceholder

autocmd User CocJumpPlaceholder {command}

Triggered when jumping to a snippet placeholder.

Example:

autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')

Highlight Groups

CocSnippetVisual

CocSnippetVisual

Highlight for snippet visual selection.

Example:

highlight CocSnippetVisual ctermbg=237 guibg=#3a3a3a

Configuration

Snippet settings in coc-settings.json:

{
  "snippets.enable": true,
  "snippets.priority": 90,
  "snippets.editSnippets": true,
  "snippets.textmateSnippetsRoots": [],
  "snippets.ultisnips.enable": true,
  "snippets.ultisnips.directories": [
    "UltiSnips",
    "~/.config/coc/ultisnips"
  ],
  "snippets.snipmate.enable": false,
  "snippets.extends": {
    "cpp": ["c"],
    "javascriptreact": ["javascript"],
    "typescriptreact": ["typescript"]
  }
}

Usage Examples

Basic Snippet Navigation

" Use Tab for trigger completion and snippet expand/jump
inoremap <silent><expr> <TAB>
  \ coc#pum#visible() ? coc#pum#next(1) :
  \ coc#expandableOrJumpable() ? "\<C-r>=coc#snippet#next()\<CR>" :
  \ CheckBackspace() ? "\<TAB>" :
  \ coc#refresh()

inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"

function! CheckBackspace() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

Separate Keys for Snippets

" Use Ctrl-j/k for snippet navigation
let g:coc_snippet_next = '<C-j>'
let g:coc_snippet_prev = '<C-k>'

" Explicit mappings
inoremap <silent><expr> <C-j> coc#jumpable() ? "\<C-r>=coc#snippet#next()\<CR>" : "\<C-j>"
inoremap <silent><expr> <C-k> coc#jumpable() ? "\<C-r>=coc#snippet#prev()\<CR>" : "\<C-k>"

Expand or Jump

" Smart Tab: expand snippet or jump
function! SmartTab() abort
  if coc#expandable()
    return "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand'])\<CR>"
  elseif coc#jumpable()
    return "\<C-r>=coc#snippet#next()\<CR>"
  elseif coc#pum#visible()
    return coc#pum#next(1)
  else
    return "\<TAB>"
  endif
endfunction

inoremap <silent><expr> <TAB> SmartTab()

Signature Help on Placeholder Jump

" Show signature help when jumping placeholders
autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')

Snippet Status in Statusline

function! SnippetStatus() abort
  if get(b:, 'coc_snippet_active', 0)
    return '[SNIP]'
  endif
  return ''
endfunction

set statusline+=%{SnippetStatus()}

Exit Snippet Mode

" Press Escape to exit snippet mode
function! ExitSnippet() abort
  if get(b:, 'coc_snippet_active', 0)
    call coc#snippet#disable()
    echo "Snippet mode exited"
    return "\<Esc>"
  endif
  return "\<Esc>"
endfunction

inoremap <silent><expr> <Esc> ExitSnippet()

Check Snippet State

function! CheckSnippetState() abort
  if !exists('*coc#expandable')
    echo "Coc not loaded"
    return
  endif

  echo "Expandable: " . coc#expandable()
  echo "Jumpable: " . coc#jumpable()
  echo "Active: " . get(b:, 'coc_snippet_active', 0)
endfunction

command! SnippetState call CheckSnippetState()

Conditional Snippet Expansion

function! SmartExpand() abort
  " Only expand in certain filetypes
  let allowed = ['javascript', 'typescript', 'python', 'go']

  if index(allowed, &filetype) < 0
    return "\<TAB>"
  endif

  if coc#expandableOrJumpable()
    return "\<C-r>=coc#snippet#next()\<CR>"
  endif

  return "\<TAB>"
endfunction

inoremap <silent><expr> <TAB> SmartExpand()

Jump with Count

function! JumpSnippet(count, direction) abort
  for i in range(a:count)
    if !coc#jumpable()
      break
    endif

    if a:direction > 0
      call coc#snippet#next()
    else
      call coc#snippet#prev()
    endif
  endfor
endfunction

" Jump 3 placeholders forward
nnoremap <silent> 3<C-j> :call JumpSnippet(3, 1)<CR>

Snippet History

let g:snippet_history = []

function! TrackSnippetJump() abort
  let pos = [bufnr('%'), line('.'), col('.')]
  call add(g:snippet_history, pos)

  " Keep only last 10 positions
  if len(g:snippet_history) > 10
    call remove(g:snippet_history, 0)
  endif
endfunction

autocmd User CocJumpPlaceholder call TrackSnippetJump()

function! ShowSnippetHistory() abort
  echo "Snippet jump history:"
  for entry in g:snippet_history
    echo entry
  endfor
endfunction

command! SnippetHistory call ShowSnippetHistory()

Disable Snippets Temporarily

let g:snippets_enabled = 1

function! ToggleSnippets() abort
  if g:snippets_enabled
    let g:snippets_enabled = 0
    call coc#config('snippets.enable', v:false)
    echo "Snippets disabled"
  else
    let g:snippets_enabled = 1
    call coc#config('snippets.enable', v:true)
    echo "Snippets enabled"
  endif
endfunction

command! ToggleSnippets call ToggleSnippets()
nnoremap <leader>ts :ToggleSnippets<CR>

Custom Snippet Key

" Use Ctrl-Space to expand snippet
inoremap <silent><expr> <C-Space>
  \ coc#expandable() ? "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand'])\<CR>" :
  \ "\<C-Space>"

Snippet Completion Integration

" Tab: completion or snippet
inoremap <silent><expr> <TAB>
  \ coc#pum#visible() ? coc#_select_confirm() :
  \ coc#expandableOrJumpable() ? "\<C-r>=coc#snippet#next()\<CR>" :
  \ CheckBackspace() ? "\<TAB>" :
  \ coc#refresh()

" Enter: confirm completion and start snippet
inoremap <silent><expr> <CR>
  \ coc#pum#visible() ? coc#_select_confirm() :
  \ "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

UltiSnips Integration

" Configure UltiSnips directories
{
  "snippets.ultisnips.enable": true,
  "snippets.ultisnips.directories": [
    "UltiSnips",
    "~/.config/nvim/UltiSnips",
    "~/.vim/UltiSnips"
  ]
}

" Edit snippets for current filetype
command! EditSnippets CocCommand snippets.editSnippets

Visual Selection Snippet

" Use visual selection in snippet
xnoremap <silent> <TAB>
  \ <Esc>:call <SID>snippetFromVisual()<CR>

function! s:snippetFromVisual() abort
  let text = GetVisualSelection()
  " Insert snippet with selected text
  call coc#rpc#request('insertSnippet', [text])
endfunction

function! GetVisualSelection() abort
  let [line_start, column_start] = getpos("'<")[1:2]
  let [line_end, column_end] = getpos("'>")[1:2]
  let lines = getline(line_start, line_end)
  if len(lines) == 0
    return ''
  endif
  let lines[-1] = lines[-1][: column_end - 1]
  let lines[0] = lines[0][column_start - 1:]
  return join(lines, "\n")
endfunction

Error Handling

Expansion Fails

Issue: Snippet doesn't expand when expected.

Solution:

function! SafeExpandSnippet() abort
  if !coc#expandable()
    echohl WarningMsg
    echo 'No snippet to expand at cursor'
    echohl None
    return "\<TAB>"
  endif

  " Check if snippet session is already active
  if get(b:, 'coc_snippet_active', 0)
    echohl WarningMsg
    echo 'Snippet already active'
    echohl None
    return coc#snippet#next()
  endif

  return "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand'])\<CR>"
endfunction

inoremap <silent><expr> <TAB> SafeExpandSnippet()

Jump Out of Bounds

Issue: Attempting to jump when no more placeholders exist.

Solution:

function! SafeJumpNext() abort
  if !coc#jumpable()
    " No more placeholders, exit snippet mode
    call coc#snippet#disable()
    return "\<Tab>"
  endif

  return coc#snippet#next()
endfunction

inoremap <silent><expr> <C-j> SafeJumpNext()

Placeholder Selection Issues

Issue: Select mode doesn't work properly for placeholders.

Solution:

" Ensure select mode mappings are enabled
let g:coc_selectmode_mapping = 1

" Check select mode behavior
function! CheckSelectMode() abort
  if &selectmode !~# 'mouse'
    set selectmode+=mouse
  endif
  if &selectmode !~# 'key'
    set selectmode+=key
  endif
endfunction

autocmd User CocNvimInit call CheckSelectMode()

Snippet State Corruption

Issue: Snippet state becomes inconsistent.

Solution:

function! ResetSnippetState() abort
  " Force disable snippet session
  call coc#snippet#disable()

  " Clear snippet-related variables
  if exists('b:coc_snippet_active')
    unlet b:coc_snippet_active
  endif

  " Exit any special modes
  if mode() == 's'
    execute "normal! \<Esc>"
  endif

  echo 'Snippet state reset'
endfunction

command! SnippetReset call ResetSnippetState()

Troubleshooting

Problem: Tab key not expanding snippets

Symptoms: Pressing Tab inserts tab character instead of expanding snippet.

Solutions:

  1. Check if coc#expandable() returns true:
    :echo coc#expandable()
  2. Verify snippet mappings are set:
    :verbose imap <Tab>
  3. Ensure snippets are enabled in config:
    {"snippets.enable": true}
  4. Check if snippet trigger matches cursor position

Problem: Can't jump between placeholders

Symptoms: Navigation keys don't move between placeholders.

Solutions:

  1. Verify snippet is active:
    :echo get(b:, 'coc_snippet_active', 0)
  2. Check jumpable state:
    :echo coc#jumpable()
  3. Review key binding variables:
    :echo g:coc_snippet_next
    :echo g:coc_snippet_prev
  4. Ensure no mapping conflicts:
    :verbose imap <C-j>

Problem: Snippets not available for filetype

Symptoms: No snippets appear in completion for certain file types.

Solutions:

  1. Check snippet paths:
    {
      "snippets.ultisnips.directories": [
        "UltiSnips",
        "~/.config/nvim/UltiSnips"
      ]
    }
  2. Verify filetype is correct:
    :set filetype?
  3. Check snippet extends configuration:
    {
      "snippets.extends": {
        "javascriptreact": ["javascript"]
      }
    }
  4. Reload snippets:
    :CocCommand snippets.editSnippets

Problem: Snippet expansion interferes with completion

Symptoms: Can't select completion items, snippets expand instead.

Solutions:

  1. Separate completion and snippet keys:
    inoremap <silent><expr> <TAB> coc#pum#visible() ? coc#pum#next(1) : "\<TAB>"
    inoremap <silent><expr> <C-j> coc#expandableOrJumpable() ? coc#snippet#next() : "\<C-j>"
  2. Use smart expansion logic:
    function! SmartComplete() abort
      if coc#pum#visible()
        return coc#pum#next(1)
      elseif coc#expandableOrJumpable()
        return coc#snippet#next()
      else
        return "\<TAB>"
      endif
    endfunction

Problem: Select mode doesn't work in snippets

Symptoms: Can't type over placeholder text, inserts instead.

Solutions:

  1. Enable select mode mapping:
    let g:coc_selectmode_mapping = 1
  2. Check select mode setting:
    :set selectmode?
    " Should include: mouse,key
  3. Verify behavior setting:
    :set selection?
    " Should be: inclusive or exclusive

Problem: Snippet choice placeholders not showing

Symptoms: ${1|a,b,c|} syntax doesn't show choices.

Solutions:

  1. Ensure coc-snippets extension is installed:
    :CocList extensions
  2. Check if completion menu is configured:
    :set completeopt?
    " Should include: menu,menuone
  3. Try manual expansion:
    call coc#snippet#show_choices(line('.'), col('.'), 5, ['a', 'b', 'c'])

See Also

  • Completion - Integrate snippets with completion
  • Configuration - Configure snippet behavior
  • Cursor Navigation - Position APIs used by snippets
  • Commands - Snippet-related commands
  • Variables - Snippet configuration variables