GVim(Windows)下面使用!运行程序是非常恶心的事情,比如调用python运行当前脚本:

:!python %

你会发现,整个VIM界面被冻结了,然后弹出cmd窗口,退出cmd后,还要返回GVim中按任意键才能编辑状态。

比如你正在调试一个程序,这个程序运行起来不是一分钟能出结果的时候,你想边对照输出结果,边在 GVim 里面查看和修改你的代码,你就会发现傻逼了。正确的做法是:

:!start python %

这样就不会卡住 Vim了,但是这个做法有个问题,程序结束的时候窗口马上关闭,因此还需要再进化一下:

:silent !start cmd /c python % & pause

这时你会发现优雅的调用了 python 来跑当前程序,并且GVIM不会被挂起,照样可以编辑,当程序结束的时候,CMD窗口还会pause等待你按任意键一下,这就比较清爽了,你可以把这条命令map到你常用的快捷键上,和 EditPlus里面一样一键运行之。

然而还是有几处需要完善的地方,首先直接使用 % 展开当前文件名不妥当,如果文件名包含空格就会错误,需要用 shellescape 来进行处理,同时我们有时候还希望运行前跳转到文件所在的目录,运行完又跳转回来,又或者需要运行前保存一下没有保存的文件。

我写了个函数来做这个事情,放到你 .vimrc里即可使用,同时支持 GVim和终端 Vim:

" run current file by detecting file extname. if cwd is non-zero, 
" it will change the current directory to the file location.
" Unsaved files will be saved if needsave is non-zero
function! VimExecute(cwd, needsave)
    let savecwd = getcwd()
    let cd = haslocaldir()? 'lcd ' : 'cd '
    let ext = tolower(expand("%:e"))
    if bufname('%') == '' 
        return 
    endif
    if a:needsave != 0
        silent! update 
    endif
    if a:cwd != 0
        let dest = expand('%:p:h')
        silent! exec cd . fnameescape(dest)
    endif
    if has('gui_running') && (has('win32') || has('win64') || has('win95'))
        let fname = shellescape(expand("%"))
        if index(['c', 'cpp'], &filetype) >= 0
            exec '!start cmd /C '. shellescape(expand("%<")) . ' & pause'
        elseif index(['py', 'pyw', 'pyc', 'pyo'], ext) >= 0
            silent exec '!start cmd /C python ' . fname . ' & pause'
        elseif ext  == "js"
            silent exec '!start cmd /C node ' . fname . ' & pause'
        elseif ext == 'sh'
            silent exec '!start cmd /C sh ' . fname . ' & pause'
        elseif ext == 'lua'
            silent exec '!start cmd /C lua ' . fname . ' & pause'
        elseif ext == 'pl'
            silent exec '!start cmd /C perl ' . fname . ' & pause'
        elseif ext == 'rb'
            silent exec '!start cmd /C ruby ' . fname . ' & pause'
        elseif ext == 'php'
            silent exec '!start cmd /C php ' . fname . ' & pause'
        elseif ext == 'ps1'
            silent exec '!start cmd /C powershell '. fname. ' & pause'
        endif
    else
        if index(['c', 'cpp'], &filetype) >= 0
            exec '!'. shellescape(expand("%<"))
        elseif index(['py', 'pyw', 'pyc', 'pyo'], ext) >= 0
            exec '!python ' . shellescape(expand("%"))
        elseif ext  == "js"
            exec '!node ' . shellescape(expand("%"))
        elseif ext == 'sh'
            exec '!sh ' . shellescape(expand("%"))
        elseif ext == 'lua'
            exec '!lua ' . shellescape(expand("%"))
        elseif ext == 'pl'
            exec '!perl ' . shellescape(expand("%"))
        elseif ext == 'rb'
            exec '!ruby ' . shellescape(expand("%"))
        elseif ext == 'php'
            exec '!php ' . shellescape(expand("%"))
        elseif index(['osa', 'scpt', 'applescript'], ext) >= 0
            exec '!osascript '. shellescape(expand('%'))
        elseif ext == 'ps1'
            exec 'powershell '. shellescape(expand('%'))
        endif
    endif
    if a:cwd != 0
        silent! exec cd . fnameescape(savecwd)
    endif
endfunc

这个函数有两个参数,第一个参数非零的话,会在运行前跳转到文件所在目录,运行后又跳回来。第二个参数非零的话,能自动保存未保存的文件,同时支持 GVim和终端 Vim.

你可以把该函数绑定到 F5上,自动判断是否运行在GVIM下面,并用正确的方式执行当前代码,函数中几处外部调用用来判断文件是否需要保存,以及是否需要切换到文件当前目录的地方,你可以按需要扩充。