vim

Is it possible to have a modal popup_menu in vim?


I have written a function that will, given a directory and a title, display a popup with all the files contained in that folder:

function! ListFiles(dir, title)
  let l:dir = a:dir
  let l:title = a:title
  let l:files = glob(l:dir . '/*', 0, 1)
  let l:file_list = []
  for l:file in l:files
    call add(l:file_list, fnamemodify(l:file, ':t'))
  endfor

  " Define custom highlight groups
  highlight PopupMenu guifg=#ffffff guibg=#333333 ctermfg=white ctermbg=darkred
  highlight PopupMenuSelected guifg=#000000 guibg=#ffff00 ctermfg=black ctermbg=yellow

  " Create a popup menu with the file list
  let l:selected_file = popup_menu(l:file_list, {
        \ 'title': ' ' . l:title . ' ',
        \ 'callback': 's:HandleFileSelection',
        \ 'line': &lines / 2,
        \ 'col': &columns / 2,
        \ 'highlight': 'PopupMenu',
        \ 'border': [],
        \ 'close': 'click',
        \ 'padding': [1, 2, 1, 2],
        \ 'mapping': 0,
        \ 'filter': 'popup_filter_menu',
        \ 'wrap': 0
        \ })

  " Define a function to handle the selection
  function! s:HandleFileSelection(id, result)
    let l:file_name = ""
    if a:result != -1
      let l:file_name = getbufline(winbufnr(a:id), a:result)[0]
    endif
    echom "FOO.".l:selected_file
    return l:file_name
  endfunction

  " Return file
  echom "BAR.".l:selected_file
  return l:selected_file

endfunction

However, when I run it, I can see that BAR... message is displayed (with a number) before FOO... message appears (with the selected file) once you press enter on an item.

So it seems that the code first executes the return of the ListFiles function and then the one at the handler.

Why is this happening? Which would be the way to just get a single return with the selected item?


Solution

  • Well, that's how it works. No matter if calling popup_menu or popup_dialog, the only difference is due to filter function that can consume all user input while popup_menu is still active. However, there is no modal loop there.

    What to do? Just return immediately and continue execution inside the callback function.