改用 NeoBundle 管理 Vim plugin
Vundle 何時才會集滿 pull request
以 plugin 管理工具來說,Vundle 概念清楚、使用穩定、開發品質高(不是一個 toy project),名字也漂亮(重要);
比較不滿的是開發進度,已確認的 pull request 等了很久也沒 merge 回去,享受不到,著急。
這次整理 plugin 覺得卡在 Vundle 蠻悶的,就變節 NeoBundle 去了。
NeoBundle 是什麼
NeoBundle 的開發者是 neocomplcache 和 unite.vim 的作者 Shougo,他的 Vundle fork 改了太多東西,就獨立出來了。
使用上除了 :BundleInstall
的花俏畫面不見外,其他沒什麼損失;
新增功能(部份是實驗性的)則包括:
-
鎖定 plugin 版本,有新版也不更新
不想一直踩地雷的話,這個會很實用:NeoBundle 'Shougo/vimshell', '3787e5'
-
支援 svn 和 Mercurial (hg) repository
使用type
參數指定 svn 或 hg,也能從 URL 自動判斷:NeoBundle 'some.hg.repo.url', {'type': 'hg'}
其實 Vundle 也有實作,但目前未採用,理由參考 Pull Request #134: Adding more DVCS support by ralesi · gmarik/vundle, 比較傾向將來以 Vundle plugin 的方式實現。 -
自動使用 vimproc
如果有裝 vimproc,就會用 vimproc 執行外部指令。(這個我沒用過) -
unite.vim 介面
用途類似 Vundle 的 interactive mode,另開 window 瀏覽 plugin,可以各別按 i 進行 Install 等操作。
我認為 unite.vim 介面比較親切,因為不必學特殊 key mapping。
註::Unite neobundle
基本上需要裝 vimproc,否則只有 neobundle/log、neobundle/lazy 和 neobundle/search 可用。 -
base 和 nosync 選項
base
可以把特定 plugin 安裝在不一樣的目錄,nosync
可以讓 plugin 不自動更新,變成很例外的處理。NeoBundle 'muttator', {'type': 'nosync', 'base': '~/.vim/bundle'}
我發現測試 local plugin 的時候很好用,後述。 -
build 選項
安裝時可以自動執行指定的 script,如果 plugin 需要編譯就會有用:NeoBundle 'Shougo/vimproc', { \ 'build': { \ 'windows': 'echo "Sorry, cannot update vimproc binary file in Windows."', \ 'cygwin': 'make -f make_cygwin.mak', \ 'mac': 'make -f make_mac.mak', \ 'unix': 'make -f make_unix.mak', \ }, \ }
Vundle 作法是提供 callback 介面,不過目前還放在 events branch。 -
Lazy Load
少用的 plugin,可以先不加進 runtimepath 以縮短啟動時間。NeoBundleLazy 'klen/python-mode'
需要的時候再用:NeoBundleSource
讀進來:autocmd FileType python NeoBundleSource python-mode
或許可以用 autocommand FuncUndefined 達成自動 source 的功能。 -
其他看不見的變更
相信 NeoBundle 還重寫了很多東西,比如「改善:NeoBundle
的效率」。 不過這些東西在 doc 甚至 commit log 都不很清楚,其實也是個問題。
忘掉安裝程序
從 Vundle 轉換到 NeoBundle,大致上把一些目錄、指令改名即可,安裝過程幾乎沒變。
不過整個流程還是太特殊/脫序了,我不想每次查文件,所以乾脆把檢查寫進 .vimrc,若有問題,直接提示我該怎麼做。
-
設定一個目錄放所有 plugin,通常是 ~/.vim(我會視環境調整)下的 bundle 目錄。
(之後我會把預設的 runtimepath (~/.vim) 砍掉,讓 Vim 只吃 bundle 目錄裡的 script)
然後把 neobundle.vim 加進 runtimepath,才能繼續安裝。if has("gui_win32") " g:gvim_rtp should be set by gvimrc if exists('g:gvim_rtp') && isdirectory(fnamemodify(g:gvim_rtp, ':p')) let s:rtp = g:gvim_rtp else echoerr "You must set runtimepath (for plugin bundling) manually." finish endif else let s:rtp = "~/.vim" endif if !isdirectory(fnamemodify(s:rtp, ':p')) try call mkdir(fnamemodify(s:rtp, ':p'), "p") catch /^Vim\%((\a\+)\)\=:E739/ echoerr "Error detected while processing: " . v:throwpoint . ":\n " . v:exception . \ "\nCan't make runtime directory. Skipped sourcing vimrc.\n" finish endtry endif execute "set runtimepath+=" . fnamemodify(s:rtp, ':p') . "bundle/neobundle.vim"
-
呼叫
neobundle#rc
。
失敗的話就是沒裝 NeoBundle,請提示我git clone
到剛設定的目錄。try call neobundle#rc(fnamemodify(s:rtp, ':p') . "bundle") catch /^Vim\%((\a\+)\)\=:E117/ echoerr "Error detected while processing: " . v:throwpoint . ":\n " . v:exception . \ "\n\nNo 'Bundle plugin' installed for this vimrc. Skipped sourcing plugins." . \ "\n\nTo install one:\n " . \ "git clone http://github.com/Shougo/neobundle.vim.git " . fnamemodify(s:rtp, ":p") . "bundle/neobundle.vim\n" finish endtry
註:步驟 1、2 要包進if has('vim_starting') ... endif
才不會每次讀 .vimrc 時都做。 -
filetype off
。 這個步驟我沒研究為什麼 doc 說必須做(而且要在一開始做?),拿掉好像也沒事。
接著移除預設 runtimepath。filetype off set runtimepath-=~/.vim
-
列出要裝的 plugin,最後再把 filetype 開回來。
NeoBundle 'Shougo/neobundle.vim' NeoBundle 'nginx.vim' NeoBundle '有幾個 plugin,這裡就加幾行' filetype plugin indent on
註:實際上我不在這做:NeoBundle
,而是用後述 s:bundles 處理。 -
這樣就完成 NeoBundle 設定了。
進入 Vim 執行:NeoBundleInstall
把 plugin 都裝起來。
安裝後:NeoBundleLog
可以看 log,個人感覺比 Vundle 的輸出有用。
自己控制 bundle 列表
把舊的 :Bundle
全部換成 :NeoBundle
也是很空虛的動作,所以我把 plugin 列表寫成陣列,這樣即使回鍋 Vundle 也比較好改。
-
建立 s:bundles 變數,就可以自訂選項,減少對 bundle plugin 的依賴。
let s:bundles = [ \ ['Shougo/neobundle.vim'], \ ['L9'], \ ] let s:bundles += [ \ ['bootleq/vim-gitdiffall', {":prefer_local": 1}], \ ['nginx.vim'], \ ]
-
此處的自訂選項有 :skip 和 :prefer_local。
:skip 就是不要裝,因為寫成陣列就不能直接註解掉一行了……
:prefer_local 則是為了測試 plugin 方便,如果這個 plugin 有一份 repository 裝在 ~/repository,那就是我自己亂改的版本,請 NeoBundle 還是要讀它,但更新的時候要跳過。for bundle in s:bundles let s:tmp_options = get(bundle, 1, {}) if get(s:tmp_options, ":skip") continue elseif get(s:tmp_options, ":prefer_local") if isdirectory(fnamemodify('~/repository/' . split(bundle[0], '/')[-1], ':p')) let s:tmp_options = {"base": '~/repository', "type": "nosync"} endif endif call filter(s:tmp_options, "v:key[0] != ':'") execute "NeoBundle " . string(bundle[0]) . (empty(s:tmp_options) ? '' : ', ' . string(s:tmp_options)) endfor unlet! s:bundles s:tmp_options
Windows 沒有 git 怎麼辦
其實這次整理 plugin 最想做的是更新 Windows 上的 bundle,結果沒什麼進展。
最後做法是:
- 從有 git 的環境複製 .vimrc 和整個 bundle 目錄。
- gVim 的 vimrc 去
:source
真正使用的 .vimrc。
有 2 個意見
其中的 lazy load 可否在关闭特定文件的时候卸载之前装载的 plugin ?
比如我关闭 python 的文件,是否之前 load 的 python-mode 会卸载掉呢?
應該是不行。
有個 :NeoBundleDisable 指令,但也只是在載入前把 plugin 從 runtimepath 中拿掉而已。
卸載也有困難,第一不確定什麼時機可以卸載,第二很難做到徹底卸載(怎麼回到未載入的狀態)。
☂