2020年了,不要再看網(wǎng)上那些老舊的文章還在教你使用手工生成 tags 的,請(qǐng)使用自動(dòng)代碼索引生成工具,比如 vim-gutentags,現(xiàn)在網(wǎng)上好像就沒有一篇能正確討論 Vim C/C++ 環(huán)境搭建的,都在談些十年前的東西,所以我寫了篇關(guān)于 Vim 8 和 C/C++ 相關(guān)插件的介紹:
假設(shè)你已經(jīng)有一定 Vim 使用經(jīng)驗(yàn),并且折騰過(guò) Vim 配置,能夠相對(duì)舒適的在 Vim 中編寫其他代碼的時(shí)候,準(zhǔn)備在 Vim 開始 C/C++ 項(xiàng)目開發(fā),或者你已經(jīng)用 Vim 編寫了幾年 C/C++ 代碼,想要更進(jìn)一步,讓自己的工作更加順暢的話,本文就是為你準(zhǔn)備的:
插件管理
為什么把插件管理放在第一個(gè)來(lái)講呢?這是比較基本的一個(gè)東西,如今 Vim 下熟練開發(fā)的人,基本上手都有 20-50 個(gè)插件,遙想十年前,Vim里常用的插件一只手都數(shù)得過(guò)來(lái)。過(guò)去我一直使用老牌的 Vundle 來(lái)管理插件,但是隨著插件越來(lái)越多,更新越來(lái)越頻繁,Vundle 這種每次更新就要好幾分鐘的東西實(shí)在是不堪重負(fù)了。
在我逐步對(duì) Vundle 失去耐心之后,我試用了 vim-plug ,用了兩天以后就再也回不去 Vundle了,它支持全異步的插件安裝,安裝50個(gè)插件只需要一分鐘不到的時(shí)間,這在 Vundle 下面根本不可想像的事情,插件更新也很快,不像原來(lái)每次更新都可以去喝杯茶去,最重要的是它支持插件延遲加載:
“ 定義插件,默認(rèn)用法,和 Vundle 的語(yǔ)法差不多Plug ‘junegunn/vim-easy-align’Plug ‘skywind3000/quickmenu.vim’” 延遲按需加載,使用到命令的時(shí)候再加載或者打開對(duì)應(yīng)文件類型才加載Plug ‘scrooloose/nerdtree’, { ‘on’: ‘NERDTreeToggle’ }Plug ‘tpope/vim-fireplace’, { ‘for’: ‘clojure’ }“ 確定插件倉(cāng)庫(kù)中的分支或者 tagPlug ‘rdnetto/YCM-Generator’, { ‘branch’: ‘stable’ }Plug ‘nsf/gocode’, { ‘tag’: ‘v.20150303’, ‘rtp’: ‘vim’ }
定義好插件以后一個(gè) :PlugInstall 命令就并行安裝所有插件了,比 Vundle 快捷不少,關(guān)鍵是 vim-plug 只有單個(gè)文件,正好可以放在我 github 上的 vim 配置倉(cāng)庫(kù)中,每次需要更新 vim-plug 時(shí)只需要 :PlugUpgrade,即可自我更新。使用時(shí)建議給插件分組,同類別插件放一個(gè)組里,vimrc 里面只需要確定下啟用哪些組就行了。
拋棄 Vundle 切換到 vim-plug 以后,不僅插件安裝和更新快了一個(gè)數(shù)量級(jí),大量的插件我都配置成了延遲加載,Vim 啟動(dòng)速度比 Vundle 時(shí)候提高了不少。使用 Vundle 的時(shí)候一旦插件數(shù)量超過(guò)30個(gè),管理是一件很痛苦的事情,而用了 vim-plug 以后,50-60個(gè)插件都輕輕松松。
符號(hào)索引
現(xiàn)在有好多 ctags 的代替品,比如 gtags, etags 和 cquery。然而我并不排斥 ctags,因?yàn)樗С?50+ 種語(yǔ)言,沒有任何一個(gè)符號(hào)索引工具有它支持的語(yǔ)言多。同時(shí) Vim 和 ctags 集成的相當(dāng)好,用它依賴最少,大量基礎(chǔ)工作可以直接通過(guò) ctags 進(jìn)行,然而到現(xiàn)在為止,我就沒見過(guò)幾個(gè)人把 ctags 用對(duì)了的。
就連配置文件他們都沒寫對(duì),正確的 ctags 配置應(yīng)該是:
set tags=。/.tags;,.tags
這里解釋一下,首先我把 tag 文件的名字從“tags” 換成了 “.tags”,前面多加了一個(gè)點(diǎn),這樣即便放到項(xiàng)目中也不容易污染當(dāng)前項(xiàng)目的文件,刪除時(shí)也好刪除,gitignore 也好寫,默認(rèn)忽略點(diǎn)開頭的文件名即可。
前半部分 “。/.tags; ”代表在文件的所在目錄下(不是 “:pwd”返回的 Vim 當(dāng)前目錄)查找名字為 “.tags”的符號(hào)文件,后面一個(gè)分號(hào)代表查找不到的話向上遞歸到父目錄,直到找到 .tags 文件或者遞歸到了根目錄還沒找到,這樣對(duì)于復(fù)雜工程很友好,源代碼都是分布在不同子目錄中,而只需要在項(xiàng)目頂層目錄放一個(gè) .tags文件即可;逗號(hào)分隔的后半部分 .tags 是指同時(shí)在 Vim 的當(dāng)前目錄(“:pwd”命令返回的目錄,可以用 :cd 。.命令改變)下面查找 .tags 文件。
最后請(qǐng)更新你的 ctags,不要再使用老舊的 Exuberant Ctags,這貨停止更新快十年了,請(qǐng)使用最新的 Universal CTags 代替之,它在 Exuberant Ctags 的基礎(chǔ)上繼續(xù)更新迭代了近十年,如今任然活躍的維護(hù)著,功能更強(qiáng)大,語(yǔ)言支持更多。
(注意最新版 universal ctags 調(diào)用時(shí)需要加一個(gè) --output-format=e-ctags,輸出格式才和老的 exuberant ctags 兼容否則會(huì)有 windows 下路徑名等小問(wèn)題)。
自動(dòng)索引
過(guò)去寫幾行代碼又需要運(yùn)行一下 ctags 來(lái)生成索引,每次生成耗費(fèi)不少時(shí)間。如今 Vim 8 下面自動(dòng)異步生成 tags 的工具有很多,這里推薦最好的一個(gè):vim-gutentags,這個(gè)插件主要做兩件事情:
- 確定文件所屬的工程目錄,即文件當(dāng)前路徑向上遞歸查找是否有 `.git`, `.svn`, `.project` 等標(biāo)志性文件(可以自定義)來(lái)確定當(dāng)前文檔所屬的工程目錄。
- 檢測(cè)同一個(gè)工程下面的文件改動(dòng),能會(huì)自動(dòng)增量更新對(duì)應(yīng)工程的 `.tags` 文件。每次改了幾行不用全部重新生成,并且這個(gè)增量更新能夠保證 `.tags` 文件的符號(hào)排序,方便 Vim 中用二分查找快速搜索符號(hào)。
vim-gutentags 需要簡(jiǎn)單配置一下:
” gutentags 搜索工程目錄的標(biāo)志,碰到這些文件/目錄名就停止向上一級(jí)目錄遞歸let g:gutentags_project_root = [‘.root’, ‘.svn’, ‘.git’, ‘.hg’, ‘.project’]“ 所生成的數(shù)據(jù)文件的名稱let g:gutentags_ctags_tagfile = ‘.tags’” 將自動(dòng)生成的 tags 文件全部放入 ~/.cache/tags 目錄中,避免污染工程目錄let s:vim_tags = expand(‘~/.cache/tags’)let g:gutentags_cache_dir = s:vim_tags“ 配置 ctags 的參數(shù)let g:gutentags_ctags_extra_args = [‘--fields=+niazS’, ‘--extra=+q’]let g:gutentags_ctags_extra_args += [‘--c++-kinds=+px’]let g:gutentags_ctags_extra_args += [‘--c-kinds=+px’]” 檢測(cè) ~/.cache/tags 不存在就新建if !isdirectory(s:vim_tags) silent! call mkdir(s:vim_tags, ‘p’)endif
有了上面的設(shè)置,你平時(shí)基本感覺不到 tags 文件的生成過(guò)程了,只要文件修改過(guò),gutentags 都在后臺(tái)為你默默打點(diǎn)是否需要更新數(shù)據(jù)文件,你根本不用管,還會(huì)幫你:setlocal tags+=。.. 添加到局部 tags 搜索列表中。
為當(dāng)前文件添加上對(duì)應(yīng)的 tags 文件的路勁而不影響其他文件。得益于 Vim 8 的異步機(jī)制,你可以任意隨時(shí)使用 ctags 相關(guān)功能,并且數(shù)據(jù)庫(kù)都是最新的。需要注意的是,gutentags 需要靠上面定義的 project_root 里的標(biāo)志,判斷文件所在的工程,如果一個(gè)文件沒有托管在 .git/.svn 中,gutentags 找不到工程目錄的話,就不會(huì)為該野文件生成 tags,這也很合理。想要避免的話,你可以在你的野文件目錄中放一個(gè)名字為 .root 的空白文件,主動(dòng)告訴 gutentags 這里就是工程目錄。
最后啰嗦兩句,少用 CTRL-] 直接在當(dāng)前窗口里跳轉(zhuǎn)到定義,多使用 CTRL-W ] 用新窗口打開并查看光標(biāo)下符號(hào)的定義,或者 CTRL-W } 使用 preview 窗口預(yù)覽光標(biāo)下符號(hào)的定義。
我自己還寫過(guò)不少關(guān)于 ctags 的 vimscript,例如在最下面命令行顯示函數(shù)的原型而不用急著跳轉(zhuǎn),或者重復(fù)按 `ALT+;` 在 preview 窗口中輪流查看多個(gè)定義,不切走當(dāng)前窗口,不會(huì)出一個(gè)很長(zhǎng)的列表讓你選擇,有興趣可以刨我的 vim dotfiles。
編譯運(yùn)行
再 Vim 8 以前,編譯和運(yùn)行程序要么就讓 vim 傻等著結(jié)束,不能做其他事情,要么切到一個(gè)新的終端下面去單獨(dú)運(yùn)行編譯命令和執(zhí)行命令,要么開個(gè) tmux 左右切換。如今新版本的異步模式可以讓這個(gè)流程更加簡(jiǎn)化,這里我們使用 AsyncRun 插件,簡(jiǎn)單設(shè)置下:
Plug ‘skywind3000/asyncrun.vim’“ 自動(dòng)打開 quickfix window ,高度為 6let g:asyncrun_open = 6” 任務(wù)結(jié)束時(shí)候響鈴提醒let g:asyncrun_bell = 1“ 設(shè)置 F10 打開/關(guān)閉 Quickfix 窗口nnoremap 《F10》 :call asyncrun#quickfix_toggle(6)《cr》
該插件可以在后臺(tái)運(yùn)行 shell 命令,并且把結(jié)果輸出到 quickfix 窗口:
最簡(jiǎn)單的編譯單個(gè)文件,和 sublime 的默認(rèn) build system 差不多,我們定義 F9 為編譯單文件:
nnoremap 《silent》 《F9》 :AsyncRun gcc -Wall -O2 “$(VIM_FILEPATH)” -o “$(VIM_FILEDIR)/$(VIM_FILENOEXT)” 《cr》
其中 $(。..) 形式的宏在執(zhí)行時(shí)會(huì)被替換成實(shí)際的文件名或者文件目錄,這樣按 F9 就可以編譯當(dāng)前文件,同時(shí)按 F5 運(yùn)行:
nnoremap 《silent》 《F5》 :AsyncRun -raw -cwd=$(VIM_FILEDIR) “$(VIM_FILEDIR)/$(VIM_FILENOEXT)” 《cr》
用雙引號(hào)引起來(lái)避免文件名包含空格,“-cwd=$(VIM_FILEDIR)” 的意思時(shí)在文件文件的所在目錄運(yùn)行可執(zhí)行,后面可執(zhí)行使用了全路徑,避免 linux 下面當(dāng)前路徑加 “。/” 而 windows 不需要的跨平臺(tái)問(wèn)題。
參數(shù) `-raw` 表示輸出不用匹配錯(cuò)誤檢測(cè)模板 (errorformat) ,直接原始內(nèi)容輸出到 quickfix 窗口。這樣你可以一邊編輯一邊 F9 編譯,出錯(cuò)了可以在 quickfix 窗口中按回車直接跳轉(zhuǎn)到錯(cuò)誤的位置,編譯正確就接著執(zhí)行。
接下來(lái)是項(xiàng)目的編譯,不管你直接使用 make 還是 cmake,都是對(duì)一群文件做點(diǎn)什么,都需要定位到文件所屬項(xiàng)目的目錄,AsyncRun 識(shí)別當(dāng)前文件的項(xiàng)目目錄方式和 gutentags相同,從文件所在目錄向上遞歸,直到找到名為 “.git”, “.svn”, “.hg”或者 “.root”文件或者目錄,如果遞歸到根目錄還沒找到,那么文件所在目錄就被當(dāng)作項(xiàng)目目錄,你重新定義項(xiàng)目標(biāo)志:
let g:asyncrun_rootmarks = [‘.svn’, ‘.git’, ‘.root’, ‘_darcs’, ‘build.xml’]
然后在 AsyncRun 命令行中,用 “《root》” 或者 “$(VIM_ROOT)”來(lái)表示項(xiàng)目所在路徑,于是我們可以定義按 F7 編譯整個(gè)項(xiàng)目:
nnoremap 《silent》 《F7》 :AsyncRun -cwd=《root》 make 《cr》
那么如果你有一個(gè)項(xiàng)目不在 svn 也不在 git 中怎么查找 《root》 呢?很簡(jiǎn)單,放一個(gè)空的 .root 文件到你的項(xiàng)目目錄下就行了,前面配置過(guò),識(shí)別名為 .root 的文件。
繼續(xù)配置用 F8 運(yùn)行當(dāng)前項(xiàng)目:
nnoremap 《silent》 《F8》 :AsyncRun -cwd=《root》 -raw make run 《cr》
當(dāng)然,你的 makefile 中需要定義怎么 run ,接著按 F6 執(zhí)行測(cè)試:
nnoremap 《silent》 《F6》 :AsyncRun -cwd=《root》 -raw make test 《cr》
如果你使用了 cmake 的話,還可以照葫蘆畫瓢,定義 F4 為更新 Makefile 文件,如果不用 cmake 可以忽略:
nnoremap 《silent》 《F4》 :AsyncRun -cwd=《root》 cmake 。 《cr》
由于 C/C++ 標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)方式是發(fā)現(xiàn)在后臺(tái)運(yùn)行時(shí)會(huì)緩存標(biāo)準(zhǔn)輸出直到程序退出,你想實(shí)時(shí)看到 printf 輸出的話需要 fflush(stdout) 一下,或者程序開頭關(guān)閉緩存:“setbuf(stdout, NULL);” 即可。
同時(shí),如果你開發(fā) C++ 程序使用 std::cout 的話,后面直接加一個(gè) std::endl 就強(qiáng)制刷新緩存了,不需要弄其他。而如果你在 Windows 下使用 GVim 的話,可以彈出新的 cmd.exe 窗口來(lái)運(yùn)行剛才的程序:
nnoremap 《silent》 《F5》 :AsyncRun -cwd=$(VIM_FILEDIR) -mode=4 “$(VIM_FILEDIR)/$(VIM_FILENOEXT)” 《cr》nnoremap 《silent》 《F8》 :AsyncRun -cwd=《root》 -mode=4 make run 《cr》
在 Windows 下使用 -mode=4 選項(xiàng)可以跟 Visual Studio 執(zhí)行命令行工具一樣,彈出一個(gè)新的 cmd.exe窗口來(lái)運(yùn)行程序或者項(xiàng)目,于是我們有了下面的快捷鍵:
F4:使用 cmake 生成 Makefile
F5:?jiǎn)挝募哼\(yùn)行
F6:項(xiàng)目:測(cè)試
F7:項(xiàng)目:編譯
F8:項(xiàng)目:運(yùn)行
F9:?jiǎn)挝募壕幾g
F10:打開/關(guān)閉底部的 quickfix 窗口
恩,編譯和運(yùn)行基本和 NotePad++ / GEdit 的體驗(yàn)差不多了。如果你重度使用 cmake 的話,你還可以寫點(diǎn)小腳本,將 F4 和 F7 的功能合并,檢測(cè) CMakeLists.txt 文件改變的話先執(zhí)行 cmake 更新一下 Makefile,然后再執(zhí)行 make,否則直接執(zhí)行 make,這樣更自動(dòng)化些。
動(dòng)態(tài)檢查
代碼檢查是個(gè)好東西,讓你在編輯文字的同時(shí)就幫你把潛在錯(cuò)誤標(biāo)注出來(lái),不用等到編譯或者運(yùn)行了才發(fā)現(xiàn)。我很奇怪 2018 年了,為啥網(wǎng)上還在到處介紹老舊的 syntastic,但凡見到介紹這個(gè)插件的文章基本都可以不看了。老的 syntastic 基本沒法用,不能實(shí)時(shí)檢查,一保存文件就運(yùn)行檢查器并且等待半天,所以請(qǐng)用實(shí)時(shí) linting 工具 ALE:
大概長(zhǎng)這個(gè)樣子,隨著你不斷的編輯新代碼,有語(yǔ)法錯(cuò)誤的地方會(huì)實(shí)時(shí)幫你標(biāo)注出來(lái),側(cè)邊會(huì)標(biāo)注本行有錯(cuò),光標(biāo)移動(dòng)過(guò)去的時(shí)候下面會(huì)顯示錯(cuò)誤原因,而具體錯(cuò)誤的符號(hào)下面會(huì)有紅色波浪線提醒。Ale 支持多種語(yǔ)言的各種代碼分析器,就 C/C++ 而言,就支持:gcc, clang, cppcheck 以及 clang-format 等,需要另行安裝并放入 PATH下面,ALE能在你修改了文本后自動(dòng)調(diào)用這些 linter 來(lái)分析最新代碼,然后將各種 linter 的結(jié)果進(jìn)行匯總并顯示再界面上。
同樣,我們也需要簡(jiǎn)單配置一下:
let g:ale_linters_explicit = 1let g:ale_completion_delay = 500let g:ale_echo_delay = 20let g:ale_lint_delay = 500let g:ale_echo_msg_format = ‘[%linter%] %code: %%s’let g:ale_lint_on_text_changed = ‘normal’let g:ale_lint_on_insert_leave = 1let g:airline#extensions#ale#enabled = 1let g:ale_c_gcc_options = ‘-Wall -O2 -std=c99’let g:ale_cpp_gcc_options = ‘-Wall -O2 -std=c++14’let g:ale_c_cppcheck_options = ‘’let g:ale_cpp_cppcheck_options = ‘’
基本上就是定義了一下運(yùn)行規(guī)則,信息顯示格式以及幾個(gè) linter 的運(yùn)行參數(shù),其中 6,7 兩行比較重要,它規(guī)定了如果 normal 模式下文字改變以及離開 insert 模式的時(shí)候運(yùn)行 linter,這是相對(duì)保守的做法,如果沒有的話,會(huì)導(dǎo)致 YouCompleteMe 的補(bǔ)全對(duì)話框頻繁刷新。
記得設(shè)置一下各個(gè) linter 的參數(shù),忽略一些你覺得沒問(wèn)題的規(guī)則,不然錯(cuò)誤太多沒法看。默認(rèn)錯(cuò)誤和警告的風(fēng)格都太難看了,你需要修改一下,比如我使用 GVim,就重新定義了警告和錯(cuò)誤的樣式,去除默認(rèn)難看的紅色背景,代碼正文使用干凈的波浪下劃線表示:
let g:ale_sign_error = “\\ue009\\ue009”hi! clear SpellBadhi! clear SpellCaphi! clear SpellRarehi! SpellBad gui=undercurl guisp=redhi! SpellCap gui=undercurl guisp=bluehi! SpellRare gui=undercurl guisp=magenta
不同項(xiàng)目之間如果評(píng)測(cè)標(biāo)準(zhǔn)不一樣還可以具體單獨(dú)制定 linter 的參數(shù),具體見 ALE 幫助文檔了。我基本使用兩個(gè)檢查器:gcc 和 cppcheck,都可以在 ALE 中進(jìn)行詳細(xì)配置,前者主要檢查有無(wú)語(yǔ)法錯(cuò)誤,后者主要會(huì)給出一些編碼建議,和對(duì)危險(xiǎn)寫法的警告。
我之前用 syntastic 時(shí)就用了兩天就徹底刪除了,而開始用 ALE 后,一用上就停不下來(lái),頭兩天我還一度覺得它就是個(gè)可有可無(wú)的點(diǎn)綴,但是第三天它幫我找出兩個(gè)潛在的 bug 的時(shí)候,我開始覺得沒白安裝,比如:
即便你使用各類 C/C++ IDE,也只能給實(shí)時(shí)你標(biāo)注一些編譯錯(cuò)誤或者警告,而使用 ALE + cppcheck/gcc,連上面類似的潛在錯(cuò)誤都能幫你自動(dòng)找出來(lái),并且當(dāng)你光標(biāo)移動(dòng)過(guò)去時(shí)在最下面命令行提示你錯(cuò)誤原因。
用上一段時(shí)間以后,讓你寫 C/C++ 有一種安心和舒適的感覺。
修改比較
這是個(gè)小功能,在側(cè)邊欄顯示一個(gè)修改狀態(tài),對(duì)比當(dāng)前文本和 git/svn 倉(cāng)庫(kù)里的版本,在側(cè)邊欄顯示修改情況,以前 Vim 做不到實(shí)時(shí)顯示修改狀態(tài),如今推薦使用 vim-signify 來(lái)實(shí)時(shí)顯示修改狀態(tài),它比 gitgutter 強(qiáng),除了 git 外還支持 svn/mercurial/cvs 等十多種主流版本管理系統(tǒng)。
沒注意到它時(shí),你可能覺得它不存在,當(dāng)你有時(shí)真的看上兩眼時(shí),你會(huì)發(fā)現(xiàn)這個(gè)功能很貼心。最新版 signify 還有一個(gè)命令`:SignifyDiff`,可以左右分屏對(duì)比提交前后記錄,比你命令行 svn/git diff 半天直觀多了。并且對(duì)我這種同時(shí)工作在 subversion 和 git 環(huán)境下的情況契合的比較好。
Signify 和前面的 ALE 都會(huì)在側(cè)邊欄顯示一些標(biāo)記,默認(rèn)側(cè)邊欄會(huì)自動(dòng)隱藏,有內(nèi)容才會(huì)顯示,不喜歡側(cè)邊欄時(shí)有時(shí)無(wú)的行為可設(shè)置強(qiáng)制顯示側(cè)邊欄:“set signcolumn=yes” 。
文本對(duì)象
相信大家用 Vim 進(jìn)行編輯時(shí)都很喜歡文本對(duì)象這個(gè)概念,diw 刪除光標(biāo)所在單詞,ciw 改寫單詞,vip 選中段落等,ci“/ci( 改寫引號(hào)/括號(hào)中的內(nèi)容。而編寫 C/C++ 代碼時(shí)我推薦大家補(bǔ)充幾個(gè)十分有用的文本對(duì)象,可以使用 textobj-user 全家桶:
Plug ‘kana/vim-textobj-user’Plug ‘kana/vim-textobj-indent’Plug ‘kana/vim-textobj-syntax’Plug ‘kana/vim-textobj-function’, { ‘for’:[‘c’, ‘cpp’, ‘vim’, ‘java’] }Plug ‘sgur/vim-textobj-parameter’
它新定義的文本對(duì)象主要有:
i, 和 a, :參數(shù)對(duì)象,寫代碼一半在修改,現(xiàn)在可以用 di, 或 ci, 一次性刪除/改寫當(dāng)前參數(shù)
ii 和 ai :縮進(jìn)對(duì)象,同一個(gè)縮進(jìn)層次的代碼,可以用 vii 選中,dii / cii 刪除或改寫
if 和 af :函數(shù)對(duì)象,可以用 vif / dif / cif 來(lái)選中/刪除/改寫函數(shù)的內(nèi)容
最開始我不太想用額外的文本對(duì)象,一直在堅(jiān)持 Vim 固有的幾個(gè)默認(rèn)對(duì)象,生怕手練習(xí)慣了肌肉形成記憶到遠(yuǎn)端沒有環(huán)境的 vim 下形成依賴改不過(guò)來(lái),后來(lái)我慢慢發(fā)現(xiàn)挺有用的,比如改寫參數(shù),以前是比較麻煩的事情,這下流暢了很多,當(dāng)我發(fā)現(xiàn)自己編碼效率得到比較大的提升時(shí),才發(fā)現(xiàn)習(xí)慣依賴不重要,行云流水才是真重要。以前看到過(guò)無(wú)數(shù)次都選擇性忽略的東西,有時(shí)候試試可能會(huì)有新的發(fā)現(xiàn)。
編輯輔助
大家都知道 color 文件定義了眾多不同語(yǔ)法元素的色彩,還有一個(gè)關(guān)鍵因素就是語(yǔ)法文件本身能否識(shí)別并標(biāo)記得出眾多不同的內(nèi)容來(lái)?語(yǔ)法文件對(duì)某些東西沒標(biāo)注,你 color 文件確定了顏色也沒用。因此 Vim 下面寫 C/C++ 代碼,語(yǔ)法高亮準(zhǔn)確豐富的話能讓你編碼的心情好很多,這里推薦 vim-cpp-enhanced-highlight 插件,提供比 Vim 自帶語(yǔ)法文件更好的 C/C++ 語(yǔ)法標(biāo)注,支持 標(biāo)準(zhǔn) 11/14/17。
前面編譯運(yùn)行時(shí)需要頻繁的操作 quickfix 窗口,ale查錯(cuò)時(shí)也需要快速再錯(cuò)誤間跳轉(zhuǎn)(location list),就連文件比較也會(huì)用到快速跳轉(zhuǎn)到上/下一個(gè)差異處,unimpaired 插件幫你定義了一系列方括號(hào)開頭的快捷鍵,被稱為官方 Vim 中丟失的快捷鍵。
我們好些地方用到了 quickfix / location 窗口,你在 quickfix 中回車選中一條錯(cuò)誤的話,默認(rèn)會(huì)把你當(dāng)前窗口給切走,變成新文件,雖然按 CTRL+O 可以返回,但是如果不太喜歡這樣切走當(dāng)前文件的做法,可以設(shè)置 switchbuf,發(fā)現(xiàn)文件已在 Vim 中打開就跳過(guò)去,沒打開過(guò)就新建窗口/標(biāo)簽打開,具體見幫助。
Vim最爽的地方是把所有 ALT 鍵映射全部留給用戶了,盡量使用 Vim 的 ALT鍵映射,可以讓冗長(zhǎng)的快捷鍵縮短很多,請(qǐng)參考:《Vim和終端軟件中支持ALT映射》。
代碼補(bǔ)全
傳統(tǒng)的 Vim 代碼補(bǔ)全基本以 omni 系列補(bǔ)全和符號(hào)補(bǔ)全為主,omni 補(bǔ)全系統(tǒng)是 Vim 自帶的針對(duì)不同文件類型編寫不同的補(bǔ)全函數(shù)的基礎(chǔ)語(yǔ)義補(bǔ)全系統(tǒng),搭配 neocomplete 可以很方便的對(duì)所有補(bǔ)全結(jié)果(omni補(bǔ)全/符號(hào)補(bǔ)全/字典補(bǔ)全)進(jìn)行一個(gè)合成并且自動(dòng)彈出補(bǔ)全框,雖然趕不上 IDE 的補(bǔ)全,但是已經(jīng)比大部分編輯器補(bǔ)全好用很多了。然而傳統(tǒng) Vim 補(bǔ)全還是有兩個(gè)邁不過(guò)去的坎:語(yǔ)義補(bǔ)全太弱,其次是補(bǔ)全分析無(wú)法再后臺(tái)運(yùn)行,對(duì)大項(xiàng)目而言,某些復(fù)雜符號(hào)的補(bǔ)全會(huì)拖慢你的打字速度。
新一代的 Vim 補(bǔ)全系統(tǒng),YouCompleteMe 和 Deoplete,都支持異步補(bǔ)全和基于 clang 的語(yǔ)義補(bǔ)全,前者集成度高,后者擴(kuò)展方便。對(duì)于 C/C++ 的話,我推薦 YCM,因?yàn)?deoplete 的 clang 補(bǔ)全插件不夠穩(wěn)定,太吃內(nèi)存,并且反應(yīng)比較慢,它的代碼量和代碼質(zhì)量和 YCM完全不是一個(gè)量級(jí)的。所以 C/C++ 的補(bǔ)全的話,請(qǐng)直接使用 YCM,沒有之一,而使用 YCM的話,需要進(jìn)行一些簡(jiǎn)單的調(diào)教:
let g:ycm_add_preview_to_completeopt = 0let g:ycm_show_diagnostics_ui = 0let g:ycm_server_log_level = ‘info’let g:ycm_min_num_identifier_candidate_chars = 2let g:ycm_collect_identifiers_from_comments_and_strings = 1let g:ycm_complete_in_strings=1let g:ycm_key_invoke_completion = ‘《c-z》’set completeopt=menu,menuonenoremap 《c-z》 《NOP》let g:ycm_semantic_triggers = { \ ‘c,cpp,python,java,go,erlang,perl’: [‘re!\w{2}’], \ ‘cs,lua,javascript’: [‘re!\w{2}’], \ }
這樣可以輸入兩個(gè)字符就自動(dòng)彈出語(yǔ)義補(bǔ)全,不用等到輸入句號(hào) 。 或者 -》 才觸發(fā),同時(shí)關(guān)閉了預(yù)覽窗口和代碼診斷這些 YCM 花邊功能,保持清靜,對(duì)于原型預(yù)覽和診斷我們后面有更好的解決方法,YCM這兩項(xiàng)功能干擾太大。
上面這幾行配置具體每行的含義,可以見:《YouCompleteMe 中容易忽略的配置》。另外我在 Windows 下編譯了一個(gè)版本,你用 Windows 的話無(wú)需下載VS編譯,點(diǎn)擊 [這里]。我日常開發(fā)使用 YCM 輔助編寫 C/C++, Python 和 Go 代碼,基本能提供 IDE 級(jí)別的補(bǔ)全。
函數(shù)列表
不再建議使用 tagbar, 它會(huì)在你保存文件的時(shí)候以同步等待的方式運(yùn)行 ctags (即便你沒有打開 tagbar),導(dǎo)致vim操作變卡,特別是 windows下開了反病毒軟件掃描的話,有時(shí)候保存文件卡5-6秒。2018年了,我們有更好的選擇,比如使用
@Yggdroot
開發(fā)的 LeaderF 來(lái)顯示函數(shù)列表:
全異步顯示文件函數(shù)列表,不用的時(shí)候不會(huì)占用你任何屏幕空間,將 ALT+P 綁定到 `:LeaderfFunction!` 這個(gè)命令上,按 ALT+P 就彈出當(dāng)前文件的函數(shù)列表,然后可以進(jìn)行模糊匹配搜索,除了上下鍵移動(dòng)選擇外,各種vim的跳轉(zhuǎn)和搜索命令都可以始用,回車跳轉(zhuǎn)然后關(guān)閉函數(shù)列表,除此之外按 i 進(jìn)入模糊匹配,按TAB切換回列表選擇。
Leaderf 的函數(shù)功能屬于你想要它的時(shí)候它才會(huì)出來(lái),不想要它的時(shí)候不會(huì)給你搗亂。
文件切換
文件/buffer模糊匹配快速切換的方式,比你打開一個(gè)對(duì)話框選擇文件便捷不少,過(guò)去我們常用的 CtrlP 可以光榮下崗了,如今有更多速度更快,匹配更精準(zhǔn)以及完美支持后臺(tái)運(yùn)行方式的文件模糊匹配工具。我自己用的是上面提到的 LeaderF,除了提供函數(shù)列表外,還支持文件,MRU,Buffer名稱搜索,完美代替 CtrlP,使用時(shí)需要簡(jiǎn)單調(diào)教下:
let g:Lf_ShortcutF = ‘《c-p》’let g:Lf_ShortcutB = ‘《m-n》’noremap 《c-n》 :LeaderfMru《cr》noremap 《m-p》 :LeaderfFunction!《cr》noremap 《m-n》 :LeaderfBuffer《cr》noremap 《m-m》 :LeaderfTag《cr》let g:Lf_StlSeparator = { ‘left’: ‘’, ‘right’: ‘’, ‘font’: ‘’ }let g:Lf_RootMarkers = [‘.project’, ‘.root’, ‘.svn’, ‘.git’]let g:Lf_WorkingDirectoryMode = ‘Ac’let g:Lf_WindowHeight = 0.30let g:Lf_CacheDirectory = expand(‘~/.vim/cache’)let g:Lf_ShowRelativePath = 0let g:Lf_HideHelp = 1let g:Lf_StlColorscheme = ‘powerline’let g:Lf_PreviewResult = {‘Function’:0, ‘BufTag’:0}
這里定義了 CTRL+P 在當(dāng)前項(xiàng)目目錄打開文件搜索,CTRL+N 打開 MRU搜索,搜索你最近打開的文件,這兩項(xiàng)是我用的最頻繁的功能。接著 ALT+P 打開函數(shù)搜索,ALT+N 打開 Buffer 搜索:
LeaderF 是目前匹配效率最高的,高過(guò) CtrlP/Fzf 不少,敲更少的字母就能把文件找出來(lái),同時(shí)搜索很迅速,使用 Python 后臺(tái)線程進(jìn)行搜索匹配,還有一個(gè) C模塊可以加速匹配性能,需要手工編譯下。LeaderF在模糊匹配模式下按 TAB 可以切換到匹配結(jié)果窗口用光標(biāo)或者 Vim 搜索命令進(jìn)一步篩選,這是 CtrlP/Fzf 不具備的,更多方便的功能見它的官方文檔。
文件/MRU 模糊匹配對(duì)于熟悉的項(xiàng)目效率是最高的,但對(duì)于一個(gè)新的項(xiàng)目,通常我們都不知道它有些什么文件,那就談不上根據(jù)文件名匹配什么了,我們需要文件瀏覽功能。如果你喜歡把 Vim 偽裝成 NotePad++ 之類的,那你該繼續(xù)使用 NERDTree 進(jìn)行文件瀏覽,但你想按照 Vim 的方式來(lái),推薦閱讀這篇文章:
Oil and vinegar - split windows and project drawer
然后像我一樣開始使用 vim-dirvish,進(jìn)行一些配置,比如當(dāng)前文檔按“-”號(hào)就能不切窗口的情況下在當(dāng)前窗口直接返回當(dāng)前文檔所在的目錄,再按一次減號(hào)就返回上一級(jí)目錄,按回車進(jìn)入下一級(jí)目錄或者再當(dāng)前窗口打開光標(biāo)下的文件。進(jìn)一步映射 “《tab》7” , “《tab》8” 和 “《tab》9” 分別用于在新的 split, vsplit 和新標(biāo)簽打開當(dāng)前文件所在目錄,這樣從一個(gè)文件如手,很容易找到和該文件相關(guān)的其他項(xiàng)目文件。
最后一個(gè)是 C/C++ 的頭文件/源文件快速切換功能,有現(xiàn)成的插件做這事情,比如 a.vim,我自己沒用,因?yàn)檫@事情太簡(jiǎn)單,再我發(fā)現(xiàn) a.vim 前我就覺得需要這個(gè)功能,然后自己兩行 vim 腳本就搞定了。
參數(shù)提示
這個(gè)功能應(yīng)人而異,有人覺得不需要,有人覺得管用。寫 C/C++ 時(shí)函數(shù)忘了可以用上面的 YCM 補(bǔ)全,但很多時(shí)候是參數(shù)忘記了怎么辦?YCM的參數(shù)提示很蛋疼,要打開個(gè) Preview 窗口,實(shí)在是太影響我的視線了,我自己寫過(guò)一些參數(shù)提醒功能,可以在最下面的命令行顯示當(dāng)前函數(shù)的參數(shù),不過(guò)這是基于 tags 的,搭配前面的 gutentags,對(duì)其他語(yǔ)言很管用,但對(duì) C/C++ 我們還可以使用更好的 echodoc 插件:
它可以無(wú)縫的和前面的 YCM 搭配,用 libclang 給你生成參數(shù)提示,當(dāng)你用 YCM 的 tab 補(bǔ)全了一個(gè)函數(shù)名后,只要輸入左括號(hào),下面命令行就會(huì)里面顯示出該函數(shù)的參數(shù)信息,隨著光標(biāo)移動(dòng),下面還會(huì)高亮出來(lái)你正在處于哪個(gè)參數(shù)位置。
唯一需要設(shè)置的是使用 “set noshowmode”關(guān)閉模式提示,就是底部 ---INSERT--- 那個(gè),我們一般都用 airline / lightline 之類的顯示當(dāng)前模式了,所以默認(rèn)模式提示可以關(guān)閉,INSERT 模式下的命令行,完全留給 echodoc 顯示參數(shù)使用。
-
Linux
+關(guān)注
關(guān)注
87文章
11342瀏覽量
210222 -
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7614瀏覽量
137505 - C++
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論