Cycoe@Home

Vim – 一款讓我相見恨晚的編輯器

1 Vim 簡介

Vim is a greatly improved version of the good old UNIX editor Vi. Many new features have been added: multi-level undo, syntax highlighting, command line history, on-line help, spell checking, filename completion, block operations, script language, etc. There is also a Graphical User Interface (GUI) available.

以上關於 Vim 的描述來自 Vim 的官方 GitHub 倉庫。 Vim是一款古老且強大的編輯器,從其誕生之日起到現在已經有二十多年的歷史(比我的年 齡都要大 orz)。很多人聽說它的大名一般是因爲它與 Emacs 並稱編輯器之神與神之編輯器。爲 什麼會有這樣的名號?除了它們的功能極其強大之外,還因爲它們極其難以上手。以下圖片 爲網友戲稱的常用編輯器的學習曲線。

learning_curve.jpg
Figure 1: 常见编辑器的学习曲线

即便如此,Vim 和 Emacs 仍流行到了現在, 以至於後來經常能在各處看到的 Vimer(Vim 黨)和 Emacser(Emacs 黨)的編輯器之爭, 雖然有的時候可能只是類似於 "php 是世界上最好的語言" 之類的梗,但足以看出 Vim 確 實非常強大,並且用上了就再也回不去了。

2 安裝 Vim

Vim 在所有的 Linux 發行版上都易於獲得,只需要使用對應的包管理器進行安裝即可, Windows 上也有編譯好的二進制包,但實際在安裝過程中仍會面臨幾個選擇。

以 Archlinux 爲例,Archlinux 的官方倉庫中包含的 Vim 在編譯過程中並沒有編譯進 python、lua 等支持,導致很多插件無法使用,因此爲了能使用上 Vim 的完全體,可以選 擇 Gvim。Gvim 是基於 Vim 的一個帶有圖形界面的版本,並在編譯過程中包含進了所有可 用組件。如果選擇 Vim 分支,強烈建議使用 Gvim,並且使用 8.0 之後的版本。下載鏈接 在此處

另一個可供選擇的版本是 Neovim,Neovim 是 Vim 的一個 fork 版本,比 Vim 具有更多的 特性(Neovim 先首先支持的異步架構和內置 terminal,隨後 Vim 也提供了支持),目前 開發活躍,推薦進行嘗試。下載鏈接在 此處

以上的所有版本在下文中都將統一稱爲 'Vim'(雖然事實上 Vim 與 Neovim 並不是同一個 項目,但在配置和使用方法上是類似的)。無論安裝哪個版本,安裝完成後從應用啓動菜單 或命令行應該就能成功啓動。恭喜你!此時已經完成了安裝 Vim 的第一步,現在你看到的 是一個黑底白字的字符界面。不過彆着急,接下來我們會一步一步將 Vim 配置成稱手的編 輯器。

3 退出 Vim - 使用 Vim 的第一大難題?

雖然客觀上講,退出 Vim 相較於 Vim 其他功能要容易的多,但是確實是第一次使用 Vim 會遇到的一個問題。相對於其他的編輯器,Vim 沒有直觀的退出方式,以至於在 Stack Overflow 上有個專門的問題 (見 此處)解答如何退出 Vim。甚至有了如下的梗:

exit_vim.jpg
Figure 2: 如何退出 Vim

看來確實有必要對 Vim 的退出方式做個總結,之列出平常最常用的幾種退出方式

  • :q 正常退出
  • :q! 強制退出不保存
  • :wq 保存並退出
  • :x 保存並退出,與 :wq 不同之處在於如果文件未做修改則不更新時間戳
  • 強制關機

4 使用 Vim 前先看幫助文檔

安裝好 Vim 後,你可能想要馬上嘗試輸入。但我強烈建議先閱讀軟件自帶的幫助文檔,一 款優秀的軟件都會帶有一個詳細的用戶手冊以及一個簡要的使用說明,這有助於降低你在接 觸新軟件時的痛苦感。你可以在 Vim 中隨時通過輸入 :help<CR> ( 代表回車)來呼出 幫助文檔。

5 配置 Vim - 從 .vimrc 開始

由於 Vim 的流行,GitHub 上有各種各樣現成的 'vimrc'(Vim的配置文件)可供使用。對 於不想自己折騰配置的用戶來說,從 GitHub 上下載安裝其他用戶已經編輯好的 'vimrc' 是最好的選擇。甚至還有像 SpaceVim 這樣 的模塊化 Vim 配置可以一鍵安裝使用,能大大減少配置所需的時間。

另一些用戶可能想要繼續對 Vim 進行自定義配置,那麼就需要對 'vimrc' 進行修改。在 Linux 系統中,Gvim 與 Vim 的配置文件默認存放在 $HOME/.vimrc (Neovim 存放在 $HOME/.config/nvim/init.vim ),Windows 下存放在 $VIM/vimrc

你可以選擇自己新建一個文件從頭開始。不過還是建議拷貝官方自帶的配置示例,一方面可 以省去一部分對常規配置的修改時間,另一方面,官方示例中對各配置語句對應有詳細的註 釋。或者也可以選擇從 GitHub 上尋找一個適合你的 'vimrc',或者參考 我的配置

5.1 常規配置

在使用 Vim 之前,我們需要對其進行一些常規配置,來使它看起來更像一般的編輯器(以 下步驟默認了你已經學會 Vim 的基本操作,包括移動光標、輸入、模式切換等)。

首先,通過輸入 :e $HOME/.vimrc<CR> 打開配置文件( $HOME/.vimrc 替換爲你平臺 對應的配置文件路徑)。這時,如果此前你已經拷貝了 Vim 的示例配置文件,或者是從網 上下載安裝了其他人的配置,那麼你打開的文件裏應該就有那些內容,如果此前還未做過配 置,那麼打開的就是一個空文件。

接下來可以選擇下面的配置中你需要的部分拷貝進你的 'vimrc'

" about vim setup
set nocompatible               " disable compatible for vi
set shell=/bin/bash            " setup default shell
set shortmess=at               " 啓動時隱去援助提示
set mouse=a                    " active mouse control
set clipboard+=unnamed         " support system clipboard
let mapleader=';'              " remap <leader>
" display setup
set relativenumber             " display relative row number
set ruler                      " 顯示標尺,就是在右下角顯示光標位置
set spell spelllang=en_us,cjk  " Spell checking for English, escape Chinese
set langmenu=zh_CN.UTF-8
set helplang=cn                " 設置幫助文檔爲中文,需要 'yianwillis/vimcdoc'
syntax enable                  " syntax highlight enable
syntax on                      " syntax highlight on
set background=dark            " setup background color
colorscheme gruvbox            " setup color scheme
set wildmenu                   " select the candidates in interactive way
set backspace=indent,eol,start " more powerful backspacing
set cursorcolumn               " 淺色高亮當前列
set cursorline                 " 淺色高亮當前行
set novisualbell               " 關閉視覺響鈴
set laststatus=2               " 啓動顯示狀態行
set showcmd                    " show the command executed by shortcuts
set noshowmode                 " don't show the mode status, avoid override argvs hint
set hlsearch                   " highlight for searched results
set incsearch                  " highlight for searching results
set showmatch                  " show the match of brackets and Quotation mark
set ignorecase                 " ignore case when searching and command completion
set gdefault                   " default substitute in the whole line

其中,最重要的一條設置就是 set nocompatible ,它的作用是關閉對 Vi 的兼容性。Vi 是一款更加古老的編輯器,可視作是 Vim 的前身。如果保留對 Vi 的兼容性也就意味着你 放棄了很多 Vim 獨有的特性。 set shell=/bin/bash 顯式地聲明瞭 Vim 在運行腳本時 調用的解釋器,如果是 Windows 用戶則需要去掉這行。

此時保存配置文件並通過 :so \$HOME/.vimrc<CR> 重載配置後應該就能看到改變了。但 此時你可能會注意到幾條錯誤信息,不要緊張,這是因爲我們在配置中引用了一些插件功能, 但事實上我們現在還未安裝插件,安裝插件後你會看到錯誤就消失了。

5.2 安裝插件管理器 - 管理插件的插件

Vim 有非常多的插件管理器可以使用,除去功能上的細微差異,我們可以將所有插件管理器 分爲異步型和同步型。可以簡單地理解爲異步型可以同時下載安裝多個插件,而同步型一次 只能處理一個。在 Vim 7.4 版本出來之前,插件管理器都是同步型的,包括大名鼎鼎的 Vundle。

在我剛開始使用 Vim 的時候,Vim 和 Neovim 都已經支持異步架構,但由於當時查的是中 文資料,很多都是非常老的文章。基本上所有文章都推薦使用 Vundle 作爲插件管理器,事 實上在你用過 vim-plug 之類的異步插件管理器後,你會無法再忍受 Vundle 的速度。這告 訴我們技術型的文章還是要去 Google…

不會有人再去使用 Vim 7.4 之前的版本了,所以這裏我推薦使用 vim-plug 作爲插件管理器。vim-plug 的優 點是異步下載安裝、插件更新方便、支持插件按需加載,並且只包含一個文件。它的 GitHub 倉庫上有詳細的安裝教程。

5.3 安裝插件 - 本體不夠再打 mod

雖然 Vim 並不是擴展性最好的編輯器,但它仍支持成千上萬個插件。尋找插件最好的途徑 是 GitHubVim Scripts。以下是我使用的插件列表:

let g:python3_host_prog = "/usr/bin/python3"
call plug#begin('~/.vim/plugged')
Plug 'tomasr/molokai'                                             " molokai color scheme
Plug 'morhetz/gruvbox'                                            " gruvbox colorscheme theme
Plug 'mhinz/vim-startify'                                         " an awesome startup screen
Plug 'vim-scripts/fcitx.vim'                                      " auto switch fcitx status
Plug 'manu-mannattil/vim-sdcv'                                    " a simple dictionary for vim
Plug 'vim-airline/vim-airline'                                    " add status line
Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }            " directory tree
Plug 'majutsushi/tagbar', { 'on': 'TagbarToggle' }                " Class structure viewer, need ctags installed
Plug 'mbbill/undotree', { 'on': 'UndotreeToggle' }                " undo tree manager
Plug 'tpope/vim-fugitive'                                         " git support
Plug 'junegunn/gv.vim'                                            " view git commits in Vim
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } " fuzzy file searcher
Plug 'junegunn/fzf.vim'                                           " provide many other search functions
Plug 'tpope/vim-commentary'                                       " a better comment tool
Plug 'vim-scripts/EasyMotion'                                     " enhance default motions
Plug 'Yggdroot/indentLine'                                        " indent line indicator
Plug 'jiangmiao/auto-pairs'                                       " auto add pairs
Plug 'junegunn/rainbow_parentheses.vim'                           " a much simpler-using fork of above
Plug 'junegunn/limelight.vim'                                     " dim others
Plug 'junegunn/goyo.vim'                                          " provide a dim writing environment
Plug 'w0rp/ale'                                                   " syntax error check
Plug 'Valloric/YouCompleteMe',                                    " auto complete
            \{ 'do': './install.py --clang-completer' }
Plug 'SirVer/ultisnips'                                           " auto complete string
Plug 'honza/vim-snippets'                                         " complete rules for ultisnips
Plug 'junegunn/vim-easy-align'                                    " align tool
Plug 'godlygeek/tabular'                                          " advanced align tool
Plug 'gabrielelana/vim-markdown', { 'for': 'markdown' }           " markdown plugin
Plug 'joker1007/vim-markdown-quote-syntax', { 'for': 'markdown' } " syntax highlight for quoted code
Plug 'iamcco/mathjax-support-for-mkdp', { 'for': 'markdown' }     " mathjax support for markdown preview
Plug 'iamcco/markdown-preview.vim', { 'for': 'markdown' }         " markdown preview support
Plug 'vim-scripts/indentpython.vim', { 'for': 'python' }          " auto indent for python
Plug 'nvie/vim-flake8', { 'for': 'python' }                       " flake8 style code check, need install flake8
" 這兩個插件被 neoformat 替代了
" Plug 'tell-k/vim-autopep8', { 'for': 'python' }                 " a python code format tool wrapper for autopep8
" Plug 'fisadev/vim-isort', { 'for': 'python' }                   " imports sorter for python
Plug 'vim-latex/vim-latex', { 'for': 'tex' }                      " latex edit environment
Plug 'skywind3000/asyncrun.vim'                                   " AsyncRun plugin
Plug 'hotoo/pangu.vim'                                            " Chinese text format
Plug 'vimwiki/vimwiki'                                            " write wiki in vim
Plug 'yianwillis/vimcdoc'                                         " a Chinese help handbook for Vim
Plug 'sbdchd/neoformat'                                           " a collection of format tools
Plug 'ludovicchabant/vim-gutentags'                               " generate tags for project
Plug 'mhinz/vim-signify'                                          " show file modifying status for git
Plug 'Shougo/echodoc.vim'                                         " show argvs hint on the command line
"======================"
"  text object 全家桶  "
"======================"
Plug 'kana/vim-textobj-user'                                            " 用戶自定義 text object
Plug 'kana/vim-textobj-indent'                                          " 縮進對象,關鍵字 <i>
Plug 'kana/vim-textobj-syntax'                                          " 語法對象
Plug 'kana/vim-textobj-function', { 'for':['c', 'cpp', 'vim', 'java'] } " 函數對象,關鍵字 <f>
Plug 'sgur/vim-textobj-parameter'                                       " 參數對象,關鍵字 <,>
Plug 'coderifous/textobj-word-column.vim'                               " 列對象,關鍵字 <c>
Plug 'bps/vim-textobj-python'                                           " python 對象合集,<c> 類,<f> 函數
Plug 'tpope/vim-surround'                                               " deal with surround for text object
call plug#end()
filetype plugin indent on

由於 vim-plug 的強大,我們只需要將上述配置粘貼進 vimrc 中,重載配置文件後運行 :PlugInstall<CR> 就會自動安裝所有插件,甚至 vim-plug 會幫我們做好安裝後的處理。

5.4 必裝插件配置 - 大大提高生產力

通過 vim-plug 安裝的插件大部分都是即裝即用的,但也包括小部分需要在安裝完成後進行 一定的配置才能舒適地使用。

5.4.1 YouCompleteMe - 最強補全神器

YouCompleteMe 是谷歌程序員 Valloric 開發的一款智能補全插件。在 YouCompleteMe 出來之前,大部分補全插件都是基 於 tag 分析,也就是從當前文件以及其他 buffer 中的文件文本中匹配類似字符串進行補 全,說白了就是靠猜。而 YouCompleteMe 是真正支持程序語義分析的補全,能夠理解程序 語言進行補全,準確率大大提高,並且體驗也提升了很多。

盜用 YouCompleteMe 官方倉庫上的一張效果圖

https://camo.githubusercontent.com/1f3f922431d5363224b20e99467ff28b04e810e2/687474703a2f2f692e696d6775722e636f6d2f304f50346f6f642e676966

雖然 YouCompleteMe 效果看起來很炫酷,但它也被稱爲“最難安裝的插件”。它的底層是用 C++ 寫成的,爲了能作爲 Vim 的插件,外層又用 Python 做了封裝,因此它也是最複雜的 插件。 Plug 'Valloric/YouCompleteMe', { 'do': './install.py --clang-completer' } 命令要求 vim-plug 將 YouCompleteMe 插件下載下來後自動編譯,但如果你的終端上缺 少編譯的依賴會導致編譯失敗,此時我們需要手動進行編譯。

在 Linux 下進行安裝會相對比較容易,在編譯之前你需要但不限於以下工具:

  • cmake
  • automake
  • python3-dev(python2 或 python3)

之後運行 cd ~/.vim/plugged/YouCompleteMe/ 進入 YouCompleteMe 的安裝目錄,運行 ./install.py --clang-completer 進行安裝,如果不需要 C 語言補全支持的話無需 --clang-completer 選項。安裝程序會自動下載 libclang 依賴,等待安裝完成即可。 Windows 的安裝過程大部分相似,可參考 YouCompleteMe 的官方倉庫文檔。

再次使用 Vim 打開一個 Python 或 cpp 文件應該就能自動補全了。但接下來你會發現 YouCompleteMe 彈出的補全菜單裏只會包括前面你輸入過的關鍵字,並沒有進行語義識別, 那不是和自帶的 omni 補全沒區別!還浪費我這麼長時間安裝!其實之前我也是這麼想的, 直到有一天我看到這篇文章,我才發現原 來我一直用的是半殘的 YouCompleteMe,根本沒發揮出它真正的實力。通過如下配置就能使 YouCompleteMe 的補全策略更貼近於常見的 IDE。

set completeopt=longest,menu                                " YCM 的提示方式,previews 會顯示具體預覽窗口,menu 不提示
" let g:ycm_add_preview_to_completeopt = 0
let g:ycm_collect_identifiers_from_tags_files=1             " 使用 ctags 生成的 tags 文件
let g:ycm_min_num_of_chars_for_completion=2                 " 從第 2 個鍵入字符就開始羅列匹配項
let g:ycm_seed_identifiers_with_syntax=1                    " 語法關鍵字補全
let g:ycm_cache_omnifunc=1                                  " 禁止緩存匹配項,每次都重新生成匹配項
let g:ycm_complete_in_comments = 1                          " 在註釋輸入中也能補全
let g:ycm_complete_in_strings = 1                           " 在字符串輸入中也能補全
let g:ycm_collect_identifiers_from_comments_and_strings = 1 " 註釋和字符串中的文字也會被收入補全
let g:ycm_python_binary_path = '/usr/bin/python'            " set default python binary
let g:ycm_global_ycm_extra_conf = '~/.ycm_extra_conf.py'    " set global ycm extra config path
let g:ycm_confirm_extra_conf = 0                            " confirm for config file
let g:ycm_use_ultisnips_completer=1                         " 查詢 ultisnips 提供的代碼模板補全
let g:ycm_key_invoke_completion = '<c-z>'                   " 手動觸發補全的按鍵
" 使 YCM 能在輸入兩個字母的時候自動基於語義補全
let g:ycm_semantic_triggers =  {
            \ 'c,cpp,python,java,go,erlang,perl': ['re!\w{2}'],
            \ 'cs,lua,javascript': ['re!\w{2}'],
            \ }
nnoremap <leader>d :YcmCompleter GoToDefinitionElseDeclaration<CR> " goto definition
autocmd InsertLeave * if pumvisible() == 0|pclose|endif            " 離開插入模式後自動關閉預覽窗口
autocmd VimEnter * EchoDocEnable

5.4.2 ALE - 第二好的異步語法檢查工具

ALE 是一款非常強大的異步語法檢查工具。在 Vim 支 持異步之前,最好的語法檢查工具是 Syntastic,但由於它非異步的工作 方式,導致語法檢查會阻塞主線程,使用體驗比較差。在新版 Vim 中,都會推薦 ALE 或其 他支持異步的語法檢查工具。事實上 ALE 並不僅僅是一款語法檢查工具,它是包括了語法 檢查、代碼風格格式化、自動補全功能的工具集。

ALE 安裝完成後,隨意打開一個代碼文件。如果語法存在錯誤,那麼在窗口的最左側會顯示 一列符號,包括 >>-- 。將光標移至有標記的行,在最下方的 statusline 中會顯 示錯誤的具體信息。

" let g:ale_set_loclist = 0           " show the lint as a dot on left of a line
" let g:ale_set_quickfix = 1          " show the lint in quickfix list
" let g:ale_open_list = 1             " show quickfix window to show error message
" let g:ale_keep_list_window_open = 1 " keep quickfix window show whether errors exist
" let g:ale_sign_column_always = 1    " 保持左側邊欄始終可見
nmap <silent> <C-k> <Plug>(ale_previous_wrap)
nmap <silent> <C-j> <Plug>(ale_next_wrap)
" Check Python files with flake8 and pylint.
" let b:ale_linters = ['flake8', 'pylint']
" set fixers for different file types
let b:ale_fixers = {
            \ 'python': ['isort', 'autopep8'],
            \ 'cpp': ['clang-format'],
            \ 'markdown': ['prettier']}
" let g:ale_linters_explicit = 1
let g:ale_completion_delay = 500                     " 補全的延遲
let g:ale_echo_delay = 20                            " 回顯的延遲
let g:ale_lint_delay = 500                           " 停止輸入後更新 lint 標記的延遲
let g:ale_echo_msg_format = '[%linter%] <%code> %%s' " 自定義 lint 輸出格式
let g:ale_lint_on_text_changed = 'normal'            " 當文字在 NORMAL 模式下發生更改的時候更新 lint,防止 YCM 頻繁刷新
let g:ale_lint_on_insert_leave = 1                   " 離開 INSERT 模式時更新 lint

將錯誤信息表示在左側只是一種方式,你也可以通過反註釋前 4 行,利用 Vim 自帶的 quickfix 窗口顯示錯誤信息。雖然通過這種方式你能夠更清晰的瞭解各個錯誤的具體信息, 但需要佔用一部分寶貴的屏幕空間。

11 至 14 行設置了 ALE 的默認格式化工具,ALE 本身並不對代碼進行格式化,而是針對不 同文件類型調用合適的格式化工具進行處理(ALE 支持的格式化工具列表可參照 官方倉庫)。其中 isortautopep8prettier 都是 Python 編寫的命令行工具, clang-formatclang 編譯器帶的 C++ 格式化工具。

20、21 行是針對 YCM 做的額外配置,據悉可以解決 YCM 補全菜單頻繁刷新的問題。

Author: Cycoe (cycoejoo@163.com)
Date: <2018-08-12 Sun 22:34>
Generator: Emacs 28.0.50 (Org mode 9.3)
Built: <2020-05-21 Thu 20:09>