1. 程式人生 > >【轉載】uvm前置 · Vim 從入門到精通

【轉載】uvm前置 · Vim 從入門到精通

宣告:該文章轉載自github - wsdjeg的專案(見如下連結),此處僅供查閱方便,如侵刪,感謝作者和翻譯者的突出貢獻。

Vim 從入門到精通

本文主要在翻譯 mhinz/vim-galore 的基礎添加了一些我在使用 Vim 及開發 Vim 外掛的過程中積累的一些細節。

簡介

什麼是 Vim?

Vim 是一個歷史悠久的文字編輯器,可以追溯到 qedBram Moolenaar 於 1991 年釋出初始版本。

Linux、Mac 使用者,可以使用包管理器安裝 Vim,對於 Windows 使用者,可以從 我的網盤 下載。 該版本可輕易新增 python 、python3

 、lua 等支援,只需要安裝 python、lua 即可。

中文 Vim 使用者交流,可以加入 QQ 群,或者使用 Vim 中文社群頻道

返回主目錄 ⤴️

Vim 哲學

Vim 採用模式編輯的理念,即它提供了多種模式,按鍵在不同的模式下作用不同。你可以在 普通模式 下瀏覽檔案,在 插入模式下插入文字,在 可視模式 下選擇行,在 命令模式 下執行命令等等。起初這聽起來可能很複雜,但是這有一個很大的優點:不需要通過同時按住多個鍵來完成操作,大多數時候你只需要依次按下這些按鍵即可。越常用的操作,所需要的按鍵數量越少。

和模式編輯緊密相連的概念是“操作符”和“動作”。操作符 開始一些行為,例如:修改,刪除,或者選擇文字。之後你要用一個 動作

 來指定需要操作的文字區域。比如,要改變括號內的文字,需要執行 ci( (讀做 change inner parentheses);刪除整個段落的內容,需要執行 dap (讀做:delete around paragraph)。

如果你能看見 Vim 老司機操作,你會發現他們使用 Vim 指令碼語言就如同鋼琴師彈鋼琴一樣。複雜的操作只需要幾個按鍵就能完成。他們甚至不用刻意去想,因為這已經成為肌肉記憶了。這減少認識負荷並幫助人們專注於實際任務。

返回主目錄 ⤴️

入門

Vim 自帶一個互動式的教程,內含你需要了解的最基礎的資訊,你可以通過終端執行以下命令開啟教程:

$ vimtutor

不要因為這個看上去很無聊而跳過,按照此教程多練習。你以前用的 IDE 或者其他編輯器很少是有“模式”概念的,因此一開始你會很難適應模式切換。但是你 Vim 使用的越多,肌肉記憶 將越容易形成。

Vim 基於一個 vi 克隆,叫做 Stevie,支援兩種執行模式:"compatible" 和 "nocompatible"。在相容模式下執行 Vim 意味著使用 vi 的預設設定,而不是 Vim 的預設設定。除非你新建一個使用者的 vimrc 或者使用 vim -N 命令啟動 Vim,否則就是在相容模式下執行 Vim!請大家不要在相容模式下執行 Vim。

下一步

  1. 建立你自己的 vimrc
  2. 在第一週準備備忘錄
  3. 通讀基礎章節瞭解 Vim 還有哪些功能。
  4. 按需學習!Vim 是學不完的。如果你遇到了問題,先上網尋找解決方案,你的問題可能已經被解決了。Vim 擁有大量的參考文件,知道如何利用這些參考文件很有必要:獲取離線幫助
  5. 瀏覽附加資源

最後一個建議:使用外掛之前,請先掌握 Vim 的基本操作。很多外掛都只是對 Vim 自帶功能的封裝。

返回主目錄 ⤴️

精簡的 vimrc

使用者的 vimrc 配置檔案可以放在 ~/.vimrc,或者為了更好的分離放在 ~/.vim/vimrc,後者更便於通過版本控制軟體備份和同步整個配置,比方說 Github。

你可以在網上找到許多精簡的 vimrc 配置檔案,我的版本可能並不是最簡單的版本,但是我的版本提供了一套我認為良好的,非常適合入門的設定。

最終你需要閱讀完那些設定,然後自行決定需要使用哪些。:-)

如果你有興趣,這裡是我(原作者)的 vimrc

建議:大多數外掛作者都維護不止一個外掛並且將他們的 vimrc 放在 Github 上展示(通常放在叫做 "vim-config" 或者 "dotfiles" 的倉庫中),所以當你發現你喜歡的外掛時,去外掛維護者的 Github 主頁看看有沒有這樣的倉庫。

返回主目錄 ⤴️

我正在使用什麼樣的 Vim

使用 :version 命令將向你展示當前正在執行的 Vim 的所有相關資訊,包括它是如何編譯的。

第一行告訴你這個二進位制檔案的編譯時間和版本號,比如:7.4。接下來的一行呈現 Included patches: 1-1051,這是補丁版本包。因此你 Vim 確切的版本號是 7.4.1051。

另一行顯示著一些像 Tiny version without GUI 或者 Huge version with GUI 的資訊。很顯然這些資訊告訴你當前的 Vim 是否支援 GUI,例如:從終端中執行 gvim 或者從終端模擬器中的 Vim 內執行 :gui 命令。另一個重要的資訊是 Tiny 和 Huge。Vim 的特性集區分被叫做 tinysmallnormalbig and huge,所有的都實現不同的功能子集。

:version 主要的輸出內容是特性列表。+clipboard 意味這剪貼簿功能被編譯支援了,-clipboard 意味著剪貼簿特性沒有被編譯支援。

一些功能特性需要編譯支援才能正常工作。例如:為了讓 :prof 工作,你需要使用 huge 模式編譯的 Vim,因為那種模式啟用了 +profile 特性。

如果你的輸出情況並不是那樣,並且你是從包管理器安裝 Vim 的,確保你安裝了 vim-xvim-x11vim-gtkvim-gnome 這些包或者相似的,因為這些包通常都是 huge 模式編譯的。

你也可以執行下面這段程式碼來測試 Vim 版本以及功能支援:

" Do something if running at least Vim 7.4.42 with +profile enabled.
if (v:version > 704 || v:version == 704 && has('patch42')) && has('profile')
  " do stuff
endif

相關幫助:

:h :version
:h feature-list
:h +feature-list
:h has-patch

返回主目錄 ⤴️

備忘錄

為了避免版權問題,我只貼出連結:

返回主目錄 ⤴️

基礎

緩衝區,視窗,標籤

Vim 是一個文字編輯器。每次文字都是作為緩衝區的一部分顯示的。每一份檔案都是在他們自己獨有的緩衝區開啟的,外掛顯示的內容也在它們自己的緩衝區中。

緩衝區有很多屬性,比如這個緩衝區的內容是否可以修改,或者這個緩衝區是否和檔案相關聯,是否需要同步儲存到磁碟上。

視窗 是緩衝區上一層的視窗。如果你想同時檢視幾個檔案或者檢視同一檔案的不同位置,那樣你會需要視窗。

請別把他們叫做 分屏 。你可以把一個視窗分割成兩個,但是這並沒有讓這兩個視窗完全 分離 。

視窗可以水平或者豎直分割並且現有視窗的高度和寬度都是可以被調節設定的,因此,如果你需要多種窗口布局,請考慮使用標籤。

標籤頁 (標籤)是視窗的集合。因此當你想使用多種窗口布局時候請使用標籤。

簡單的說,如果你啟動 Vim 的時候沒有附帶任何引數,你會得到一個包含著一個呈現一個緩衝區的視窗的標籤。

順帶提一下,緩衝區列表是全域性可見的,你可以在任何標籤中訪問任何一個緩衝區。

返回主目錄 ⤴️

已啟用、已載入、已列出、已命名的緩衝區

用類似 vim file1 的命令啟動 Vim 。這個檔案的內容將會被載入到緩衝區中,你現在有一個已載入的緩衝區。如果你在 Vim 中儲存這個檔案,緩衝區內容將會被同步到磁碟上(寫回檔案中)。

由於這個緩衝區也在一個視窗上顯示,所以他也是一個已啟用的緩衝區。如果你現在通過 :e file2 命令載入另一個檔案,file1 將會變成一個隱藏的緩衝區,並且 file2 變成已啟用緩衝區。

使用 :ls 我們能夠列出所有可以列出的緩衝區。外掛緩衝區和幫助緩衝區通常被標記為不可以列出的緩衝區,因為那並不是你經常需要在編輯器中編輯的常規檔案。通過 :ls! 命令可以顯示被放入緩衝區列表的和未被放入列表的緩衝區。

未命名的緩衝區是一種沒有關聯特定檔案的緩衝區,這種緩衝區經常被外掛使用。比如 :enew 將會建立一個無名臨時緩衝區。新增一些文字然後使用 :w /tmp/foo 將他寫入到磁碟,這樣這個緩衝區就會變成一個已命名的緩衝區

返回主目錄 ⤴️

引數列表

全域性緩衝區列表是 Vim 的特性。在這之前的 vi 中,僅僅只有引數列表,引數列表在 Vim 中依舊可以使用。

每一個通過 shell 命令傳遞給 Vim 的檔名都被記錄在一個引數列表中。可以有多個引數列表:預設情況下所有引數都被放在全域性引數列表下,但是你可以使用 :arglocal 命令去建立一個新的本地視窗的引數列表。

使用 :args 命令可以列出當前引數。使用 :next:previous:first:last 命令可以在切換在引數列表中的檔案。通過使用 :argadd:argdelete 或者 :args 等命令加上一個檔案列表可以改變引數列表。

偏愛緩衝區列表還是引數列表完全是個人選擇,我的印象中大多數人都是使用緩衝區列表的。

然而引數列表在有些情況下被大量使用:批處理 使用 :argdo! 一個簡單的重構例子:

:args **/*.[ch]
:argdo %s/foo/bar/ge | update

這條命令將替換掉當前目錄下以及當前目錄的子目錄中所有的 C 原始檔和標頭檔案中的“foo”,並用“bar”代替。

相關幫助::h argument-list

返回主目錄 ⤴️

按鍵對映

使用 :map 命令家族你可以定義屬於你自己的快捷鍵。該家族的每一個命令都限定在特定的模式下。從技術上來說 Vim 自帶高達 12 中模式,其中 6 種可以被對映。另外一些命令作用於多種模式:

  遞迴     非遞迴     模式                          
:map :noremap normal, visual, operator-pending
:nmap :nnoremap normal
:xmap :xnoremap visual
:cmap :cnoremap command-line
:omap :onoremap operator-pending
:imap :inoremap insert

例如:這個自定義的快捷鍵只在普通模式下工作。

:nmap <space> :echo "foo"<cr>

使用 :nunmap <space> 可以取消這個對映。

對於更少數,不常見的模式(或者他們的組合),檢視 :h map-modes

到現在為止還好,對新手而言有一個問題會困擾他們::nmap 是遞迴執行的!結果是,右邊執行可能的對映。

你自定義了一個簡單的對映去輸出“Foo”:

:nmap b :echo "Foo"<cr>

但是如果你想要對映 b (回退一個單詞)的預設功能到一個鍵上呢?

:nmap a b

如果你敲擊a,我們期望著游標回退到上一個單詞,但是實際情況是“Foo”被輸出到命令列裡!因為在右邊,b 已經被對映到別的行為上了,換句話說就是 :echo "Foo"<cr>

解決此問題的正確方法是使用一種 非遞迴 的對映代替:

:nnoremap a b

經驗法則:除非遞迴是必須的,否則總是使用非遞迴對映。

通過不給一個右值來檢查你的對映。比如:nmap 顯示所以普通模式下的對映,:nmap <leader> 顯示所有以 <leader> 鍵開頭的普通模式下的對映。

如果你想禁止用標準對映,把他們對映到特殊字元 <nop> 上,例如::noremap <left> <nop>

相關幫助:

:h key-notation
:h mapping
:h 05.3

返回主目錄 ⤴️

對映前置鍵

對映前置鍵(Leader 鍵)本身就是一個按鍵對映,預設為 \。我們可以通過在 map 中呼叫 <leader> 來為把它新增到其他按鍵對映中。

nnoremap <leader>h :helpgrep<space>

這樣,我們只需要先按 \ 然後連續按 \h 就可以啟用這個對映 :helpgrep<space>。如果你想通過先按 空格 鍵來觸發,只需要這樣做:

let mapleader = ' '
nnoremap <leader>h :helpgrep<space>

另外,還有一個叫 <localleader> 的,可以把它理解為區域性環境中的 <leader>,預設值依然為 \。當我們需要只對某一個條件下(比如,特定檔案型別的外掛)的緩衝區設定特別的 <leader> 鍵,那麼我們就可以通過修改當前環境下的 <localleader>來實現。

注意:如果你打算設定 Leader 鍵,請確保在設定按鍵對映之前,先設定好 Leader 鍵。如果你先設定了含有 Leader 鍵的對映,然後又修改了 Leader 鍵,那麼之前對映內的 Leader 鍵是不會因此而改變的。你可以通過執行 :nmap <leader> 來檢視普通模式中已繫結給 Leader 鍵的所有對映。

請參閱 :h mapleader 與 :h maploacalleader 來獲取更多幫助。

返回主目錄 ⤴️

暫存器

暫存器就是儲存文字的地方。我們常用的「複製」操作就是把文字儲存到暫存器,「 貼上」 操作就是把文字從暫存器中讀出來。順便,在 Vim 中複製的快捷鍵是 y,貼上的快捷鍵是 p。

Vim 為我們提供瞭如下的暫存器:

型別 標識 讀寫者 是否為只讀 包含的字元來源
Unnamed " vim 最近一次的複製或刪除操作 (dcsxy)
Numbered 09 vim 暫存器 0: 最近一次複製。暫存器 1: 最近一次刪除。暫存器 2: 倒數第二次刪除,以此類推。對於暫存器 1 至 9,他們其實是隻讀的最多包含 9 個元素的佇列。這裡的佇列即為資料型別 queue
Small delete - vim 最近一次行內刪除
Named azAZ 使用者 如果你通過複製操作儲存文字至暫存器 a,那麼 a 中的文字就會被完全覆蓋。如果你儲存至 A,那麼會將文字新增給暫存器 a,不會覆蓋之前已有的文字
Read-only :.% vim :: 最近一次使用的命令,.: 最近一次新增的文字,%: 當前的檔名
Alternate buffer # vim 大部分情況下,這個暫存器是當前視窗中,上一次訪問的緩衝區。請參閱 :h alternate-file 來獲取更多幫助
Expression = 使用者 複製 VimL 程式碼時,這個暫存器用於儲存程式碼片段的執行結果。比如,在插入模式下複製 <c-r>=5+5<cr>,那麼這個暫存器就會存入 10
Selection +* vim * 和 + 是 剪貼簿 暫存器
Drop ~ vim 最後一次拖拽新增至 Vim 的文字(需要 "+dnd" 支援,暫時只支援 GTK GUI。請參閱 :help dnd 及 :help quote~
Black hole _ vim 一般稱為黑洞暫存器。對於當前操作,如果你不希望在其他暫存器中保留文字,那就在命令前加上 _。比如,"_dd 命令不會將文字放到暫存器 "1+ 或 * 中
Last search pattern / vim 最近一次通過 /? 或 :global 等命令呼叫的匹配條件

只要不是隻讀的暫存器,使用者都有許可權修改它的內容,比如:

:let @/ = 'register'

這樣,我們按 n 的時候就會跳轉到單詞"register" 出現的地方。

有些時候,你的操作可能已經修改了暫存器,而你沒有察覺到。請參閱 :h registers 獲取更多幫助。

上面提到過,複製的命令是 y,貼上的命令是 p 或者 P。但請注意,Vim 會區分「字元選取」與「行選取」。請參閱 :h linewise 獲取更多幫助。

行選取: 命令 yy 或 Y 都是複製當前行。這時移動游標至其他位置,按下 p 就可以在游標下方貼上複製的行,按下 P 就可以在游標上方貼上至複製的行。

字元選取: 命令 0yw 可以複製第一個單詞。這時移動游標至其他位置,按下 p 就可以在當前行、游標後的位置貼上單詞,按下 P 就可以在當前行、游標前的位置貼上單詞。

將文字存到指定的暫存器中: 命令 "aY 可以將當前行復制,並存儲到暫存器 a 中。這時移動游標至其他位置,通過命令 "AY就可以把這一行的內容擴充套件到暫存器 a 中,而之前儲存的內容也不會丟失。

為了便於理解和記憶,建議大家現在就試一試上面提到的這些操作。操作過程中,你可以隨時通過 :reg 來檢視暫存器的變化。

有趣的是: 在 Vim 中,y 是複製命令,源於單詞 "yanking"。而在 Emacs 中,"yanking" 代表的是貼上(或者說,重新插入剛才刪掉的內容),而並不是複製。

返回主目錄 ⤴️

範圍

範圍 (Ranges) 其實很好理解,但很多 Vim 使用者的理解不到位。

  • 很多命令都可以加一個數字,用於指明操作範圍
  • 範圍可以是一個行號,用於指定某一行
  • 範圍也可以是一對通過 , 或 ; 分割的行號
  • 大部分命令,預設只作用於當前行
  • 只有 :write 和 :global 是預設作用於所有行的

範圍的使用是十分直觀的。以下為一些例子(其中,:d 為 :delete 的縮寫):

命令 操作的行
:d 當前行
:.d 當前行
:1d 第一行
:$d 最後一行
:1,$d 所有行
:%d 所有行(這是 1,$ 的語法糖)
:.,5d 當前行至第 5 行
:,5d 同樣是當前行至第 5 行
:,+3d 當前行及接下來的 3 行
:1,+3d 第一行至當前行再加 3 行
:,-3d 當前行及向上的 3 行(Vim 會彈出提示資訊,因為這是一個保留的範圍)
:3,'xdelete 第三行至標註 為 x 的那一行
:/^foo/,$delete 當前行以下,以字元 "foo" 開頭的那一行至結尾
:/^foo/+1,$delete 當前行以下,以字元 "foo" 開頭的那一行的下一行至結尾

需要注意的是,; 也可以用於表示範圍。區別在於,a,b 的 b 是以當前行作為參考的。而 a;b 的 b 是以 a 行作為參考的。舉個例子,現在你的游標在第 5 行。這時 :1,+1d 會刪除第 1 行至第 6 行,而 :1;+1d 會刪除第 1 行和第 2 行。

如果你想設定多個尋找條件,只需要在條件前加上 /,比如:

:/foo//bar//quux/d

這就會刪除當前行之後的某一行。定位方式是,先在當前行之後尋找第一個包含 "foo" 字元的那一行,然後在找到的這一行之後尋找第一個包含 "bar" 字元的那一行,然後再在找到的這一行之後尋找第一個包含 "quux" 的那一行。刪除的就是最後找到的這一行。

有時,Vim 會在命令前自動新增範圍。舉個例子,如果你先通過 V 命令進入行選取模式,選中一些行後按下 : 進入命令模式,這時候你會發現 Vim 自動添加了 '<,'> 範圍。這表示,接下來的命令會使用之前選取的行號作為範圍。但如果後續命令不支援範圍,Vim 就會報錯。為了避免這樣的情況發生,有些人會設定這樣的按鍵對映::vnoremap foo :<c-u>command,組合鍵 Ctrl + u 可以清除當前命令列中的內容。

另一個例子是在普通模式中按下 !!,命令列中會出現 :.!。如果這時你如果輸入一個外部命令,那麼當前行的內容就會被這個外部命令的輸出替換。你也可以通過命令 :?^$?+1,/^$/-1!ls 把當前段落的內容替換成外部命令 ls 的輸出,原理是向前和向後各搜尋一個空白行,刪除這兩個空白行之間的內容,並將外部命令 ls 的輸出放到這兩個空白行之間。

請參閱以下兩個命令來獲取更多幫助:

:h cmdline-ranges
:h 10.3

返回主目錄 ⤴️

標註

你可以使用標註功能來標記一個位置,也就是記錄檔案某行的某個位置。

標註 設定者 使用
a-z 使用者 僅對當前的一個檔案生效,也就意味著只可以在當前檔案中跳轉
A-Z 使用者 全域性標註,可以作用於不同檔案。大寫標註也稱為「檔案標註」。跳轉時有可能會切換到另一個緩衝區
0-9 viminfo 0 代表 viminfo 最後一次被寫入的位置。實際使用中,就代表 Vim 程序最後一次結束的位置。1 代表 Vim 程序倒數第二次結束的位置,以此類推

如果想跳轉到指定的標註,你可以先按下 ' / g' 或者 ` / g` 然後按下標註名。

如果你想定義當前檔案中的標註,可以先按下 m 再按下標註名。比如,按下 mm 就可以把當前位置標註為 m。在這之後,如果你的游標切換到了檔案的其他位置,只需要通過 'm 或者 \m即可回到剛才標註的行。區別在於,'m會跳轉回被標記行的第一個非空字元,而`m會跳轉回被標記行的被標記列。根據 viminfo 的設定,你可以在退出 Vim 的時候保留小寫字元標註。請參閱:h viminfo-'` 來獲取更多幫助。

如果你想定義全域性的標註,可以先按下 m 再按下大寫英文字元。比如,按下 mM 就可以把當前檔案的當前位置標註為 M。在這之後,就算你切換到其他的緩衝區,依然可以通過 'M 或 \M` 跳轉回來。

關於跳轉,還有以下的方式:

按鍵 跳轉至
'[ 與 `[ 上一次修改或複製的第一行或第一個字元
'] 與 `] 上一次修改或複製的最後一行或最後一個字元
'< 與 `< 上一次在可視模式下選取的第一行或第一個字元
'> 與 `> 上一次在可視模式下選取的最後一行或最後一個字元
'' 與 `' 上一次跳轉之前的游標位置
'" 與 `" 上一次關閉當前緩衝區時的游標位置
'^ 與 `^ 上一次插入字元後的游標位置
'. 與 `. 上一次修改文字後的游標位置
'( 與 `( 當前句子的開頭
') 與 `) 當前句子的結尾
'{ 與 `{ 當前段落的開頭
'} 與 `} 當前段落的結尾

標註也可以搭配 範圍 一起使用。前面提到過,如果你在可視模式下選取一些文字,然後按下 :,這時候你會發現命令列已經被填充了 :'<,'>。對照上面的表格,現在你應該明白了,這段代表的就是可視模式下選取的範圍。

請使用 :marks 命令來顯示所有的標註,參閱 :h mark-motions 來獲取關於標註的更多幫助。

返回主目錄 ⤴️

補全

Vim 在插入模式中為我們提供了多種補全方案。如果有多個補全結果,Vim 會彈出一個選單供你選擇。

常見的補全有標籤、專案中引入的模組或庫中的方法名、檔名、字典及當前緩衝區的欄位。

針對不同的補全方案,Vim 為我們提供了不同的按鍵對映。這些對映都是在插入模式中通過 Ctrl + x 來觸發:

對映 型別 幫助文件
<c-x><c-l> 整行 :h i^x^l
<c-x><c-n> 當前緩衝區中的關鍵字 :h i^x^n
<c-x><c-k> 字典(請參閱 :h 'dictionary')中的關鍵字 :h i^x^k
<c-x><c-t> 同義詞字典(請參閱 :h 'thesaurus')中的關鍵字 :h i^x^t
<c-x><c-i> 當前檔案以及包含的檔案中的關鍵字 :h i^x^i
<c-x><c-]> 標籤 :h i^x^]
<c-x><c-f> 檔名 :h i^x^f
<c-x><c-d> 定義或巨集定義 :h i^x^d
<c-x><c-v> Vim 命令 :h i^x^v
<c-x><c-u> 使用者自定義補全(通過 'completefunc' 定義) :h i^x^u
<c-x><c-o> Omni Completion(通過 'omnifunc' 定義) :h i^x^o
<c-x>s 拼寫建議 :h i^Xs

儘管使用者自定義補全與 Omni Completion 是不同的,但他們做的事情基本一致。共同點在於,他們都是一個監聽當前游標位置的函式,返回值為一系列的補全建議。使用者自定義補全是由使用者定義的,基於使用者的個人用途,因此你可以根據自己的喜好和需求隨意定製。而 Omni Completion 是針對檔案型別的補全,比如在 C 語言中補全一個結構體(struct)的成員(members),或者補全一個類的方法,因而它通常都是由檔案型別外掛設定和呼叫的。

如果你設定了 'complete' 選項,那麼你就可以在一次操作中採用多種補全方案。這個選項預設包含了多種可能性,因此請按照自己的需求來配置。你可以通過 <c-n> 來呼叫下一個補全建議,或通過 <c-p> 來呼叫上一個補全建議。當然,這兩個對映同樣可以直接呼叫補全函式。請參閱 :h i^n 與 :h 'complete' 來獲得更多幫助。

如果你想配置彈出選單的行為,請一定要看一看 :h 'completeopt' 這篇幫助文件。預設的配置已經不錯了,但我個人(原作者)更傾向於把 "noselect" 加上。

請參閱以下文件獲取更多幫助:

:h ins-completion
:h popupmenu-keys
:h new-omni-completion

返回主目錄 ⤴️

動作,操作符,文字物件

動作也就是指移動游標的操作,你肯定很熟悉 hjk 和 l,以及 w 和 b。但其實,/ 也是一個動作。他們都可以搭配數字使用,比如 2?the<cr> 可以將游標移動到倒數第二個 "the" 出現的位置。

以下會列出一些常用的動作。你也可以通過 :h navigation 來獲取更多的幫助。

操作符是對某個區域文字執行的操作。比如,d~gU 和 > 都是操作符。這些操作符既可以在普通模式下使用,也可以在可視模式下使用。在普通模式中,順序是先按操作符,再按動作指令,比如 >j。在可是模式中,選中區域後直接按操作符就可以,比如 Vjd

與動作一樣,操作符也可以搭配數字使用,比如 2gUw 可以將當前單詞以及下一個單詞轉成大寫。由於動作和操作符都可以搭配數字使用,因此 2gU2w 與執行兩次 gU2w 效果是相同的。

請參閱 :h operator 來檢視所有的操作符。你也可以通過 :set tildeop 命令把 ~ 也變成一個操作符

值得注意的是,動作是單向的,而文字物件是雙向的。文字物件不僅作用於符號(比如括號、中括號和大括號等)標記的範圍內,也作用於整個單詞、整個句子等其他情況。

文字物件不能用於普通模式中移動游標的操作,因為游標還沒有智慧到可以向兩個方向同時跳轉。但這個功能可以在可視模式中實現,因為在物件的一端選中的情況下,游標只需要跳轉到另一端就可以了。

文字物件操作一般用 i 或 a 加上物件識別符號操作,其中 i 表示在物件內(英文 inner)操作,a 表示對整個物件(英文 around)操作,這時開頭和結尾的空格都會被考慮進來。舉個例子,diw 可以刪除當前單詞,ci( 可以改變括號中的內容。

文字物件同樣可以與數字搭配使用。比如,像 ((( ))) 這樣的文字,假如游標位於最內層的括號上或最內層的括號內,那麼 d2a( 將會刪除從最內層開始的兩對括號,以及他們之間的所有內容。其實,d2a( 這個操作等同於 2da(。在 Vim 的命令中,如果有兩處都可以接收數字作為引數,那麼最終結果就等同於兩個數字相乘。在這裡,d 與 a( 都是可以接收引數的,一個引數是 1,另一個是 2,我們可以把它們相乘然後放到最前面。

請參閱 :h text-objects 來獲取更多關於文字物件的幫助。

返回主目錄 ⤴️

自動命令

在特定的情況下,Vim 會傳出事件。如果你想針對這些事件執行回撥方法,那麼就需要用到自動命令這個功能。

如果沒有了自動命令,那你基本上是用不了 Vim 的。自動命令一直都在執行,只是很多時候你沒有注意到。不信的話,可以執行命令 :au ,不要被結果嚇到,這些是當前有效的所有自動命令。

請使用 :h {event} 來檢視 Vim 中所有事件的列表,你也可以參考 :h autocmd-events-abc 來獲取關於事件的更多幫助。

一個很常用的例子,就是針對檔案型別執行某些設定:

autocmd FileType ruby setlocal shiftwidth=2 softtabstop=2 comments-=:#

但是緩衝區是如何知道當前的檔案中包含 Ruby 程式碼呢?這其實是另一個自動命令檢測的到的,然後把檔案型別設定成為 Ruby,這樣就觸發了上面的 FileType 事件。

在配置 vimrc 的時候,一般第一行加進去的就是 filetype on。這就意味著,Vim 啟動時會讀取 filetype.vim 檔案,然後根據檔案型別來觸發相應的自動命令。

如果你勇於嘗試,可以檢視下 :e $VIMRUNTIME/filetype.vim,然後在輸出中搜索 "Ruby"。這樣,你就會發現其實 Vim 只是通過副檔名 .rb 判斷某個檔案是不是 Ruby 的。

注意:對於相同事件,如果有多個自動命令,那麼自動命令會按照定義時的順序執行。通過 :au 就可以檢視它們的執行順序。

au BufNewFile,BufRead *.rb,*.rbw setf ruby

BufNewFile 與 BufRead 事件是被寫在 Vim 原始檔中的。因此,每當你通過 :e 或者類似的命令開啟檔案,這兩個事件都會觸發。然後,就是讀取 filetype.vim 檔案來判斷開啟的檔案型別。

簡單來說,事件和自動命令在 Vim 中的應用十分廣泛。而且,Vim 為我們留出了一些易用的介面,方便使用者配置適合自己的事件驅動回撥。

請參閱 :h autocommand 來獲取更多幫助

返回主目錄 ⤴️

變更歷史,跳轉歷史

在 Vim 中,使用者最近 100 次的文字改動都會被儲存在變更歷史中。如果在同一行有多個小改動,那麼 Vim 會把它們合併成一個。儘管內容改動會合並,但作用的位置還是會只記錄下最後一次改動的位置。

在你移動游標或跳轉的時候,每一次的移動或跳轉前的位置會被記錄到跳轉歷史中。類似地,跳轉歷史也可以最多儲存 100 條記錄。對於每個視窗,跳轉記錄是獨立的。但當你分離視窗時(比如使用 :split 命令),跳轉歷史會被複制過去。

Vim 中的跳轉命令,包括 '`G/?nN%()[[]]{}:s:tagLMH 以及開始編輯一個新檔案的命令。

列表 顯示所有條目 跳轉到上一個位置 跳轉到下一個位置
跳轉歷史 :jumps [count]<c-o> [count]<c-i>
變更歷史 :changes [count]g; [count]g,

如果你執行第二列的命令顯示所有條目,這時 Vim 會用 > 標記來為你指示當前位置。通常這個標記位於 1 的下方,也就代表最後一次的位置。

如果你希望關閉 Vim 之後還保留這些條目,請參閱 :h viminfo-' 來獲取更多幫助。

注意:上面提到過,最後一次跳轉前的位置也會記錄在標註中,也可以通過連按 `` 或 '' 跳轉到那個位置

請參閱以下兩個命令來獲取更多幫助:

:h changelist
:h jumplist

返回主目錄 ⤴️

內容變更歷史記錄

Vim 會記錄文字改變之前的狀態。因此,你可以使用「撤銷」操作 u 來取消更改,也可以通過「重做」操作 Ctrl + r 來恢復更改。

值得注意的是,Vim 採用 tree 資料結構來儲存內容變更的歷史記錄,而不是採用 queue。你的每次改動都會成為儲存為樹的節點。而且,除了第一次改動(根節點),之後的每次改動都可以找到一個對應的父節點。每一個節點都會記錄改動的內容和時間。其中,「分支」代表從任一節點到根節點的路徑。當你進行了撤銷操作,然後又輸入了新的內容,這時候就相當於建立了分支。這個原理和 git 中的 branch(分支)十分類似。

考慮以下這一系列按鍵操作:

ifoo<esc>
obar<esc>
obaz<esc>
u
oquux<exc>

那麼現在,Vim 中會顯示三行文字,分別是 "foo"、"bar" 和 "quux"。這時候,儲存的樹形結構如下:

     foo(1)
       /
    bar(2)
   /      \
baz(3)   quux(4)

這個樹形結構共包含四次改動,括號中的數字就代表時間順序。

現在,我們有兩種方式遍歷這個樹結構。一種叫「按分支遍歷」,一種叫「按時間遍歷」。

撤銷 u 與重做 Ctrl + r 操作是按分支遍歷。對於上面的例子,現在我們有三行字元。這時候按 u 會回退到 "bar" 節點,如果再按一次 u 則會回退到 "foo" 節點。這時,如果我們按下 Ctrl + r 就會前進至 "bar" 節點,再按一次就回前進至 "quux" 節點。在這種方式下,我們無法訪問到兄弟節點(即 "baz" 節點)。

與之對應的是按時間遍歷,對應的按鍵是 g- 和 g+。對於上面的例子,按下 g- 會首先回退到 "baz" 節點。再次按下 g- 會回退到 "bar" 節點。

命令/按鍵 執行效果
[count]u 或 :undo [count] 回退到 [count] 次改動之前
[count]<c-r> 或 :redo [count] 重做 [count] 次改動
U 回退至最新的改動
[count]g- 或 :earlier [count]? 根據時間回退到 [count] 次改動之前。"?" 為 "s"、"m"、"h"、"d" 或 "f"之一。例如,:earlier 2d會回退到兩天之前。:earlier 1f 則會回退到最近一次檔案儲存時的內容
[count]g+ 或 :later [count]? 類似 g-,但方向相反

內容變更記錄會儲存在記憶體中,當 Vim 退出時就會清空。如果需要持久化儲存內容變更記錄,請參閱備份檔案,交換檔案,撤銷檔案以及viminfo檔案的處理章節的內容。

如果你覺得這一部分的內容難以理解,請參閱 undotree,這是一個視覺化管理內容變更歷史記錄的外掛。類似的還有 vim-mundo

請參閱以下連結獲取更多幫助:

:h undo.txt
:h usr_32

返回主目錄 ⤴️

全域性位置資訊表,區域性位置資訊表

在某一個動作返回一系列「位置」的時候,我們可以利用「全域性位置資訊表」和「區域性位置資訊表」來儲存這些位置資訊,方便以後跳轉回對應的位置。每一個儲存的位置包括檔名、行號和列號。

比如,編譯程式碼是出現錯誤,這時候我們就可以把錯誤的位置直接顯示在全域性位置資訊表,或者通過外部抓取工具使位置顯示在區域性位置資訊表中。

儘管我們也可以把這些資訊顯示到一個空格緩衝區中,但用這兩個資訊表顯示的好處在於介面呼叫很方便,而且也便於瀏覽輸出。

Vim 中,全域性位置資訊表只能有一個,但每一個視窗都可以有自己的區域性位置資訊表。這兩個資訊表的外觀看上去很類似,但在操作上會稍有不同。

以下為兩者的操作比較:

動作 全域性位置資訊表 區域性位置資訊表
開啟視窗 :copen :lopen
關閉視窗 :cclose :lclose
下一個條目 :cnext :lnext
上一個條目 :cprevious :lprevious
第一個條目 :cfirst :lfirst
最後一個條目 :clast :llast

請參閱 :h :cc 以及底下的內容,來獲取更多命令的幫助。

應用例項: 如果我們想用 grep 遞迴地在當前資料夾中尋找某個關鍵詞,然後把輸出結果放到全域性位置資訊表中,只需要這樣:

:let &grepprg = 'grep -Rn $* .'
:grep! foo
<grep output - hit enter>
:copen

執行了上面的程式碼,你就能看到所有包含字串 "foo" 的檔名以及匹配到的相關欄位都會顯示在全域性位置資訊表中。

返回主目錄 ⤴️

巨集

你可以在 Vim 中錄製一系列按鍵,並把他們儲存到暫存器中。對於一些需要臨時使用多次的一系列操作,把它們作為巨集儲存起來會顯著地提升效率。對於一些複雜的操作,建議使用 Vim 指令碼來實現。

  • 首先,按下 q,然後按下你想要儲存的暫存器,任何小寫字母都可以。比如我們來把它儲存到 q 這個暫存器中。按下 qq,你會發現命令列裡已經顯示了 "recording @q"。
  • 如果你已經錄製完成,那麼只需要再按一次 q 就可以結束錄製。
  • 如果你想呼叫剛才錄製的巨集,只需要 [count]@q
  • 如果你想呼叫上一次使用的巨集,只需要 [count]@@

例項1

一個插入字串 "abc" 後換行的巨集,重複呼叫十次:

qq
iabc<cr><esc>
q
[email protected]

(對於上面這個功能,你同樣可以通過如下的按鍵: oabc 然後 ESC 然後 10. 來實現)。

例項2

一個在每行前都加上行號的巨集。從第一行開始,行號為 1,後面依次遞增。我們可以通過 Ctrl + a 來實現遞增的行號,在定義巨集的時候,它會顯示成 ^A

qq
0yf jP0^A
q
1000 @q

這裡能實現功能,是因為我們假定了檔案最多隻有 1000 行。但更好的方式是使用「遞迴」巨集,它會一直執行,知道不能執行為止:

qq
0yf jP0^[email protected]
q
@q

(對於上面這個插入行號的功能,如果你不願意使用巨集,同樣可以通過這段按鍵操作來實現::%s/^/\=line('.') . '. ')。

這裡向大家展示瞭如何不用巨集來達到相應的效果,但要注意,這些不用巨集的實現方式只適用於這些簡單的示例。對於一些比較複雜的自動化操作,你確實應該考慮使用巨集。

請參閱以下文件獲取更多幫助:

:h recording
:h 'lazyredraw'

返回主目錄 ⤴️

顏色主題

顏色主題可以把你的 Vim 變得更漂亮。Vim 是由多個元件構成的,我們可以給每一個元件都設定不同的文字顏色、背景顏色以及文字加粗等等。比如,我們可以通過這個命令來設定背景顏色:

:highlight Normal ctermbg=1 guibg=red

執行後你會發現,現在背景顏色變成紅色了。請參閱 :h :highlight 來獲取更多幫助。

其實,顏色主題就是一系列的 :highlight 命令的集合。

事實上,大部分顏色主題都包含兩套配置。一套適用於例如 xterm 和 iTerm 這樣的終端環境(使用字首 cterm),另一套適用於例如 gvim 和 MacVim 的圖形介面環境(使用字首 gui)。對於上面的例子,ctermbg 就是針對終端環境的,而 guibg 就是針對圖形介面環境的。

如果你下載了一個顏色主題,並且在終端環境中打開了 Vim,然後發現顯示的顏色與主題截圖中差別很大,那很可能是配置檔案只設置了圖形介面環境的顏色。反之同理,如果你使用的是圖形介面環境,發現顯示顏色有問題,那就很可能是配置檔案只設置了終端環境的顏色。

第二種情況(圖形介面環境的顯示問題)其實不難解決。如果你使用的是 Neovim 或者 Vim 7.4.1830 的後續版本,可以通過開啟真彩色設定來解決顯示問題。這就可以讓終端環境的 Vim 使用 GUI 的顏色定義,但首先,你要確認一下你的終端環境和環境內的元件(比如 tmux)是否都支援真彩色。可以看一下這篇文件,描述的十分詳細。

請參閱以下文件或連結來獲取更多幫助:

返回主目錄 ⤴️

摺疊

每一部分文字(或者程式碼)都會有特定的結構。對於存在結構的文字和程式碼,也就意味著它們可以按照一定的邏輯分割成不同區域。Vim 中的摺疊功能,就是按照特定的邏輯把文字和程式碼摺疊成一行,並顯示一些簡短的描述。摺疊功能涉及到很多操作,而且摺疊功能可以巢狀使用。

在 Vim 中,有以下 6 中摺疊型別:

摺疊方式 概述
diff 在「比較視窗」中摺疊未改變的文字
expr 使用 'foldexpr' 來建立新的摺疊邏輯
indent 基於縮排摺疊
manual 使用 zfzF 或 :fold 來自定義摺疊
marker 根據特定的文字標記摺疊(通常用於程式碼註釋)
syntax 根據語法摺疊,比如摺疊 if 程式碼塊

注意:摺疊功能可能會顯著地影響效能。如果你在使用摺疊功能的時候出現了打字卡頓之類的問題,請考慮使用 FastFold 外掛。這個外掛可以讓 Vim 按需更新摺疊內容,而不是一直呼叫。

請參閱以下文件獲取更多幫助:

:h usr_28
:h folds

會話

如果你儲存了當前的「檢視」(請參閱 :h :mkview),那麼當前視窗、配置和按鍵對映都會被儲存下來(請參閱 :h :loadview)。

「會話」就是儲存所有視窗的相關設定,以及全域性設定。簡單來說,就是給當前的 Vim 執行例項拍個照,然後把相關資訊儲存到會話檔案中。儲存之後的改動就不會在會話檔案中顯示,你只需要在改動後更新一下會話檔案就可以了。

你可以把當前工作的「專案」儲存起來,然後可以在不同的「專案」之間切換。

現在就來試試吧。開啟幾個視窗和標籤,然後執行 :mksession Foo.vim。如果你沒有指定檔名,那就會預設儲存為 Session.vim。這個檔案會儲存在當前的目錄下,你可以通過 :pwd 來顯示當前路徑。重啟 Vim 之後,你只需要執行 :source Foo.vim,就可以恢復剛才的會話了。所有的緩衝區、窗口布局、按鍵對映以及工作路徑都會恢復到儲存時的狀態。

其實 Vim 的會話檔案就只是 Vim 命令的集合。你可以通過命令 :vs Foo.vim 來看看會話檔案中究竟有什麼。

你可以決定 Vim 會話中究竟要儲存哪些配置,只需要設定一下 'sessionoptions' 就可以了。

為了方便開發,Vim 把最後一次呼叫或寫入的會話賦值給了一個內部變數 v:this_session

請參閱以下文件來獲取更多幫助:

:h Session
:h 'sessionoptions'
:h v:this_session

區域性化

以上提到的很多概念,都有一個區域性化(非全域性)的版本:

全域性 區域性 作用域 幫助文件
:set :setlocal 緩衝區或視窗 :h local-options
:map :map <buffer> 緩衝區 :h :map-local
:autocmd :autocmd * <buffer> 緩衝區 :h autocmd-buflocal
:cd :lcd 視窗 :h :lcd
:<leader> :<localleader> 緩衝區 :h maploacalleader

用法

獲取離線幫助

Vim 自帶了一套很完善的幫助文件,它們是一個個有固定排版格式的文字檔案,通過標籤可以訪問這些檔案的特定位置。

在開始之前先讀一下這個章節::help :help。執行這個命令以後會在新視窗開啟 $VIMRUNTIME/doc/helphelp.txt 檔案並跳轉到這個檔案中 :help 標籤的位置。

一些關於幫助主題的簡單規則:

  • 用單引號把文字包起來表示選項,如::h 'textwidth'
  • 以小括號結尾表示 VimL 函式,如::h reverse()
  • 以英文冒號開頭表示命令,如::h :echo

使用快捷鍵 <c-d> (這是 ctrl+d)來列出所有包含你當前輸入的內容的幫助主題。如::h tab<c-d> 會列出所有包含 tab主題,從 softtabstop 到 setting-guitablabel (譯者注:根據安裝的外掛不同列出的選項也會不同)。

你想檢視所有的 VimL 方法嗎?很簡單,只要輸入::h ()<c-d> 就可以了。你想檢視所有與視窗相關的函式嗎?輸入 :h win*()<c-d>

相信你很快就能掌握這些技巧,但是在剛開始的時候,你可能對於該通過什麼進行查詢一點線索都沒有。這時你可以想象一些與要查詢的內容相關的關鍵字,再讓 :helpgrep 來幫忙。

:helpgrep backwards

上面的命令會在所有的幫助檔案中搜索“backwards”,然後跳轉到第一個匹配的位置。所有的匹配位置都會被新增到全域性位置資訊表,用 :cp / :cn 可以在匹配位置之間進行切換。或者用 :copen 命令來開啟全域性位置資訊表,將游標定位到你想要的位置,再按 回車就可以跳轉到該匹配項。詳細說明請參考 :h quickfix

獲取離線幫助(補充)

這個列表最初發表在 vim_dev,由 @chrisbra 編輯的,他是 Vim 開發人員中最活躍的一個。

經過一些微小的改動後,重新發布到了這裡。

如果你知道你想要找什麼,使用幫助系統的搜尋會更簡單一些,因為搜尋出的主題都帶有固定的格式。

而且幫助系統中的主題包含了你當前使用的 Vim 版本的所特有特性,而網上那些已經過時或者是早期釋出的話題是不會包含這些的。

因此學習使用幫助系統以及它所用的語言是很有必要的。這裡是一些例子(不一定全,我有可能忘了一些什麼)。

(譯者注:下面列表中提及的都是如何指定搜尋主題以便快速準確的找到你想要的幫助)

  1. 選項要用單引號引起來。用 :h 'list' 來檢視列表選項幫助。只有你明確的知道你要找這麼一個選項的時候才可以這麼做,不然的話你可以用 :h options.txt 來開啟所有選項的幫助頁面,再用正則表示式進行搜尋,如:/width。某些選項有它們自己的名稱空間,如::h cpo-a:h cpo-A, :h cpo-b 等等。

  2. 普通模式的命令不能用冒號作為字首。使用 :h gt 來轉到“gt”命令的幫助頁面。

  3. 正則表示式以“/”開頭,所以 :h /\+ 會帶你到正則表示式中量詞“+”的幫助頁面。

  4. 組合鍵經常以一個字母開頭表示它們可以在哪些模式中使用。如::h i_CTRL-X 會帶你到插入模式下的 CTRL-X 命令的用法幫助頁面,這是一個自動完成類的組合鍵。需要注意的是某些鍵是有固定寫法的,如Control鍵寫成CTRL。還有,查詢普通模式下的組合鍵幫助時,可以省略開頭的字母“n”,如::h CTRL-A。而 :h c_CTRL-A(譯者注:原文為 :h c_CRTL-R,感覺改為 A 更符合上下文語境)會解釋 CTRL-A 在命令模式下輸入命令時的作用;:h v_CTRL-A 說的是在可見模式下把游標所在處的數字加 1;:h g_CTRL-A 則說的是 g 命令(你需要先按 "g" 的命令)。這裡的 "g" 代表一個普通的命令,這個命令總是與其它的按鍵組合使用才生效,與 "z" 開始的命令相似。

  5. 暫存器是以 "quote" 開頭的。如::h quote: (譯者注:原文為:h quote,感覺作者想以":"來舉例)來檢視關於":"暫存器的說明。

  6. 關於 Vim 指令碼(VimL)的幫助都在 :h eval.txt 裡。而某些方面的語言可以使用 :h expr-X 獲取幫助,其中的 'X' 是一個特定的字元,如