Skip to content

kizza/actionmenu.nvim

Repository files navigation

A nice context menu for vim.

Tests

Example

This is intended for use within vim plugins or scripts, to open a context menu and provide a callback with the selected item.

Usage

The menu is opened with a simple function. It will open at the current cursor location with whatever items your provide.

call actionmenu#open(items, callback, {opts})
  • items each item can be a string or a dictionary (see complete-items)
  • callback either a string or a funcref()
    • Will be invoked with the selected index (zero based) and item
    • Cancelling a selection will invoke the callback with an index of -1
  • opts (optional) Currently allows you to specify the icon for the menu

A simple example

Just paste the snippet below into your .vimrc then execute :call Demo()

func! Demo()
  call actionmenu#open(['First', 'Second', 'Third'], 'Callback')
endfunc

func! Callback(index, item)
  echo "I selected index " . a:index
endfunc

Example with Coc Code Actions (ie. context menu for a language server)

Use the actionmenu to list and execue available code actions from coc.nvim plugin

Once both actionmenu.nvim and coc.nvim are installed, put the folowing in your .vimrc file

let s:code_actions = []

func! ActionMenuCodeActions() abort
  if coc#float#has_float()
    call coc#float#close_all()
  endif

  let s:code_actions = CocAction('codeActions')
  let l:menu_items = map(copy(s:code_actions), { index, item -> item['title'] })
  call actionmenu#open(l:menu_items, 'ActionMenuCodeActionsCallback')
endfunc

func! ActionMenuCodeActionsCallback(index, item) abort
  if a:index >= 0
    let l:selected_code_action = s:code_actions[a:index]
    let l:response = CocAction('doCodeAction', l:selected_code_action)
  endif
endfunc

then map it to a keybinding eg. perhaps <Leader>s

nnoremap <silent> <Leader>s :call ActionMenuCodeActions()<CR>

asciicast

More examples

Below is an example using vim's complete-items allowing you to pass in more complex data

func! Demo()
  let l:items = [
    \ { 'word': 'First', 'abbr': '1st', 'user_data': 'Custom data 1' },
    \ { 'word': 'Second', 'abbr': '2nd', 'user_data': 'Custom data 2' },
    \ { 'word': 'Third', 'abbr': '3rd', 'user_data': 'Custom data 3' }
    \ ]

  call actionmenu#open(
    \ l:items,
    \ { index, item -> Callback(index, item) }
    \ )
endfunc

func! Callback(index, item)
  if a:index >= 0
    echo "Custom data is ". a:item['user_data']
  endif
endfunc

You can open the actionmenu with a custom icon (i've recently been using nerdfonts for this)

func! Demo()
  call actionmenu#open(
    \ ['First', 'Second', 'Third'],
    \ 'Callback',
    \ { 'icon': { 'character': 'X', 'foreground': 'yellow' } }
    \ )
endfunc

func! Callback(index, item)
  echo "I selected index " . a:index
endfunc

Requirements

The latest version of neovim (which provides the "popup window" functionaity that is leveraged)

brew install --HEAD neovim

and this plugin (show below using vim-plug installation - don't forget to run :PlugInstall)

if has('nvim')
  Plug 'kizza/actionmenu.nvim'
endif

Once installed, you can try it out by running :call actionmenu#example()

Finally

Build it into your existing plugin or useful scripts however you like!