> Vim from zero to hero - Vim 從入門到精通- 原文地址:- 原文作者:Marco Hinz- [簡介](#簡介) - [什麼是 Vim?](#什麼是-vim) - [Vim 哲學](#vim-哲學) - [入門](#入門) - [精簡的 vimrc](#精簡的-vimr... ...
Vim from zero to hero - Vim 從入門到精通
- 原文地址:https://github.com/mhinz/vim-galore
- 原文作者:Marco Hinz
- 簡介
- 什麼是 Vim?
- Vim 哲學
- 入門
- 精簡的 vimrc
- 我正在使用什麼樣的 Vim
- 備忘錄
- 基礎
- 緩衝區,視窗,標簽
- 已激活、已載入、已列出、已命名的緩衝區
- 參數列表
- 按鍵映射
- 映射前置鍵
- 寄存器
- 範圍
- 標註
- 補全
- 動作,操作符,文本對象
- 自動命令
- 變更歷史,跳轉歷史
- 內容變更歷史記錄
- 全局位置信息表,局部位置信息表
- 巨集
- 顏色主題
- 摺疊
- 會話
- 局部化
- 用法
- 獲取離線幫助
- 獲取離線幫助(補充)
- 獲取線上幫助
- 執行自動命令
- 剪切板
- 打開文件時恢復游標位置
- 臨時文件
- 編輯遠程文件
- 插件管理
- 多行編輯
- 使用外部程式和過濾器
- Cscope
- MatchIt
- 技巧
- 聰明地使用 n 和 N
- 聰明地使用命令行歷史
- 智能 Ctrl-l
- 禁用錯誤報警聲音和圖標
- 快速移動當前行
- 快速添加空行
- 快速編輯自定義巨集
- 快速跳轉到源(頭)文件
- 在 GUI 中快速改變字體大小
- 根據模式改變游標類型
- 防止水平滑動的時候失去選擇
- 重新載入保存文件
- 更加智能的當前行高亮
- 更快的關鍵字補全
- 改變顏色主題的預設外觀
- 命令
- 調試
- 常規建議
- 調整日誌等級
- 查看啟動日誌
- 查看運行時日誌
- vim 腳本調試
- 語法文件調試
- 雜項
- 附加資源
- Vim 配置集合
- 內置插件
- 將 Control 映射到 CapsLock
- 複活節彩蛋
- 為何使用 hjkl
- 常見問題
- 主題列表
- 插件列表
- Neovim
- 加入我們
- 致謝:
簡介
什麼是 Vim?
Vim 是一個歷史悠久的文本編輯器,可以追溯到 qed。Bram
Moolenaar 於 1991 年發佈初始版本。
該項目托管在 vim.org。
獲取 Vim:用包管理器安裝或者直接到 vim.org 下載。
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。
下一步
- 創建你自己的 vimrc。
- 在第一周準備備忘錄。
- 通讀基礎章節瞭解 Vim 還有哪些功能。
- 按需學習!Vim 是學不完的。如果你遇到了問題,先上網尋找解決方案,你的問題可能已經被解決了。Vim 擁有大量的參考文檔,知道如何利用這些參考文檔很有必要:獲取離線幫助。
- 瀏覽附加資源。
最後一個建議:使用插件之前,請先掌握 Vim 的基本操作。很多插件都只是對 Vim 自帶功能的封裝。
返回主目錄 ⤴️
精簡的 vimrc
用戶的 vimrc 配置文件可以放在 ~/.vimrc
,或者為了更好的分離放在 ~/.vim/vimrc
,後者更便於通過版本控制軟體備份和同步整個配置,比方說 Github。
你可以在網上找到許多精簡的 vimrc 配置文件,我的版本可能並不是最簡單的版本,但是我的版本提供了一套我認為良好的,非常適合入門的設置。
最終你需要閱讀完那些設置,然後自行決定需要使用哪些。:-)
精簡的 vimrc 地址:minimal-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 的特性集區分被叫做 tiny
,small
,normal
,big
and huge
,所有的都實現不同的功能子集。
:version
主要的輸出內容是特性列表。+clipboard
意味這剪貼板功能被編譯支持了,-clipboard
意味著剪貼板特性沒有被編譯支持。
一些功能特性需要編譯支持才能正常工作。例如:為了讓 :prof
工作,你需要使用 huge
模式編譯的 Vim,因為那種模式啟用了 +profile
特性。
如果你的輸出情況並不是那樣,並且你是從包管理器安裝 Vim 的,確保你安裝了 vim-x
,vim-x11
,vim-gtk
,vim-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
返回主目錄 ⤴️
備忘錄
為了避免版權問題,我只貼出鏈接:
- http://people.csail.mit.edu/vgod/vim/vim-cheat-sheet-en.png
- https://cdn.shopify.com/s/files/1/0165/4168/files/preview.png
- http://www.nathael.org/Data/vi-vim-cheat-sheet.svg
- http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_screen.png
- http://www.rosipov.com/images/posts/vim-movement-commands-cheatsheet.png
或者在 Vim 中快速打開備忘錄:vim-cheat40。
返回主目錄 ⤴️
基礎
緩衝區,視窗,標簽
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 | 否 | 最近一次的複製或刪除操作 (d , c , s , x , y ) |
Numbered | 0 至9 |
vim | 否 | 寄存器 0 : 最近一次複製。寄存器 1 : 最近一次刪除。寄存器 2 : 倒數第二次刪除,以此類推。對於寄存器 1 至 9 ,他們其實是只讀的最多包含 9 個元素的隊列。這裡的隊列即為數據類型 queue |
Small delete | - |
vim | 否 | 最近一次行內刪除 |
Named | a 至z , A 至Z |
用戶 | 否 | 如果你通過複製操作存儲文本至寄存器 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
返回主目錄 ⤴️
動作,操作符,文本對象
動作也就是指移動游標的操作,你肯定很熟悉 h
、j
、k
和 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
、/
、?
、n
、N
、%
、(
、)
、[[
、]]
、{
、}
、:s
、:tag
、L
、M
、H
以及開始編輯一個新文件的命令。
列表 | 顯示所有條目 | 跳轉到上一個位置 | 跳轉到下一個位置 |
---|---|---|---|
跳轉歷史 | :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
10@q
(對於上面這個功能,你同樣可以通過如下的按鍵: oabc 然後 ESC 然後 10. 來實現)。
實例2:
一個在每行前都加上行號的巨集。從第一行開始,行號為 1,後面依次遞增。我們可以通過 Ctrl + a 來實現遞增的行號,在定義巨集的時候,它會顯示成 ^A
。
qq
0yf jP0^A
q
1000 @q
這裡能實現功能,是因為我們假定了文件最多只有 1000 行。但更好的方式是使用「遞歸」巨集,它會一直執行,知道不能執行為止:
qq
0yf jP0^A@q
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 | 使用 zf 、zF 或 :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
局部化
以上提到的很多概念,都有一個局部化(非全局)的版本: