如何在 Linux 下利用 Vim 搭建 C


2020年了,不要再看網上那些老舊的文章還在教你使用手工生成 tags 的,請使用自動代碼索引生成工具,比如 vim-gutentags,現在網上好像就沒有一篇能正確討論 Vim C/C++ 環境搭建的,都在談些十年前的東西,所以我寫了篇關於 Vim 8 和 C/C++ 相關插件的介紹:

假設你已經有一定 Vim 使用經驗,並且折騰過 Vim 配置,能夠相對舒適的在 Vim 中編寫其他代碼的時候,準備在 Vim 開始 C/C++ 項目開發,或者你已經用 Vim 編寫了幾年 C/C++ 代碼,想要更進一步,讓自己的工作更加順暢的話,本文就是為你準備的:


如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

需要視頻資料的加關注後臺私信“1”獲取

插件管理

為什麼把插件管理放在第一個來講呢?這是比較基本的一個東西,如今 Vim 下熟練開發的人,基本上手都有 20-50 個插件,遙想十年前,Vim裡常用的插件一隻手都數得過來。過去我一直使用老牌的 Vundle 來管理插件,但是隨著插件越來越多,更新越來越頻繁,Vundle 這種每次更新就要好幾分鐘的東西實在是不堪重負了。

在我逐步對 Vundle 失去耐心之後,我試用了 vim-plug ,用了兩天以後就再也回不去 Vundle了,它支持全異步的插件安裝,安裝50個插件只需要一分鐘不到的時間,這在 Vundle 下面根本不可想像的事情,插件更新也很快,不像原來每次更新都可以去喝杯茶去,最重要的是它支持插件延遲加載:

<code>" 定義插件,默認用法,和 Vundle 的語法差不多
Plug 'junegunn/vim-easy-align'
Plug 'skywind3000/quickmenu.vim'

" 延遲按需加載,使用到命令的時候再加載或者打開對應文件類型才加載

Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
Plug 'tpope/vim-fireplace', { 'for': 'clojure' }

" 確定插件倉庫中的分支或者 tag
Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }/<code>

定義好插件以後一個 :PlugInstall 命令就並行安裝所有插件了,比 Vundle 快捷不少,關鍵是 vim-plug 只有單個文件,正好可以放在我 github 上的 vim 配置倉庫中,每次需要更新 vim-plug 時只需要 :PlugUpgrade,即可自我更新。使用時建議給插件分組,同類別插件放一個組裡,vimrc 裡面只需要確定下啟用哪些組就行了。

拋棄 Vundle 切換到 vim-plug 以後,不僅插件安裝和更新快了一個數量級,大量的插件我都配置成了延遲加載,Vim 啟動速度比 Vundle 時候提高了不少。使用 Vundle 的時候一旦插件數量超過30個,管理是一件很痛苦的事情,而用了 vim-plug 以後,50-60個插件都輕輕鬆鬆。


符號索引

現在有好多 ctags 的代替品,比如 gtags, etags 和 cquery。然而我並不排斥 ctags,因為他支持 50+ 種語言,沒有任何一個符號索引工具有它支持的語言多。同時 Vim 和 ctags 集成的相當好,用它依賴最少,大量基礎工作可以直接通過 ctags 進行,然而到現在為止,我就沒見過幾個人把 ctags 用對了的。

就連配置文件他們都沒寫對,正確的 ctags 配置應該是:

<code>set tags=./.tags;,.tags/<code>

這裡解釋一下,首先我把 tag 文件的名字從“tags” 換成了 “.tags”,前面多加了一個點,這樣即便放到項目中也不容易汙染當前項目的文件,刪除時也好刪除,gitignore 也好寫,默認忽略點開頭的文件名即可。

前半部分 “./.tags; ”代表在文件的所在目錄下(不是 “:pwd”返回的 Vim 當前目錄)查找名字為 “.tags”的符號文件,後面一個分號代表查找不到的話向上遞歸到父目錄,直到找到 .tags 文件或者遞歸到了根目錄還沒找到,這樣對於複雜工程很友好,源代碼都是分佈在不同子目錄中,而只需要在項目頂層目錄放一個 .tags文件即可;逗號分隔的後半部分 .tags 是指同時在 Vim 的當前目錄(“:pwd”命令返回的目錄,可以用 :cd ..命令改變)下面查找 .tags 文件。

最後請更新你的 ctags,不要再使用老舊的 Exuberant Ctags,這貨停止更新快十年了,請使用最新的 Universal CTags 代替之,它在 Exuberant Ctags 的基礎上繼續更新迭代了近十年,如今任然活躍的維護著,功能更強大,語言支持更多。

(注意最新版 universal ctags 調用時需要加一個 --output-format=e-ctags,輸出格式才和老的 exuberant ctags 兼容否則會有 windows 下路徑名等小問題)。


自動索引

過去寫幾行代碼又需要運行一下 ctags 來生成索引,每次生成耗費不少時間。如今 Vim 8 下面自動異步生成 tags 的工具有很多,這裡推薦最好的一個:vim-gutentags,這個插件主要做兩件事情:

- 確定文件所屬的工程目錄,即文件當前路徑向上遞歸查找是否有 `.git`, `.svn`, `.project` 等標誌性文件(可以自定義)來確定當前文檔所屬的工程目錄。

- 檢測同一個工程下面的文件改動,能會自動增量更新對應工程的 `.tags` 文件。每次改了幾行不用全部重新生成,並且這個增量更新能夠保證 `.tags` 文件的符號排序,方便 Vim 中用二分查找快速搜索符號。

vim-gutentags 需要簡單配置一下:

<code>" gutentags 搜索工程目錄的標誌,碰到這些文件/目錄名就停止向上一級目錄遞歸
let g:gutentags_project_root = ['.root', '.svn', '.git', '.hg', '.project']

" 所生成的數據文件的名稱
let g:gutentags_ctags_tagfile = '.tags'

" 將自動生成的 tags 文件全部放入 ~/.cache/tags 目錄中,避免汙染工程目錄

let s:vim_tags = expand('~/.cache/tags')
let g:gutentags_cache_dir = s:vim_tags

" 配置 ctags 的參數
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']

" 檢測 ~/.cache/tags 不存在就新建
if !isdirectory(s:vim_tags)
silent! call mkdir(s:vim_tags, 'p')
endif/<code>

有了上面的設置,你平時基本感覺不到 tags 文件的生成過程了,只要文件修改過,gutentags 都在後臺為你默默打點是否需要更新數據文件,你根本不用管,還會幫你:setlocal tags+=... 添加到局部 tags 搜索列表中。

為當前文件添加上對應的 tags 文件的路勁而不影響其他文件。得益於 Vim 8 的異步機制,你可以任意隨時使用 ctags 相關功能,並且數據庫都是最新的。需要注意的是,gutentags 需要靠上面定義的 project_root 裡的標誌,判斷文件所在的工程,如果一個文件沒有託管在 .git/.svn 中,gutentags 找不到工程目錄的話,就不會為該野文件生成 tags,這也很合理。想要避免的話,你可以在你的野文件目錄中放一個名字為 .root 的空白文件,主動告訴 gutentags 這裡就是工程目錄。

最後囉嗦兩句,少用 CTRL-]

直接在當前窗口裡跳轉到定義,多使用 CTRL-W ] 用新窗口打開並查看光標下符號的定義,或者 CTRL-W } 使用 preview 窗口預覽光標下符號的定義。

我自己還寫過不少關於 ctags 的 vimscript,例如在最下面命令行顯示函數的原型而不用急著跳轉,或者重複按 `ALT+;` 在 preview 窗口中輪流查看多個定義,不切走當前窗口,不會出一個很長的列表讓你選擇,有興趣可以刨我的 vim dotfiles。


編譯運行

再 Vim 8 以前,編譯和運行程序要麼就讓 vim 傻等著結束,不能做其他事情,要麼切到一個新的終端下面去單獨運行編譯命令和執行命令,要麼開個 tmux 左右切換。如今新版本的異步模式可以讓這個流程更加簡化,這裡我們使用 AsyncRun 插件,簡單設置下:

<code>Plug 'skywind3000/asyncrun.vim'

" 自動打開 quickfix window ,高度為 6
let g:asyncrun_open = 6

" 任務結束時候響鈴提醒
let g:asyncrun_bell = 1


" 設置 F10 打開/關閉 Quickfix 窗口
nnoremap :call asyncrun#quickfix_toggle(6)/<code>

該插件可以在後臺運行 shell 命令,並且把結果輸出到 quickfix 窗口:

如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

最簡單的編譯單個文件,和 sublime 的默認 build system 差不多,我們定義 F9 為編譯單文件:

<code>nnoremap <silent>  :AsyncRun gcc -Wall -O2 "$(VIM_FILEPATH)" -o "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" /<silent>/<code>

其中 $(...) 形式的宏在執行時會被替換成實際的文件名或者文件目錄,這樣按 F9 就可以編譯當前文件,同時按 F5 運行:

<code>nnoremap <silent>  :AsyncRun -raw -cwd=$(VIM_FILEDIR) "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" /<silent>/<code>

用雙引號引起來避免文件名包含空格,“-cwd=$(VIM_FILEDIR)” 的意思時在文件文件的所在目錄運行可執行,後面可執行使用了全路徑,避免 linux 下面當前路徑加 “./” 而 windows 不需要的跨平臺問題。

參數 `-raw` 表示輸出不用匹配錯誤檢測模板 (errorformat) ,直接原始內容輸出到 quickfix 窗口。這樣你可以一邊編輯一邊 F9 編譯,出錯了可以在 quickfix 窗口中按回車直接跳轉到錯誤的位置,編譯正確就接著執行。

接下來是項目的編譯,不管你直接使用 make 還是 cmake,都是對一群文件做點什麼,都需要定位到文件所屬項目的目錄,AsyncRun 識別當前文件的項目目錄方式和 gutentags相同,從文件所在目錄向上遞歸,直到找到名為 “.git”, “.svn”, “.hg”或者 “.root”文件或者目錄,如果遞歸到根目錄還沒找到,那麼文件所在目錄就被當作項目目錄,你重新定義項目標誌:

<code>let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs', 'build.xml'] /<code>

然後在 AsyncRun 命令行中,用 “<root>” 或者 “$(VIM_ROOT)”來表示項目所在路徑,於是我們可以定義按 F7 編譯整個項目:/<root>

<code>nnoremap <silent>  :AsyncRun -cwd=<root> make /<root>/<silent>/<code>

那麼如果你有一個項目不在 svn 也不在 git 中怎麼查找 <root> 呢?很簡單,放一個空的 .root 文件到你的項目目錄下就行了,前面配置過,識別名為 .root 的文件。/<root>

繼續配置用 F8 運行當前項目:

<code>nnoremap <silent>  :AsyncRun -cwd=<root> -raw make run /<root>/<silent>/<code>

當然,你的 makefile 中需要定義怎麼 run ,接著按 F6 執行測試:

<code>nnoremap <silent>  :AsyncRun -cwd=<root> -raw make test /<root>/<silent>/<code>

如果你使用了 cmake 的話,還可以照葫蘆畫瓢,定義 F4 為更新 Makefile 文件,如果不用 cmake 可以忽略:

<code>nnoremap <silent>  :AsyncRun -cwd=<root> cmake . /<root>/<silent>/<code>

由於 C/C++ 標準庫的實現方式是發現在後臺運行時會緩存標準輸出直到程序退出,你想實時看到 printf 輸出的話需要 fflush(stdout) 一下,或者程序開頭關閉緩存:“setbuf(stdout, NULL);” 即可。

同時,如果你開發 C++ 程序使用 std::cout 的話,後面直接加一個 std::endl 就強制刷新緩存了,不需要弄其他。而如果你在 Windows 下使用 GVim 的話,可以彈出新的 cmd.exe 窗口來運行剛才的程序:

<code>nnoremap <silent>  :AsyncRun -cwd=$(VIM_FILEDIR) -mode=4 "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" 
nnoremap <silent> :AsyncRun -cwd=<root> -mode=4 make run /<root>/<silent>
/<silent>/<code>

在 Windows 下使用 -mode=4 選項可以跟 Visual Studio 執行命令行工具一樣,彈出一個新的 cmd.exe窗口來運行程序或者項目,於是我們有了下面的快捷鍵:

  • F4:使用 cmake 生成 Makefile
  • F5:單文件:運行
  • F6:項目:測試
  • F7:項目:編譯
  • F8:項目:運行
  • F9:單文件:編譯
  • F10:打開/關閉底部的 quickfix 窗口

恩,編譯和運行基本和 NotePad++ / GEdit 的體驗差不多了。如果你重度使用 cmake 的話,你還可以寫點小腳本,將 F4 和 F7 的功能合併,檢測 CMakeLists.txt 文件改變的話先執行 cmake 更新一下 Makefile,然後再執行 make,否則直接執行 make,這樣更自動化些。


動態檢查

代碼檢查是個好東西,讓你在編輯文字的同時就幫你把潛在錯誤標註出來,不用等到編譯或者運行了才發現。我很奇怪 2018 年了,為啥網上還在到處介紹老舊的 syntastic,但凡見到介紹這個插件的文章基本都可以不看了。老的 syntastic 基本沒法用,不能實時檢查,一保存文件就運行檢查器並且等待半天,所以請用實時 linting 工具 ALE:

如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

大概長這個樣子,隨著你不斷的編輯新代碼,有語法錯誤的地方會實時幫你標註出來,側邊會標註本行有錯,光標移動過去的時候下面會顯示錯誤原因,而具體錯誤的符號下面會有紅色波浪線提醒。Ale 支持多種語言的各種代碼分析器,就 C/C++ 而言,就支持:gcc, clang, cppcheck 以及 clang-format 等,需要另行安裝並放入 PATH下面,ALE能在你修改了文本後自動調用這些 linter 來分析最新代碼,然後將各種 linter 的結果進行彙總並顯示再界面上。

同樣,我們也需要簡單配置一下:

<code>let g:ale_linters_explicit = 1
let g:ale_completion_delay = 500
let g:ale_echo_delay = 20
let g:ale_lint_delay = 500
let g:ale_echo_msg_format = '[%linter%] %code: %%s'
let g:ale_lint_on_text_changed = 'normal'
let g:ale_lint_on_insert_leave = 1
let g:airline#extensions#ale#enabled = 1

let 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 = ''/<code>

基本上就是定義了一下運行規則,信息顯示格式以及幾個 linter 的運行參數,其中 6,7 兩行比較重要,它規定了如果 normal 模式下文字改變以及離開 insert 模式的時候運行 linter,這是相對保守的做法,如果沒有的話,會導致 YouCompleteMe 的補全對話框頻繁刷新。

記得設置一下各個 linter 的參數,忽略一些你覺得沒問題的規則,不然錯誤太多沒法看。默認錯誤和警告的風格都太難看了,你需要修改一下,比如我使用 GVim,就重新定義了警告和錯誤的樣式,去除默認難看的紅色背景,代碼正文使用乾淨的波浪下劃線表示:

<code>let g:ale_sign_error = "\\\\ue009\\\\ue009"
hi! clear SpellBad
hi! clear SpellCap
hi! clear SpellRare
hi! SpellBad gui=undercurl guisp=red
hi! SpellCap gui=undercurl guisp=blue
hi! SpellRare gui=undercurl guisp=magenta/<code>

不同項目之間如果評測標準不一樣還可以具體單獨制定 linter 的參數,具體見 ALE 幫助文檔了。我基本使用兩個檢查器:gcc 和 cppcheck,都可以在 ALE 中進行詳細配置,前者主要檢查有無語法錯誤,後者主要會給出一些編碼建議,和對危險寫法的警告。

我之前用 syntastic 時就用了兩天就徹底刪除了,而開始用 ALE 後,一用上就停不下來,頭兩天我還一度覺得它就是個可有可無的點綴,但是第三天它幫我找出兩個潛在的 bug 的時候,我開始覺得沒白安裝,比如:

如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

即便你使用各類 C/C++ IDE,也只能給實時你標註一些編譯錯誤或者警告,而使用 ALE + cppcheck/gcc,連上面類似的潛在錯誤都能幫你自動找出來,並且當你光標移動過去時在最下面命令行提示你錯誤原因。

用上一段時間以後,讓你寫 C/C++ 有一種安心和舒適的感覺。


修改比較

這是個小功能,在側邊欄顯示一個修改狀態,對比當前文本和 git/svn 倉庫裡的版本,在側邊欄顯示修改情況,以前 Vim 做不到實時顯示修改狀態,如今推薦使用 vim-signify 來實時顯示修改狀態,它比 gitgutter 強,除了 git 外還支持 svn/mercurial/cvs 等十多種主流版本管理系統。

沒注意到它時,你可能覺得它不存在,當你有時真的看上兩眼時,你會發現這個功能很貼心。最新版 signify 還有一個命令`:SignifyDiff`,可以左右分屏對比提交前後記錄,比你命令行 svn/git diff 半天直觀多了。並且對我這種同時工作在 subversion 和 git 環境下的情況契合的比較好。

Signify 和前面的 ALE 都會在側邊欄顯示一些標記,默認側邊欄會自動隱藏,有內容才會顯示,不喜歡側邊欄時有時無的行為可設置強制顯示側邊欄:“set signcolumn=yes” 。


文本對象

相信大家用 Vim 進行編輯時都很喜歡文本對象這個概念,diw 刪除光標所在單詞,ciw 改寫單詞,vip 選中段落等,ci"/ci( 改寫引號/括號中的內容。而編寫 C/C++ 代碼時我推薦大家補充幾個十分有用的文本對象,可以使用 textobj-user 全家桶:

<code>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'/<code>

它新定義的文本對象主要有:

  • i, 和 a, :參數對象,寫代碼一半在修改,現在可以用 di, 或 ci, 一次性刪除/改寫當前參數
  • ii 和 ai :縮進對象,同一個縮進層次的代碼,可以用 vii 選中,dii / cii 刪除或改寫
  • if 和 af :函數對象,可以用 vif / dif / cif 來選中/刪除/改寫函數的內容

最開始我不太想用額外的文本對象,一直在堅持 Vim 固有的幾個默認對象,生怕手練習慣了肌肉形成記憶到遠端沒有環境的 vim 下形成依賴改不過來,後來我慢慢發現挺有用的,比如改寫參數,以前是比較麻煩的事情,這下流暢了很多,當我發現自己編碼效率得到比較大的提升時,才發現習慣依賴不重要,行雲流水才是真重要。以前看到過無數次都選擇性忽略的東西,有時候試試可能會有新的發現。


大家都知道 color 文件定義了眾多不同語法元素的色彩,還有一個關鍵因素就是語法文件本身能否識別並標記得出眾多不同的內容來?語法文件對某些東西沒標註,你 color 文件確定了顏色也沒用。因此 Vim 下面寫 C/C++ 代碼,語法高亮準確豐富的話能讓你編碼的心情好很多,這裡推薦 vim-cpp-enhanced-highlight 插件,提供比 Vim 自帶語法文件更好的 C/C++ 語法標註,支持 標準 11/14/17。

前面編譯運行時需要頻繁的操作 quickfix 窗口,ale查錯時也需要快速再錯誤間跳轉(location list),就連文件比較也會用到快速跳轉到上/下一個差異處,unimpaired 插件幫你定義了一系列方括號開頭的快捷鍵,被稱為官方 Vim 中丟失的快捷鍵。

我們好些地方用到了 quickfix / location 窗口,你在 quickfix 中回車選中一條錯誤的話,默認會把你當前窗口給切走,變成新文件,雖然按 CTRL+O 可以返回,但是如果不太喜歡這樣切走當前文件的做法,可以設置 switchbuf,發現文件已在 Vim 中打開就跳過去,沒打開過就新建窗口/標籤打開,具體見幫助。

Vim最爽的地方是把所有 ALT 鍵映射全部留給用戶了,儘量使用 Vim 的 ALT鍵映射,可以讓冗長的快捷鍵縮短很多,請參考:《Vim和終端軟件中支持ALT映射》。


代碼補全

傳統的 Vim 代碼補全基本以 omni 系列補全和符號補全為主,omni 補全系統是 Vim 自帶的針對不同文件類型編寫不同的補全函數的基礎語義補全系統,搭配 neocomplete 可以很方便的對所有補全結果(omni補全/符號補全/字典補全)進行一個合成並且自動彈出補全框,雖然趕不上 IDE 的補全,但是已經比大部分編輯器補全好用很多了。然而傳統 Vim 補全還是有兩個邁不過去的坎:語義補全太弱,其次是補全分析無法再後臺運行,對大項目而言,某些複雜符號的補全會拖慢你的打字速度。

新一代的 Vim 補全系統,YouCompleteMe 和 Deoplete,都支持異步補全和基於 clang 的語義補全,前者集成度高,後者擴展方便。對於 C/C++ 的話,我推薦 YCM,因為 deoplete 的 clang 補全插件不夠穩定,太吃內存,並且反應比較慢,它的代碼量和代碼質量和 YCM完全不是一個量級的。所以 C/C++ 的補全的話,請直接使用 YCM,沒有之一,而使用 YCM的話,需要進行一些簡單的調教:

<code>let g:ycm_add_preview_to_completeopt = 0
let g:ycm_show_diagnostics_ui = 0
let g:ycm_server_log_level = 'info'
let g:ycm_min_num_identifier_candidate_chars = 2
let g:ycm_collect_identifiers_from_comments_and_strings = 1

let g:ycm_complete_in_strings=1
let g:ycm_key_invoke_completion = ''
set completeopt=menu,menuone

noremap

let g:ycm_semantic_triggers = {
\\ 'c,cpp,python,java,go,erlang,perl': ['re!\\w{2}'],
\\ 'cs,lua,javascript': ['re!\\w{2}'],
\\ }
/<code>

這樣可以輸入兩個字符就自動彈出語義補全,不用等到輸入句號 . 或者 -> 才觸發,同時關閉了預覽窗口和代碼診斷這些 YCM 花邊功能,保持清靜,對於原型預覽和診斷我們後面有更好的解決方法,YCM這兩項功能干擾太大。

上面這幾行配置具體每行的含義,可以見:《YouCompleteMe 中容易忽略的配置》。另外我在 Windows 下編譯了一個版本,你用 Windows 的話無需下載VS編譯,點擊 [這裡]。我日常開發使用 YCM 輔助編寫 C/C++, Python 和 Go 代碼,基本能提供 IDE 級別的補全。


函數列表

不再建議使用 tagbar, 它會在你保存文件的時候以同步等待的方式運行 ctags (即便你沒有打開 tagbar),導致vim操作變卡,特別是 windows下開了反病毒軟件掃描的話,有時候保存文件卡5-6秒。2018年了,我們有更好的選擇,比如使用

@Yggdroot

開發的 LeaderF 來顯示函數列表:


如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

全異步顯示文件函數列表,不用的時候不會佔用你任何屏幕空間,將 ALT+P 綁定到 `:LeaderfFunction!` 這個命令上,按 ALT+P 就彈出當前文件的函數列表,然後可以進行模糊匹配搜索,除了上下鍵移動選擇外,各種vim的跳轉和搜索命令都可以始用,回車跳轉然後關閉函數列表,除此之外按 i 進入模糊匹配,按TAB切換回列表選擇。

Leaderf 的函數功能屬於你想要它的時候它才會出來,不想要它的時候不會給你搗亂。


文件切換

文件/buffer模糊匹配快速切換的方式,比你打開一個對話框選擇文件便捷不少,過去我們常用的 CtrlP 可以光榮下崗了,如今有更多速度更快,匹配更精準以及完美支持後臺運行方式的文件模糊匹配工具。我自己用的是上面提到的 LeaderF,除了提供函數列表外,還支持文件,MRU,Buffer名稱搜索,完美代替 CtrlP,使用時需要簡單調教下:

<code>let g:Lf_ShortcutF = ''
let g:Lf_ShortcutB = ''
noremap :LeaderfMru
noremap :LeaderfFunction!
noremap :LeaderfBuffer
noremap :LeaderfTag
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.30

let g:Lf_CacheDirectory = expand('~/.vim/cache')
let g:Lf_ShowRelativePath = 0
let g:Lf_HideHelp = 1
let g:Lf_StlColorscheme = 'powerline'
let g:Lf_PreviewResult = {'Function':0, 'BufTag':0}
/<code>

這裡定義了 CTRL+P 在當前項目目錄打開文件搜索,CTRL+N 打開 MRU搜索,搜索你最近打開的文件,這兩項是我用的最頻繁的功能。接著 ALT+P 打開函數搜索,ALT+N 打開 Buffer 搜索:

如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

LeaderF 是目前匹配效率最高的,高過 CtrlP/Fzf 不少,敲更少的字母就能把文件找出來,同時搜索很迅速,使用 Python 後臺線程進行搜索匹配,還有一個 C模塊可以加速匹配性能,需要手工編譯下。LeaderF在模糊匹配模式下按 TAB 可以切換到匹配結果窗口用光標或者 Vim 搜索命令進一步篩選,這是 CtrlP/Fzf 不具備的,更多方便的功能見它的官方文檔。

文件/MRU 模糊匹配對於熟悉的項目效率是最高的,但對於一個新的項目,通常我們都不知道它有些什麼文件,那就談不上根據文件名匹配什麼了,我們需要文件瀏覽功能。如果你喜歡把 Vim 偽裝成 NotePad++ 之類的,那你該繼續使用 NERDTree 進行文件瀏覽,但你想按照 Vim 的方式來,推薦閱讀這篇文章:

Oil and vinegar - split windows and project drawer

然後像我一樣開始使用 vim-dirvish,進行一些配置,比如當前文檔按“-”號就能不切窗口的情況下在當前窗口直接返回當前文檔所在的目錄,再按一次減號就返回上一級目錄,按回車進入下一級目錄或者再當前窗口打開光標下的文件。進一步映射 “7” , “8” 和 “9” 分別用於在新的 split, vsplit 和新標籤打開當前文件所在目錄,這樣從一個文件如手,很容易找到和該文件相關的其他項目文件。

最後一個是 C/C++ 的頭文件/源文件快速切換功能,有現成的插件做這事情,比如 a.vim,我自己沒用,因為這事情太簡單,再我發現 a.vim 前我就覺得需要這個功能,然後自己兩行 vim 腳本就搞定了。


如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

需要視頻資料的加關注後臺私信“1”獲取

參數提示

這個功能應人而異,有人覺得不需要,有人覺得管用。寫 C/C++ 時函數忘了可以用上面的 YCM 補全,但很多時候是參數忘記了怎麼辦?YCM的參數提示很蛋疼,要打開個 Preview 窗口,實在是太影響我的視線了,我自己寫過一些參數提醒功能,可以在最下面的命令行顯示當前函數的參數,不過這是基於 tags 的,搭配前面的 gutentags,對其他語言很管用,但對 C/C++ 我們還可以使用更好的 echodoc 插件:

如何在 Linux 下利用 Vim 搭建 C/C++ 開發環境?

它可以無縫的和前面的 YCM 搭配,用 libclang 給你生成參數提示,當你用 YCM 的 tab 補全了一個函數名後,只要輸入左括號,下面命令行就會里面顯示出該函數的參數信息,隨著光標移動,下面還會高亮出來你正在處於哪個參數位置。

唯一需要設置的是使用 “set noshowmode”關閉模式提示,就是底部 ---INSERT--- 那個,我們一般都用 airline / lightline 之類的顯示當前模式了,所以默認模式提示可以關閉,INSERT 模式下的命令行,完全留給 echodoc 顯示參數使用。


分享到:


相關文章: