tessl install tessl/npm-coc-nvim@0.0.0LSP based intellisense engine for neovim & vim8.
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.
if coc#expandable()
call coc#rpc#request('doKeymap', ['snippets-expand'])
endifVerify cursor is on expandable snippet.
inoremap <silent><expr> <C-j> coc#snippet#next()Jump forward through snippet placeholders.
inoremap <silent><expr> <TAB>
\ coc#expandableOrJumpable() ? "\<C-r>=coc#snippet#next()\<CR>" :
\ "\<TAB>"Expand or jump with single key.
if get(b:, 'coc_snippet_active', 0)
echo "Snippet mode active"
endifQuery snippet state in buffer.
coc#expandable()
" Returns: booleanCheck 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'])
endifcoc#jumpable()
" Returns: booleanCheck if can jump to next snippet placeholder.
Returns: 1 if jumpable, 0 otherwise.
Example:
if coc#jumpable()
call coc#snippet#next()
endifcoc#expandableOrJumpable()
" Returns: booleanCheck 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>"coc#snippet#next()Jump to next snippet placeholder.
Example:
inoremap <silent><expr> <C-j> coc#snippet#next()coc#snippet#prev()Jump to previous snippet placeholder.
Example:
inoremap <silent><expr> <C-k> coc#snippet#prev()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)/**
* 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 otherwiseInternal Use: Typically called automatically by coc.nvim when entering snippet mode.
/**
* 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()/**
* 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 selectedInternal Use: Called by coc.nvim for placeholder selection.
/**
* 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.
/**
* 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)/**
* 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 fromInternal Use: Called automatically by coc.nvim for choice placeholders like ${1|option1,option2,option3|}.
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>'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>'let g:coc_selectmode_mapping = 1Enable select mode mappings for snippet placeholders.
Type: Number
Default: 1
Example:
" Disable select mode mappings
let g:coc_selectmode_mapping = 0b:coc_snippet_activeIndicates 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"
endifautocmd User CocJumpPlaceholder {command}Triggered when jumping to a snippet placeholder.
Example:
autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')CocSnippetVisualHighlight for snippet visual selection.
Example:
highlight CocSnippetVisual ctermbg=237 guibg=#3a3a3aSnippet 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"]
}
}" 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" 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>"" 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()" Show signature help when jumping placeholders
autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')function! SnippetStatus() abort
if get(b:, 'coc_snippet_active', 0)
return '[SNIP]'
endif
return ''
endfunction
set statusline+=%{SnippetStatus()}" 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()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()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()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>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()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>" Use Ctrl-Space to expand snippet
inoremap <silent><expr> <C-Space>
\ coc#expandable() ? "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand'])\<CR>" :
\ "\<C-Space>"" 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>"" 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" 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")
endfunctionIssue: 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()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()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()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()Symptoms: Pressing Tab inserts tab character instead of expanding snippet.
Solutions:
:echo coc#expandable():verbose imap <Tab>{"snippets.enable": true}Symptoms: Navigation keys don't move between placeholders.
Solutions:
:echo get(b:, 'coc_snippet_active', 0):echo coc#jumpable():echo g:coc_snippet_next
:echo g:coc_snippet_prev:verbose imap <C-j>Symptoms: No snippets appear in completion for certain file types.
Solutions:
{
"snippets.ultisnips.directories": [
"UltiSnips",
"~/.config/nvim/UltiSnips"
]
}:set filetype?{
"snippets.extends": {
"javascriptreact": ["javascript"]
}
}:CocCommand snippets.editSnippetsSymptoms: Can't select completion items, snippets expand instead.
Solutions:
inoremap <silent><expr> <TAB> coc#pum#visible() ? coc#pum#next(1) : "\<TAB>"
inoremap <silent><expr> <C-j> coc#expandableOrJumpable() ? coc#snippet#next() : "\<C-j>"function! SmartComplete() abort
if coc#pum#visible()
return coc#pum#next(1)
elseif coc#expandableOrJumpable()
return coc#snippet#next()
else
return "\<TAB>"
endif
endfunctionSymptoms: Can't type over placeholder text, inserts instead.
Solutions:
let g:coc_selectmode_mapping = 1:set selectmode?
" Should include: mouse,key:set selection?
" Should be: inclusive or exclusiveSymptoms: ${1|a,b,c|} syntax doesn't show choices.
Solutions:
:CocList extensions:set completeopt?
" Should include: menu,menuonecall coc#snippet#show_choices(line('.'), col('.'), 5, ['a', 'b', 'c'])