如何提示用户输入列表中的多个条目?

How to prompt a user for multiple entries in a list?

我正在开发 vim 插件。配置参数有一组有效选项。我希望用户能够 select 从列表中选择他们想要的选项。

这类似于inputlist,但是inputlist只是return单个选择元素的索引。我更喜欢 return 所有选定元素的索引。

如何在 vim 中创建 mutliselect?

我不知道您具体想要哪种接口,但既然您提到了 inputlist(),我认为您可以简单地编写一个循环,其主体将调用它。

也许是这样的:

let options_chosen = []
let options_valid  = [
                     \ 'foo',
                     \ 'bar',
                     \ 'baz',
                     \ 'qux',
                     \ 'norf'
                     \ ]

for i in range(1,len(options_valid))
    let choice = inputlist([ 'Select your options:' ]
                          \ + map(copy(options_valid), '(v:key+1).". ".v:val'))

    if choice >= 1 && choice <= len(copy(options_valid))
        let options_chosen += [copy(options_valid)[choice - 1]]
        let options_valid = filter(options_valid, 'v:val !=# options_chosen[-1]')
    else
        break
    endif

    redraw
endfor

如果您执行此代码,它应该让您从列表中选择一个选项 options_valid。每次迭代后,它应该将所选项目添加到列表 options_chosen 中并将其从列表 options_valid 中删除。循环迭代次数与最初 options_valid 中的项目一样多。完成后,您可以通过点击 Escape.

来停止循环

它可能不是你想要的,因为我不知道你想呈现给用户的界面是什么:一个命令,一个映射,一个交互式缓冲区......但它可能是一个开始,在此基础上你可以建造别的东西。


以交互式缓冲区作为界面,我想到了这个:

let s:options_valid = ['foo', 'bar', 'baz', 'qux', 'norf']

com! MultipleOptions call s:multiple_options()

fu! s:multiple_options() abort
    vnew | exe 'vert resize '.(&columns/3)
    setl bh=wipe bt=nofile nobl noswf nowrap
    if !bufexists('Multiple Options') | sil file Multiple\ Options | endif

    sil! 0put =s:options_valid
    sil! $d_
    setl noma ro

    nno <silent> <buffer> <nowait> q     :<c-u>close<cr>
    nno <silent> <buffer> <nowait> <cr>  :<c-u>call <sid>toggle_option()<cr>

    augroup multi_op_close
        au!
        au WinLeave <buffer> call s:close()
    augroup END
endfu

fu! s:close() abort
    let g:selected_options = exists('w:options_chosen')
                           \   ? map(w:options_chosen.lines, 's:options_valid[v:val-1]')
                           \   : []
    au! multi_op_close | aug! multi_op_close
    close
endfu

fu! s:toggle_option() abort
    if !exists('w:options_chosen')
        let w:options_chosen = { 'lines' : [], 'pattern' : '', 'id' : 0 }
    else
        if w:options_chosen.id
            call matchdelete(w:options_chosen.id)
            let w:options_chosen.pattern .= '|'
        endif
    endif

    if !empty(w:options_chosen.lines) && count(w:options_chosen.lines, line('.'))
        call filter(w:options_chosen.lines, "v:val != line('.')")
    else
        let w:options_chosen.lines += [ line('.') ]
    endif

    let w:options_chosen.pattern = '\v'.join(map(
                                 \               copy(w:options_chosen.lines),
                                 \               "'%'.v:val.'l'"
                                 \              ), '|')

    let w:options_chosen.id = !empty(w:options_chosen.lines)
                            \   ? matchadd('IncSearch', w:options_chosen.pattern)
                            \   : 0
endfu

如果你执行命令:MultipleOptions,它应该打开一个临时的垂直视口,其中应该显示存储在列表s:options_valid中的选项。

从那里,您可以点击 Enter 到 select 或取消 select 当前行。当一个选项被 selected 时,它的行用高亮组 IncSearch.

着色

完成后,您可以关闭 window 点击 q,您选择的所有选项都应该在 g:selected_options.

lh-vim-lib 中,我提供了一个 lh#ui#check() 函数来执行此操作。它的行为类似于文本模式下的 confirm()。也就是说,我依赖于破解状态线的老把戏。 (这个需要玩"\r":redraw等等)

您可以在 screencast I've made for lh-tags 中实时看到它。等待"Which kinds to you wish to display?"问题。 (在截屏视频中,您应该看到带有 CHECKCONFIRMCHOOSE 的旧代码)

顺便说一句,lh-tags 中用于选择一个条目的对话框也可以用于(将 tagged 参数设置为一个)一次 select 多个条目。