Assigning every useful function to a key works well for frequently used tools, but there are some operations I use so rarely, that I would prefer to assign them to some popup menu defined in my vimrc.

I've seen plugins like CtrlP open a popup list so this should be possible.

So my question is:

How to define a custom popup menu which can run various commands?

Note: this should run in a terminal too.

Something like CtrlP plugin or dmenu on X11 would be ideal, where you can refine the options as you type, but a different kind of menu would be useful too.

2 Answers 2


Since vim 8.2 there are native popups. The above may now be achieved without any plugins:

func! s:lines_count()
    echom line('$') . ' lines in buffer'

func! s:cmdMenu()

     " variation of command-types
    let cmds = [
            \ "echom 'vim-version' version",
            \ 'call s:lines_count()',
            \ 'grep TODO'
            \ ]

    " callback for menu-popup
    func! s:selectedCommand(id, cmd) closure
        if a:cmd == -1  " menu was canceled
        " execute selection; NOTE menu-items ided from 1
        exe cmds[a:cmd-1]

    call popup_menu(['version', 'line-count', 'TODO'], #{
            \ callback: function('s:selectedCommand'),
            \ })

nnoremap <buffer> <silent> X :call <SID>cmdMenu()<cr>

Doing this out-of-the-box would require a fair amount of work, but I think you can do something fairly straight-forward using the Unite.vim plugin. It provides an integration interface for creating menus from various sources. (In fact, some have even replaced CtrlP with Unite.) This example in the Unite documentation (or take a look at :help g:unite_source_menu_menus, once you've installed Unite) details how to create a basic menu of commands.

Following that documentation, I've come up with a simple example that offers a menu of commands. For demonstration purposes, I've set it up with commands for opening NERDTree (from the NERDTree plugin), showing a git blame (from the fugitive.vim plugin) and grepping for TODOs in a project (using the built-in :grep). I've defined a mapping to open the menu with <Leader>c.

# Initialize Unite's global list of menus
if !exists('g:unite_source_menu_menus')
    let g:unite_source_menu_menus = {}

# Create an entry for our new menu of commands
let g:unite_source_menu_menus.my_commands = {
\    'description': 'My Commands'
\ }

# Define the function that maps our command labels to the commands they execute
function! g:unite_source_menu_menus.my_commands.map(key, value)
    return {
    \   'word': a:key,
    \   'kind': 'command',
    \   'action__command': a:value
    \ }

# Define our list of [Label, Command] pairs
let g:unite_source_menu_menus.my_commands.command_candidates = [
\   ['Open/Close NERDTree', 'NERDTreeToggle'],
\   ['Git Blame', 'Gblame'],
\   ['Grep for TODOs', 'grep TODO']
\ ]

# Create a mapping to open our menu
nnoremap <Leader>c :<C-U>Unite menu:my_commands -start-insert -ignorecase<CR>

You can copy this into your vimrc, and edit the list of commands defined by the array g:unite_source_menu_menus.my_commands.command_candidates. Each item of the array is an array of the form [Label, Command].

In my example, my_commands was a name I chose to identify my menu. You can use any name you'd like.

Hope this helps!

EDIT: Added -start-insert and -ignorecase options to the mapping to make the menu start in narrowing mode (like a fuzzy-search).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.