V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
skywind3000
V2EX  ›  Vim

asyncrun 发布六年后的更新

  •  2
     
  •   skywind3000 · 2022-01-05 04:55:58 +08:00 · 2885 次点击
    这是一个创建于 813 天前的主题,其中的信息可能已经有所发展或是发生改变。

    asyncrun.vim 是个发布于今六年前的老插件了,常常被大家用来在 quickfix 中异步运行 shell 命令,当时 vim 还是 7.4.1829 ,+job 特性刚刚能用,因为做这个插件给 bram 提了十几个关于 +job 和 quickfix 的 issue ,都被他快速修正了,并在 8.1 时变得稳定可用。

    应该说这个插件是伴随 vim 8.0 一路成长稳定起来的,在修改了多年 bug 的同时,也通过收集到的有价值的反馈来不断的打磨自己。乘着新年之际,给大家分享一些该插件最近两年的主要更新,或许能够对大家提高工作效率有所帮助。

    外部终端

    不论 Vim 还是 NeoVim 引入内置终端都好多年了,但仍然有一些人习惯在系统终端或者 tmux 之类的地方执行任务。asyncrun 提供了一个 User-Defined Runners 的机制,可以让你按想要的方式运行命令,并同时发布了一些预置的 runner 让你可以方便的用外置终端运行特定命令:

    Runner 描述 依赖
    gnome 在新的 gnome-terminal 窗口里运行 GNOME
    gnome_tab 在 gnome-terminal 的新 tab 里运行 GNOME
    tmux 在 tmux pane 里运行 Vimux
    xfce 在新的 xfce 的 terminal 窗口里运行 xfce4-terminal
    konsole 在新的 konsole 终端里运行 KDE
    macos 在新的 macOS 系统 terminal 里运行 macOS
    iterm 在一个新的 iterm2 tab 里运行 macOS + iTerm2
    external 在一个新的 cmd 窗口里运行 Windows

    当使用 -mode=term 参数调用 asyncrun 命令时,可以用 -pos={runner} 参数来指定 runner 名字:

    :AsyncRun -mode=term -pos=gnome      ls -la
    :AsyncRun -mode=term -pos=floaterm   ls -la
    :AsyncRun -mode=term -pos=tmux       ls -la 
    

    当你在 GVim 里使用 gnome, konsole, external 或者 xfce 这些 runner 运行命令时,你将会得到同 IDE 里运行命令行程序一模一样的体验:

    如果你在终端下使用 Vim ,那么一个新的 gnome-terminal 或者 iterm2 的 tab 是个很恰当的方式:

    或者,在一个 tmux 的分屏里运行命令:

    除去外部终端外,还有不少 runner 可以和各种著名的 Vim/NeoVim 内置终端增强插件们打交道:

    Runner 描述 依赖
    floaterm 在一个新的 floaterm 窗口里 floaterm
    floaterm_reuse 在一个可复用的 floaterm 窗口里 floaterm
    quickui 在一个 quickui 的 popup 终端里 vim-quickui
    toggleterm 再一个 toggleterm 的窗口里 toggleterm.nvim
    termhelp 在 terminal-help 的窗口里(可复用) vim-terminal-help

    使用 floaterm 这个 runner 的效果:

    Terminal-help:

    所有 Runner 皆可定制,可以修改或者定义新的 runner ,见项目 wiki customize runner.

    Internal terminal

    另外一个比有意思的更新就是对 (neo)vim 内置终端的封装,可以用内置终端运行命令:

    " run command in the internal-terminal in a new tab 
    :AsyncRun -mode=term -pos=tab    ls -la
    :AsyncRun -mode=term -pos=TAB    ls -la
    
    " open on the left/right/top/bottom side
    :AsyncRun -mode=term -pos=left   ls -la
    :AsyncRun -mode=term -pos=right  ls -la
    :AsyncRun -mode=term -pos=top    ls -la
    :AsyncRun -mode=term -pos=bottom ls -la 
    

    你也许会奇怪,这和直接用内置的 :term xxx 运行 shell 命令有什么区别呢?为什么要用 asyncrun 来调度内置终端呢?理由很简单,便利性:

    • 命令里支持类似 $(VIM_FILENAME) 或者 $(VIM_ROOT) 的宏变量替换:

      :AsyncRun -mode=term -pos=TAB  ls -la $(VIM_FILEDIR)
      
    • 可以用 -cwd= 参数指定运行路径:

      :AsyncRun -mode=term -pos=TAB -cwd=~/github  ls -la
      
    • 更容易的指定终端窗口的位置和尺寸(非 tab 模式):

      :AsyncRun -mode=term -pos=bottom -rows=8  ls -la
      :AsyncRun -mode=term -pos=right -cols=40  ls -la
      
    • 可以用 -focus=0 参数进行无干扰模式(避免切换焦点到新的终端窗口上带来的分心):

      :AsyncRun -mode=term -pos=tab -focus=0  ls -la
      
    • 可以用 -close 来指定程序结束后自动关闭终端窗口:

      :AsyncRun -mode=term -pos=tab -focus=0 -close ls -la
      
    • 可以在任务结束时触发 vim 命令提醒你任务结束:

      :AsyncRun -mode=term -post=echo\ 'notify'   ls -la
      

    还有其他一些额外好处:

    • 配套系统变量类似 $VIM_FILENAME 之类的会同时初始化。
    • 可以用 -listed=0 来避免在 buffer-list 里列入 terminal buffer 。
    • 可以用 -hidden=1 来将 terminal buffer 的 bufhidden 设置为 hide
    • 可以用 -reuse 来指定复用已经结束的内置终端窗口。
    • 可以为 Vim / NeoVim 提供完全一致的体验(二者内置终端还是有不少体验上的差别)。

    最重要的是 vim/neovim 中用 :term xxx 传递包含多个单双引号,竖线,重定向符号等的复杂命令时,经常不能正确解析,特别是一些小众 shell 或者 windows 下面,而 AsyncRun 命令可以准确的传递参数。

    在终端下使用 Vim 时,我个人最喜欢的内置终端位置是 -pos=TAB,和小写的 -pos=tab 不同,这个方式会在当前 tabpage 的 左边 创建新 tab 运行内置终端命令。如此,在命令结束的时候,就能刚好回到我先前工作的 tabpage ,也回到我之前编辑的文档上:

    除去宏变量替换外,上面大部分行为是可以用 :term 命令搭配 2-3 条其他命令来完成,但是用 asyncrun 只需要一条唯一的命令就能搞定这些琐碎的事情,并且消除了 vim 和 neovim 的体验差别。

    Summary

    asyncrun.vim 提供了可以用任何方式运行命令的方式,并且为他们提供了统一的封装和诸如宏变量,工作目录指定,窗口位置和焦点控制之类的增强。你可以从项目的 wiki: 命令用法说明 查看更多用法。

    PS:如果你觉得为不同的项目或者文件类型建立 asyncrun 的 keymap 很麻烦,你可以试试兄弟插件 asynctasks.vim,它使用 asyncrun 作为运行命令的后端,并且给 vim 引入了类似 vscode 任务系统的机制,不管是运行外部命令还是执行内部 vim 命令,一切行为皆可以看作 “任务”,而对于同一个任务,在不同的项目内又有不同的执行方式,asynctasks.vim 可以帮你轻松的打理这些问题。

    16 条回复    2022-06-29 13:47:50 +08:00
    icySoda
        1
    icySoda  
       2022-01-05 09:16:33 +08:00 via iPhone
    这标题起得好像 6 年才更新一次似的
    kindjeff
        2
    kindjeff  
       2022-01-05 09:34:13 +08:00 via Android
    如果用 floatterm 等 terminal 插件的话,直接启动一个 terminal 再去敲命令不是更顺手么(另外 floatterm 和很多 shell 命令有直接的集成)
    kindjeff
        3
    kindjeff  
       2022-01-05 09:34:56 +08:00 via Android
    floatterm->floaterm
    xuboying
        4
    xuboying  
       2022-01-05 10:15:15 +08:00
    Asyncrun 应该可以理解成 vsc 的 task 吧,或者说后者是借鉴 asyncrun ?
    执行 ls 这种只是演示例子,实际上应该执行一些更复杂的命令
    skywind3000
        5
    skywind3000  
    OP
       2022-01-05 10:19:57 +08:00 via iPhone
    @kindjeff 对于偶然用的命令,终端里直接敲即可,对于重复命令,显然自动化更好,比如可以 map 到 f9 或者 f10 上,一按即可。
    skywind3000
        6
    skywind3000  
    OP
       2022-01-05 10:21:02 +08:00 via iPhone
    @xuboying asynctasks.vim 才是对标 vscode 的 task ,而且更强。
    haoliang
        7
    haoliang  
       2022-01-05 23:13:51 +08:00
    对 neovim 的深度支持我比较惊讶,因为结合之前在知乎看到的楼主关于 neovim 的回答,楼主给我的感觉是对 neovim 并不看好。
    完整看下来,我其实没有找到适合的使用场景。tmux (popup)、rofi 、neovim (lua 、floatwin) 已经解决我可以想见的异步任务使用场景了。
    可能我这回复像是单纯在刷存在感...不过我早上就看到这帖子,脑子里搁了一天,就当提供一个没有引入该工具的路人视角吧
    skywind3000
        8
    skywind3000  
    OP
       2022-01-06 06:33:53 +08:00
    @haoliang 这和你开发用的语言有关,比如你如果是开机启动个浏览器,然后就一直改代码,刷新浏览器,那么确实没有用武之地,你如果开发需要频繁编译,或者执行的程序,那你一看就知道用在哪里了。
    xuboying
        9
    xuboying  
       2022-01-06 10:51:10 +08:00
    趁着大佬在问个问题
    我现在用 vim 比较少,主要我觉得还是我不太会用。比如我想实现 git grep 以后加入到 leadf 的 quickfixwindow , 每次调用都很慢。我是这么做的:

    先在 quickui 里加一个内容,然后执行这个菜单,调用命令行,补完 git 的 grep 的关键字
    我觉得不满意的地方是,调用麻烦,mapping 用完了。quickui 也不能实现调用顺序重排序。操错比较慢
    命令重复执行不能重新补全,比如写错一个字需要修改一下,就得全部重写
    另外 quicikui 和 leadf 都很现代化了,输入筐还是老实的命令行,体验不是特别好。
    不知道大佬能不能给点建议。谢谢

    call quickui#menu#install("&File", [
    \ ['Asyncrun (git grep)', 'call _My_UI_asyncrun("git grep -nw --column " . expand("<cword>"))', ''],

    function! _My_UI_asyncrun(reply) abort
    let l:my_input=input("command=> ", a:reply)
    echo l:my_input
    execute 'AsyncRun -strip -raw -post=call\ _My_Update_quicklist() ' .l:my_input
    endfunction


    function! _My_Update_quicklist() abort
    let l:foo=getqflist()
    let l:bar=[]
    let l:skip=0
    for l:aaa in l:foo
    if l:aaa.valid==1
    let l:skip=1
    break
    endif
    call add(l:bar, l:aaa.text)
    endfor
    if l:skip == 0
    cgetexpr l:bar
    endif
    execute 'LeaderfQuickFix'
    endfunction
    skywind3000
        10
    skywind3000  
    OP
       2022-01-06 12:31:27 +08:00
    xuboying
        11
    xuboying  
       2022-01-06 17:27:20 +08:00
    @skywind3000 #10 感谢! quickui 让上了年纪人不用记太多复杂命令了,非常棒! Rebase 到最新的 tag 后能用这个 input 模块了。
    zbinlin
        12
    zbinlin  
       2022-01-09 21:38:58 +08:00
    可以实现 Bexec 插件那样的功能吗?

    PS:好像之前问过类似的问题?😹
    skywind3000
        13
    skywind3000  
    OP
       2022-01-12 22:50:17 +08:00   ❤️ 1
    @zbinlin 更新一下,我给你加了一下,通过 asyncrun 的 “命令修改器” 机制来完成:

    :AsyncRun -program=shebang -raw $(VIM_FILEPATH)
    :AsyncRun -program=shebang -mode=term -pos=top $(VIM_FILEPATH)
    skywind3000
        14
    skywind3000  
    OP
       2022-01-12 22:53:41 +08:00   ❤️ 1
    @zbinlin 你可以定义成一个命令:

    command! -nargs=0 AsyncBexec AsyncRun -program=shebang -mode=term -pos=top $(VIM_FILEPATH)

    然后用 :AsyncBexec 代替你的老命了
    Kaiv2
        15
    Kaiv2  
       2022-01-27 11:06:35 +08:00
    很好用,配合 LeaderF, floaterm 直接起飞
    linuxyz
        16
    linuxyz  
       2022-06-29 13:47:50 +08:00
    趕忙去 GitHub ❤👍 一個!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3125 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 12:54 · PVG 20:54 · LAX 05:54 · JFK 08:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.