2604,vim可定制对话框

原文

Vim里使用真正的可自定义对话框

作者:韦易笑

你有没有试过在Vim里搭一个表单?不是那种简单的单行提示,而是一个真正的有文本输入框,单选按钮,复选框,下拉列表确认按钮,全部集中在一个窗口里的表单?

如果你试过,你应该知道有多痛苦.

原方式有多难受

Vim本地给你的工具,只有input()单行输入,和inputlist()列表选择,就这些了.

想要问用户要一个项名?简单:

cpp 复制代码
let name = input("Project name: ")

再加一个邮箱:

cpp 复制代码
let name = input("Project name: ")
let email = input("Email: ")

两次阻塞式的提示.用户填完项名按回车,然后再填邮箱.填错了项名?对不起,回不去.没有可视化布局,没法同时看到两个字段.

再加个语言选择:

cpp 复制代码
let name = input("Project name: ")
let email = input("Email: ")
let lang = inputlist(["1. Python", "2. Go", "3. Rust"])

三个字段,三次独立的交互.这不是表单,这是审讯.

用户选完语言后想改项名?从头来过.

如果还需要一个"是否初化git仓库"的复选框呢?Vim没有inputcheck()该东西,你只能再搞一个input("初化git?(y/n):"),然后自己解析答案.

这样,不能扩展.

更好的方式:vimquickuiDialog

vimquickui是一个为VimNeoVim设计的TUI组件库,提供菜单,列表框,文本框等控件,全部用纯VimScript实现,不依赖任何外部工具.

1.5.0版本中,它新增了一套数据驱动对话框系统:按一组字典声明控件,quickui在一个弹窗中渲染它们;

用户完成操作后,按一个字典返回给你所有值.

不需要+神算,不需要Lua,不需要外部依赖,纯VimScript搞定.

安装

vimplug:

cpp 复制代码
Plug skywind3000/vimquickui"

或用Vim内置包管理:

cpp 复制代码
cd ~/.vim/pack/vendor/start && git clone https://github.com/skywind3000/vim-quickui

可选设置统一边框:

cpp 复制代码
let g:quickui_border_style = 2

好了,没有构建步骤,没有依赖.

你的第一个对话框

来简单设置对话框,如下放置进一个函数里:

cpp 复制代码
function! MySettings()
    let items = [
        \ {'type': 'label', 'text': 'Settings:'},
        \ {'type': 'input', 'name': 'name', 'prompt': 'Name:',
        \  'value': 'test'},
        \ {'type': 'radio', 'name': 'choice', 'prompt': 'Pick:',
        \  'items': ['A', 'B', 'C']},
        \ {'type': 'check', 'name': 'flag',
        \  'text': 'Enable Feature'},
        \ {'type': 'button', 'name': 'confirm',
        \  'items': [' &OK ', ' &Cancel ']},
        \ ]
    let result = quickui#dialog#open(items, {'title': 'Settings'})
    echo result
endfunc

执行:callMySettings()效果如下:略.

一个真正的对话框,在Vim里,带多个控件.

逐行解释一下:

1,label,顶部的静态文本,不可得焦

2,input,带提示标签和默认值文本输入框

3,radio,单选按钮组,只能选一个

4,check,复选框,可切换开关

5,button,底部的按钮行

可用制表符ShiftTab在控件间切换焦点,在输入框里直接输入,按空格切换复选框选择单选项,按回车点击按钮确认.

结果字典里返回所有的值.

怎么退出?

对话框关闭后,你需要知道两件事:用户是确认了还是取消了?如果确认了,是按了按钮还是在输入框按了回车?
返回值有两个关键字段:

1,button_index,按了哪个按钮(0开始),取消时为-1.

2,button,按钮的名字,如果是在非按钮上按回车或取消则为"".

这是在每个对话框里都会用到的判断模式:

cpp 复制代码
let r = quickui#dialog#open(items, opts)
if r.button_index == -1
  " 用户按了 ESC,CtrlC 或`关闭按钮`"
  echo "Cancelled"
elseif r.button == ""
  " 用户在输入框/单选/复选上按了回车"
  echo "Confirmed (Enter): name=" . r.name
else
  " 用户点击了按钮"
  echo "Button pressed: " . r.button . " #" . r.button_index
endif

几个要点:

1,button_index0开始.第一个按钮返回0,第二个返回1,等.

2,用按钮区分回车和按钮点击.当button_index为0时,检查r.button:如果为"",说明是在非按钮上按了回车;如果非空,说明是点击了第一个按钮.

3,取消时仍返回值.即使按了ESC,r.name等字段仍包含用户在取消前输入的内容.下次重新打开对话框时可恢复状态.

一般,只需要这样判断:

cpp 复制代码
let r = quickui#dialog#open(items, opts)
if r.button_index >= 0 && r.button != ""
  " 用户点击了按钮,处理`返回值`"
  echo "Name: " . r.name
endif

或如果有取消两个按钮:

cpp 复制代码
" " &OK " 是按钮 0," &Cancel " 是按钮 1"
if r.button_index == 0 && r.button != ""
  echo "Accepted: " . r.name
endif

一个实战示例

一个更贴近真实插件的:一个包含所有控件类型"新建项":

cpp 复制代码
function! NewProject()
    let items = [
        \ {'type': 'label', 'text': 'Create New Project:'},
        \ {'type': 'input', 'name': 'project_name', 'prompt': 'Project:'},
        \ {'type': 'input', 'name': 'email', 'prompt': 'Email:'},
        \ {'type': 'dropdown', 'name': 'language', 'prompt': 'Language:',
        \  'items': ['Python', 'JavaScript', 'Go', 'Rust', 'C++'],
        \  'value': 0},
        \ {'type': 'dropdown', 'name': 'build', 'prompt': 'Build:',
        \  'items': ['Make', 'CMake', 'Cargo', 'npm', 'pip'],
        \  'value': 0},
        \ {'type': 'radio', 'name': 'license', 'prompt': 'License:',
        \  'items': ['&MIT', '&Apache', '&GPL', '&Proprietary'],
        \  'value': 0},
        \ {'type': 'check', 'name': 'git_init',
        \  'text': 'Initialize git repo', 'value': 1},
        \ {'type': 'check', 'name': 'ci',
        \  'text': 'Add CI config'},
        \ {'type': 'button', 'name': 'confirm',
        \  'items': [' &Create ', '  Cancel  ']},
        \ ]

    let opts = {'title': 'New Project', 'w': 50, 'focus': 'project_name'}
    let result = quickui#dialog#open(items, opts)

    " 检查用户是否点击了 "Create" 按钮(按钮 0)"
    if result.button_index == 0 && result.button != ''
        let languages = ['Python', 'JavaScript', 'Go', 'Rust', 'C++']
        let builds = ['Make', 'CMake', 'Cargo', 'npm', 'pip']

        echo 'Project:  ' . result.project_name
        echo 'Email:    ' . result.email
        echo 'Language: ' . languages[result.language]
        echo 'Build:    ' . builds[result.build]
        echo 'License:  ' . result.license
        echo 'Git:      ' . (result.git_init ? 'yes' : 'no')
        echo 'CI:       ' . (result.ci ? 'yes' : 'no')
    else
        echo 'Cancelled'
    endif
endfunc

效果截图:略.

该例展示了几个要点:

1,下拉列表控件(dropdown)按一个折叠的选择框显示.按回车空格弹出选项列表供选择.返回值0开始的索引,需要自己映射回文本.

2,分隔线(separator)在复选框和按钮间画一条水平线,替代了控件间的默认空行,保持布局整洁.

3,opts.focus初始焦点设置到project_name输入框,用户打开对话框就能立刻开始输入.

4,提示文本自动对齐.注意Project:,Email:,Language:,Build:和License:这些标签都是左对齐的,它们后面的控件开始位置在同一列.

5,QuickUI自动计算最长的提示文本,补齐其余的.

6,热键标记.按钮,单选和复选文本中的&标记了热键符.比如&创建,&CreateC变成热键,在对话框任何地方(不在输入框中时)按C就可触发该按钮.

单选组的&MIT,&Apache同理.

使用技巧

分享几个我在开发对话过程中总结的经验:

1,先设好opts.w.如果不设宽度,QuickUI自动计算.对简单对话框没问题,但对有多个字段,显式设置一个宽度(比如50)可让布局更一致.

2,用"值"默认值.每个控件都支持字段.输入框接收串,单选/下拉/复选框接收数字.预填好默认值可让用户少打几个字.

3,复选框不需要提示标签.和输入框,单选不同,复选框的文本自身就是标签,不加提示看着更自然.如果想让它和其他有提示的控件对齐,也可加"提示"字段.

4,给按钮行命名.如果只有一行按钮,默认名字"按钮"就够了.但如果有两行按钮(比如"Apply/Reset""OK/Cancel"),要给它们不同,这样才能区分用户点的是哪一行.

更多进阶特性

这里只覆盖了基础用法.对话框系统还有更多能力:

1,输入历史,输入框历史字段在多次调用间共享历史记录,按Ctrl+Up/Ctrl+Down浏览

2,垂直单选,选项文本较长时,单选组会自动切换垂直布局

3,表单验证,opts.validator设置一个回调函数,在对话框关闭前验证字段值.

4,支持鼠标,点击任何控件即可得焦,切换或激活.

5,自定义颜色和边框,配合你的Vim配色方案
完整参考可查看vimquickui仓库中的DialogGuide.

不只是对话框

vimquickui不仅是对话框.它还提供:

1,顶部菜单栏,屏幕顶部下拉菜单,Borland/TurboC++风格

2,右键菜单,光标附近弹的环境菜单

3,列表框,带搜索功能的可滚动栏

4,文本框,在弹窗中显示文本

5,预览窗口,在光标附近预览文件内容

6,输入框,简单的单行输入(比完整对话框更轻量)

7,终端,在弹窗中运行命令

全部是纯VimScript实现,全部同时支持VimNeoVim.

相关推荐
Codigger官方2 天前
2026年4月Vim生态最新官方消息
linux·编辑器·vim
aidesignplus3 天前
从平方到线性:Mamba如何挑战Transformer的长序列效率瓶颈?
人工智能·python·深度学习·vim·transformer
李日灐5 天前
<5> Linux 开发工具:包管理 + Vim 实操 + GCC 编译流程 + 静态与动态链接详解
linux·运维·服务器·面试·vim·gcc
坚持就完事了5 天前
再谈编辑器Vim
linux·编辑器·vim
脆皮炸鸡7556 天前
进程的程序替换
linux·经验分享·笔记·vim·学习方法
buhuizhiyuci6 天前
笑谈vim的快捷方式和简易配置
linux·编辑器·vim
冷风阿健8 天前
Linux 源码安装 Vim 9.2.0272 完整步骤(修复 CVE-2026-34714)
linux·运维·vim
白菜欣8 天前
Linux —《从零上手Vim:核心用法全解析》
linux·编辑器·vim
Chockong8 天前
11_vim自动插入文件头模板(.c/.cpp/.py/.txt)设置
c语言·vim