Vim 從入門到精通【轉(zhuǎn)載】
Vim 從入門到精通
聲明:該文章轉(zhuǎn)載自github - wsdjeg的項(xiàng)目(見如下鏈接),此處僅供查閱方便:https://github.com/wsdjeg/vim-galore-zh_cn#vim-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A- 簡介
- 基礎(chǔ)
- 用法
- 技巧
- 調(diào)試
- 雜項(xiàng)
簡介
什么是 Vim?
Vim 是一個歷史悠久的文本編輯器,可以追溯到
qed。
Bram Moolenaar 于
1991 年發(fā)布初始版本。
Linux、Mac 用戶,可以使用包管理器安裝 Vim,對于 Windows 用戶,可以從
我的網(wǎng)盤 下載。
該版本可輕易添加 python 、python3 、lua 等支持,只需要安裝 python、lua
即可。
項(xiàng)目在 Github 上開發(fā),項(xiàng)目討論請訂閱
vim_dev 郵件列表。
通過閱讀 Why, oh WHY, do those #?@! nutheads use vi?
來對 Vim 進(jìn)行大致的了解。
Vim 哲學(xué)
Vim 采用模式編輯的理念,即它提供了多種模式,按鍵在不同的模式下作用不同。
你可以在普通模式 下瀏覽文件,在插入模式下插入文本,
在可視模式下選擇行,在命令模式下執(zhí)行命令等等。起初這聽起來可能很復(fù)雜,
但是這有一個很大的優(yōu)點(diǎn):不需要通過同時按住多個鍵來完成操作,
大多數(shù)時候你只需要依次按下這些按鍵即可。越常用的操作,所需要的按鍵數(shù)量越少。
和模式編輯緊密相連的概念是 操作符 和 動作。操作符 指的是開始某個行為,
例如:修改、刪除或者選擇文本,之后你要用一個 動作 來指定需要操作的文本區(qū)域。
比如,要改變括號內(nèi)的文本,需要執(zhí)行 ci( (讀做 change inner parentheses);
刪除整個段落的內(nèi)容,需要執(zhí)行 dap (讀做:delete around paragraph)。
如果你能看見 Vim 老司機(jī)操作,你會發(fā)現(xiàn)他們使用 Vim 腳本語言就如同鋼琴師彈鋼琴一樣。
復(fù)雜的操作只需要幾個按鍵就能完成。他們甚至不用刻意去想,因?yàn)檫@已經(jīng)成為肌肉記憶了。
這減少認(rèn)識負(fù)荷并幫助人們專注于實(shí)際任務(wù)。
入門
Vim 自帶一個交互式的教程,內(nèi)含你需要了解的最基礎(chǔ)的信息,你可以通過終端運(yùn)行以下命令打開教程:
$ vimtutor
不要因?yàn)檫@個看上去很無聊而跳過,按照此教程多練習(xí)。你以前用的 IDE 或者其他編輯器很少是有“模式”概念的,因此一開始你會很難適應(yīng)模式切換。但是你 Vim 使用的越多,肌肉記憶 將越容易形成。
Vim 基于一個 vi 克隆,叫做 Stevie,支持兩種運(yùn)行模式:"compatible" 和 "nocompatible"。在兼容模式下運(yùn)行 Vim 意味著使用 vi 的默認(rèn)設(shè)置,而不是 Vim 的默認(rèn)設(shè)置。除非你新建一個用戶的 vimrc 或者使用 vim -N 命令啟動 Vim,否則就是在兼容模式下運(yùn)行 Vim!請大家不要在兼容模式下運(yùn)行 Vim。
下一步
- 創(chuàng)建你自己的 vimrc。
- 在第一周準(zhǔn)備備忘錄。
- 通讀基礎(chǔ)章節(jié)了解 Vim 還有哪些功能。
- 按需學(xué)習(xí)!Vim 是學(xué)不完的。如果你遇到了問題,先上網(wǎng)尋找解決方案,你的問題可能已經(jīng)被解決了。Vim 擁有大量的參考文檔,知道如何利用這些參考文檔很有必要:獲取離線幫助。
- 瀏覽附加資源。
最后一個建議:使用插件之前,請先掌握 Vim 的基本操作。很多插件都只是對 Vim 自帶功能的封裝。
返回主目錄 ??
精簡的 vimrc
Vim 啟動是會按照一定的優(yōu)先順序來搜索配置文件,這個順序,可以通過 :version 命令查看。下面分 Windows 系統(tǒng),
和 *niux 系統(tǒng)分別來說明 Vim 是如何載入配置文件的。
Windows 系統(tǒng)
system vimrc file: "$VIM\vimrc"
user vimrc file: "$HOME\_vimrc"
2nd user vimrc file: "$HOME\vimfiles\vimrc"
3rd user vimrc file: "$VIM\_vimrc"
user exrc file: "$HOME\_exrc"
2nd user exrc file: "$VIM\_exrc"
system gvimrc file: "$VIM\gvimrc"
user gvimrc file: "$HOME\_gvimrc"
2nd user gvimrc file: "$HOME\vimfiles\gvimrc"
3rd user gvimrc file: "$VIM\_gvimrc"
defaults file: "$VIMRUNTIME\defaults.vim"
system menu file: "$VIMRUNTIME\menu.vim"
我們只看上面這一段,Vim 會優(yōu)先讀取 user vimrc file: $HOME\_vimrc, 當(dāng)這一文件不存在是,
Vim 再去尋找 2nd user vimrc file: $HOME\vimfiles\vimrc; 倘若這個文件還是不存在,那么 Vim
會去繼續(xù)尋找 3rd user vimrc file: $VIM\_vimrc。 了解以上順序后,就不會再因?yàn)?Vim
總是不讀取配置文件而感到煩惱了。
Linux 或者 Mac OS
同 Windows 系統(tǒng)類似,也可以使用 :version 命令查看 vim 載入配置的優(yōu)先順序。
系統(tǒng) vimrc 文件: "/etc/vimrc"
用戶 vimrc 文件: "$HOME/.vimrc"
第二用戶 vimrc 文件: "~/.vim/vimrc"
用戶 exrc 文件: "$HOME/.exrc"
defaults file: "$VIMRUNTIME/defaults.vim"
$VIM 預(yù)設(shè)值: "/etc"
$VIMRUNTIME 預(yù)設(shè)值: "/usr/share/vim/vim81"
建議:大多數(shù)插件作者都維護(hù)不止一個插件并且將他們的 vimrc 放在 Github 上展示(通常放在叫做 "vim-config" 或者 "dotfiles" 的倉庫中),所以當(dāng)你發(fā)現(xiàn)你喜歡的插件時,去插件維護(hù)者的 Github 主頁看看有沒有這樣的倉庫。
返回主目錄 ??
我正在使用什么樣的 Vim
使用 :version 命令將向你展示當(dāng)前正在運(yùn)行的 Vim 的所有相關(guān)信息,包括它是如何編譯的。
第一行告訴你這個二進(jìn)制文件的編譯時間和版本號,比如:7.4。接下來的一行呈現(xiàn) Included patches: 1-1051,這是補(bǔ)丁版本包。因此你 Vim 確切的版本號是 7.4.1051。
另一行顯示著一些像 Tiny version without GUI 或者 Huge version with GUI 的信息。很顯然這些信息告訴你當(dāng)前的 Vim 是否支持 GUI,例如:從終端中運(yùn)行 gvim 或者從終端模擬器中的 Vim 內(nèi)運(yùn)行 :gui 命令。另一個重要的信息是 Tiny 和 Huge。Vim 的特性集區(qū)分被叫做 tiny,small,normal,big and huge,所有的都實(shí)現(xiàn)不同的功能子集。
:version 主要的輸出內(nèi)容是特性列表。+clipboard 意味這剪貼板功能被編譯支持了,-clipboard 意味著剪貼板特性沒有被編譯支持。
一些功能特性需要編譯支持才能正常工作。例如:為了讓 :prof 工作,你需要使用 huge 模式編譯的 Vim,因?yàn)槟欠N模式啟用了 +profile 特性。
如果你的輸出情況并不是那樣,并且你是從包管理器安裝 Vim 的,確保你安裝了 vim-x,vim-x11,vim-gtk,vim-gnome 這些包或者相似的,因?yàn)檫@些包通常都是 huge 模式編譯的。
你也可以運(yùn)行下面這段代碼來測試 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
相關(guān)幫助:
:h :version
:h feature-list
:h +feature-list
:h has-patch
返回主目錄 ??
備忘錄
為了避免版權(quán)問題,我只貼出鏈接:
- 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。
返回主目錄 ??
基礎(chǔ)
緩沖區(qū),窗口,標(biāo)簽
Vim 是一個文本編輯器。每次文本都是作為緩沖區(qū)的一部分顯示的。每一份文件都是在他們自己獨(dú)有的緩沖區(qū)打開的,插件顯示的內(nèi)容也在它們自己的緩沖區(qū)中。
緩沖區(qū)有很多屬性,比如這個緩沖區(qū)的內(nèi)容是否可以修改,或者這個緩沖區(qū)是否和文件相關(guān)聯(lián),是否需要同步保存到磁盤上。
窗口 是緩沖區(qū)上一層的視窗。如果你想同時查看幾個文件或者查看同一文件的不同位置,那樣你會需要窗口。
請別把他們叫做 分屏 。你可以把一個窗口分割成兩個,但是這并沒有讓這兩個窗口完全 分離 。
窗口可以水平或者豎直分割并且現(xiàn)有窗口的高度和寬度都是可以被調(diào)節(jié)設(shè)置的,因此,如果你需要多種窗口布局,請考慮使用標(biāo)簽。
標(biāo)簽頁 (標(biāo)簽)是窗口的集合。因此當(dāng)你想使用多種窗口布局時候請使用標(biāo)簽。
簡單的說,如果你啟動 Vim 的時候沒有附帶任何參數(shù),你會得到一個包含著一個呈現(xiàn)一個緩沖區(qū)的窗口的標(biāo)簽。
順帶提一下,緩沖區(qū)列表是全局可見的,你可以在任何標(biāo)簽中訪問任何一個緩沖區(qū)。
返回主目錄 ??
已激活、已載入、已列出、已命名的緩沖區(qū)
用類似 vim file1 的命令啟動 Vim 。這個文件的內(nèi)容將會被加載到緩沖區(qū)中,你現(xiàn)在有一個已載入的緩沖區(qū)。如果你在 Vim 中保存這個文件,緩沖區(qū)內(nèi)容將會被同步到磁盤上(寫回文件中)。
由于這個緩沖區(qū)也在一個窗口上顯示,所以他也是一個已激活的緩沖區(qū)。如果你現(xiàn)在通過 :e file2 命令加載另一個文件,file1 將會變成一個隱藏的緩沖區(qū),并且 file2 變成已激活緩沖區(qū)。
使用 :ls 我們能夠列出所有可以列出的緩沖區(qū)。插件緩沖區(qū)和幫助緩沖區(qū)通常被標(biāo)記為不可以列出的緩沖區(qū),因?yàn)槟遣⒉皇悄憬?jīng)常需要在編輯器中編輯的常規(guī)文件。通過 :ls! 命令可以顯示被放入緩沖區(qū)列表的和未被放入列表的緩沖區(qū)。
未命名的緩沖區(qū)是一種沒有關(guān)聯(lián)特定文件的緩沖區(qū),這種緩沖區(qū)經(jīng)常被插件使用。比如 :enew 將會創(chuàng)建一個無名臨時緩沖區(qū)。添加一些文本然后使用 :w /tmp/foo 將他寫入到磁盤,這樣這個緩沖區(qū)就會變成一個已命名的緩沖區(qū)。
返回主目錄 ??
參數(shù)列表
全局緩沖區(qū)列表是 Vim 的特性。在這之前的 vi 中,僅僅只有參數(shù)列表,參數(shù)列表在 Vim 中依舊可以使用。
每一個通過 shell 命令傳遞給 Vim 的文件名都被記錄在一個參數(shù)列表中??梢杂卸鄠€參數(shù)列表:默認(rèn)情況下所有參數(shù)都被放在全局參數(shù)列表下,但是你可以使用 :arglocal 命令去創(chuàng)建一個新的本地窗口的參數(shù)列表。
使用 :args 命令可以列出當(dāng)前參數(shù)。使用 :next,:previous,:first,:last 命令可以在切換在參數(shù)列表中的文件。通過使用 :argadd,:argdelete 或者 :args 等命令加上一個文件列表可以改變參數(shù)列表。
偏愛緩沖區(qū)列表還是參數(shù)列表完全是個人選擇,我的印象中大多數(shù)人都是使用緩沖區(qū)列表的。
然而參數(shù)列表在有些情況下被大量使用:批處理
使用 :argdo! 一個簡單的重構(gòu)例子:
:args **/*.[ch]
:argdo %s/foo/bar/ge | update
這條命令將替換掉當(dāng)前目錄下以及當(dāng)前目錄的子目錄中所有的 C 源文件和頭文件中的“foo”,并用“bar”代替。
相關(guān)幫助::h argument-list
返回主目錄 ??
按鍵映射
使用 :map 命令家族你可以定義屬于你自己的快捷鍵。該家族的每一個命令都限定在特定的模式下。從技術(shù)上來說 Vim 自帶高達(dá) 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> 可以取消這個映射。
對于更少數(shù),不常見的模式(或者他們的組合),查看 :h map-modes。
到現(xiàn)在為止還好,對新手而言有一個問題會困擾他們::nmap 是遞歸執(zhí)行的!結(jié)果是,右邊執(zhí)行可能的映射。
你自定義了一個簡單的映射去輸出“Foo”:
:nmap b :echo "Foo"<cr>
但是如果你想要映射 b (回退一個單詞)的默認(rèn)功能到一個鍵上呢?
:nmap a b
如果你敲擊a,我們期望著光標(biāo)回退到上一個單詞,但是實(shí)際情況是“Foo”被輸出到命令行里!因?yàn)樵谟疫叄?code>b 已經(jīng)被映射到別的行為上了,換句話說就是 :echo "Foo"<cr>。
解決此問題的正確方法是使用一種 非遞歸 的映射代替:
:nnoremap a b
經(jīng)驗(yàn)法則:除遞歸映射是必須的,否則總是使用非遞歸映射。
通過不給一個右值來檢查你的映射。比如:nmap 顯示所以普通模式下的映射,:nmap <leader> 顯示所有以 <leader> 鍵開頭的普通模式下的映射。
如果你想禁止用標(biāo)準(zhǔn)映射,把他們映射到特殊字符 <nop> 上,例如::noremap <left> <nop>。
相關(guān)幫助:
:h key-notation
:h mapping
:h 05.3
返回主目錄 ??
映射前置鍵
映射前置鍵(Leader 鍵)本身就是一個按鍵映射,默認(rèn)為 \。我們可以通過在 map 中調(diào)用 <leader> 來為把它添加到其他按鍵映射中。
nnoremap <leader>h :helpgrep<space>
這樣,我們只需要先按 \ 然后按 h 就可以激活這個映射 :helpgrep<space>。如果你想通過先按 空格 鍵來觸發(fā),只需要這樣做:
let g:mapleader = ' '
nnoremap <leader>h :helpgrep<space>
此處建議使用 g:mapleader,因?yàn)樵?Vim 腳本中,函數(shù)外的變量缺省的作用域是全局變量,但是在函數(shù)內(nèi)缺省作用域是局部變量,而設(shè)置快捷鍵前綴需要修改全局變量 g:mapleader 的值。
另外,還有一個叫 <localleader> 的,可以把它理解為局部環(huán)境中的 <leader>,默認(rèn)值依然為 \。當(dāng)我們需要只對某一個條件下(比如,特定文件類型的插件)的緩沖區(qū)設(shè)置特別的 <leader> 鍵,那么我們就可以通過修改當(dāng)前環(huán)境下的 <localleader> 來實(shí)現(xiàn)。
注意:如果你打算設(shè)置 Leader 鍵,請確保在設(shè)置按鍵映射之前,先設(shè)置好 Leader 鍵。如果你先設(shè)置了含有 Leader 鍵的映射,然后又修改了 Leader 鍵,那么之前映射內(nèi)的 Leader 鍵是不會因此而改變的。你可以通過執(zhí)行 :nmap <leader> 來查看普通模式中已綁定給 Leader 鍵的所有映射。
請參閱 :h mapleader 與 :h maploacalleader 來獲取更多幫助。
返回主目錄 ??
寄存器
寄存器就是存儲文本的地方。我們常用的「復(fù)制」操作就是把文本存儲到寄存器,「 粘貼」 操作就是把文本從寄存器中讀出來。順便,在 Vim 中復(fù)制的快捷鍵是 y,粘貼的快捷鍵是 p。
Vim 為我們提供了如下的寄存器:
| 類型 | 標(biāo)識 | 讀寫者 | 是否為只讀 | 包含的字符來源 |
|---|---|---|---|---|
| Unnamed | " |
vim | 否 | 最近一次的復(fù)制或刪除操作 (d, c, s, x, y) |
| Numbered | 0至9 |
vim | 否 | 寄存器 0: 最近一次復(fù)制。寄存器 1: 最近一次刪除。寄存器 2: 倒數(shù)第二次刪除,以此類推。對于寄存器 1 至 9,他們其實(shí)是只讀的最多包含 9 個元素的隊(duì)列。這里的隊(duì)列即為數(shù)據(jù)類型 queue |
| Small delete | - |
vim | 否 | 最近一次行內(nèi)刪除 |
| Named | a至z, A至Z |
用戶 | 否 | 如果你通過復(fù)制操作存儲文本至寄存器 a,那么 a 中的文本就會被完全覆蓋。如果你存儲至 A,那么會將文本添加給寄存器 a,不會覆蓋之前已有的文本 |
| Read-only | :與.和% |
vim | 是 | :: 最近一次使用的命令,.: 最近一次添加的文本,%: 當(dāng)前的文件名 |
| Alternate buffer | # |
vim | 否 | 大部分情況下,這個寄存器是當(dāng)前窗口中,上一次訪問的緩沖區(qū)。請參閱 :h alternate-file 來獲取更多幫助 |
| Expression | = |
用戶 | 否 | 復(fù)制 VimL 代碼時,這個寄存器用于存儲代碼片段的執(zhí)行結(jié)果。比如,在插入模式下復(fù)制 <c-r>=5+5<cr>,那么這個寄存器就會存入 10 |
| Selection | +和* |
vim | 否 | * 和 + 是 剪貼板 寄存器 |
| Drop | ~ |
vim | 是 | 最后一次拖拽添加至 Vim 的文本(需要 "+dnd" 支持,暫時只支持 GTK GUI。請參閱 :help dnd 及 :help quote~) |
| Black hole | _ |
vim | 否 | 一般稱為黑洞寄存器。對于當(dāng)前操作,如果你不希望在其他寄存器中保留文本,那就在命令前加上 _。比如,"_dd 命令不會將文本放到寄存器 "、1、+ 或 * 中 |
| Last search pattern | / |
vim | 否 | 最近一次通過 /、? 或 :global 等命令調(diào)用的匹配條件 |
只要不是只讀的寄存器,用戶都有權(quán)限修改它的內(nèi)容,比如:
:let @/ = 'register'
這樣,我們按 n 的時候就會跳轉(zhuǎn)到單詞"register" 出現(xiàn)的地方。
有些時候,你的操作可能已經(jīng)修改了寄存器,而你沒有察覺到。請參閱 :h registers 獲取更多幫助。
上面提到過,復(fù)制的命令是 y,粘貼的命令是 p 或者 P。但請注意,Vim 會區(qū)分「字符選取」與「行選取」。請參閱 :h linewise 獲取更多幫助。
行選取:
命令 yy 或 Y 都是復(fù)制當(dāng)前行。這時移動光標(biāo)至其他位置,按下 p 就可以在光標(biāo)下方粘貼復(fù)制的行,按下 P 就可以在光標(biāo)上方粘貼至復(fù)制的行。
字符選取:
命令 0yw 可以復(fù)制第一個單詞。這時移動光標(biāo)至其他位置,按下 p 就可以在當(dāng)前行、光標(biāo)后的位置粘貼單詞,按下 P 就可以在當(dāng)前行、光標(biāo)前的位置粘貼單詞。
將文本存到指定的寄存器中:
命令 "aY 可以將當(dāng)前行復(fù)制,并存儲到寄存器 a 中。這時移動光標(biāo)至其他位置,通過命令 "AY 就可以把這一行的內(nèi)容擴(kuò)展到寄存器 a 中,而之前存儲的內(nèi)容也不會丟失。
為了便于理解和記憶,建議大家現(xiàn)在就試一試上面提到的這些操作。操作過程中,你可以隨時通過 :reg 來查看寄存器的變化。
有趣的是:
在 Vim 中,y 是復(fù)制命令,源于單詞 "yanking"。而在 Emacs 中,"yanking" 代表的是粘貼(或者說,重新插入剛才刪掉的內(nèi)容),而并不是復(fù)制。
返回主目錄 ??
范圍
范圍 (Ranges) 其實(shí)很好理解,但很多 Vim 用戶的理解不到位。
- 很多命令都可以加一個數(shù)字,用于指明操作范圍
- 范圍可以是一個行號,用于指定某一行
- 范圍也可以是一對通過
,或;分割的行號 - 大部分命令,默認(rèn)只作用于當(dāng)前行
- 只有
:write和:global是默認(rèn)作用于所有行的
范圍的使用是十分直觀的。以下為一些例子(其中,:d 為 :delete 的縮寫):
| 命令 | 操作的行 |
|---|---|
:d |
當(dāng)前行 |
:.d |
當(dāng)前行 |
:1d |
第一行 |
:$d |
最后一行 |
:1,$d |
所有行 |
:%d |
所有行(這是 `1, |
| ------------------- | ----------------------------------------------------------------- |
:d |
當(dāng)前行 |
:.d |
當(dāng)前行 |
:1d |
第一行 |
| 的語法糖) | |
:.,5d |
當(dāng)前行至第 5 行 |
:,5d |
同樣是當(dāng)前行至第 5 行 |
:,+3d |
當(dāng)前行及接下來的 3 行 |
:1,+3d |
第一行至當(dāng)前行再加 3 行 |
:,-3d |
當(dāng)前行及向上的 3 行(Vim 會彈出提示信息,因?yàn)檫@是一個保留的范圍) |
:3,'xdelete |
第三行至標(biāo)注 為 x 的那一行 |
:/^foo/,$delete |
當(dāng)前行以下,以字符 "foo" 開頭的那一行至結(jié)尾 |
:/^foo/+1,$delete |
當(dāng)前行以下,以字符 "foo" 開頭的那一行的下一行至結(jié)尾 |
需要注意的是,; 也可以用于表示范圍。區(qū)別在于,a,b 的 b 是以當(dāng)前行作為參考的。而 a;b 的 b 是以 a 行作為參考的。舉個例子,現(xiàn)在你的光標(biāo)在第 5 行。這時 :1,+1d 會刪除第 1 行至第 6 行,而 :1;+1d 會刪除第 1 行和第 2 行。
如果你想設(shè)置多個尋找條件,只需要在條件前加上 /,比如:
:/foo//bar//quux/d
這就會刪除當(dāng)前行之后的某一行。定位方式是,先在當(dāng)前行之后尋找第一個包含 "foo" 字符的那一行,然后在找到的這一行之后尋找第一個包含 "bar" 字符的那一行,然后再在找到的這一行之后尋找第一個包含 "quux" 的那一行。刪除的就是最后找到的這一行。
有時,Vim 會在命令前自動添加范圍。舉個例子,如果你先通過 V 命令進(jìn)入行選取模式,選中一些行后按下 : 進(jìn)入命令模式,這時候你會發(fā)現(xiàn) Vim 自動添加了 '<,'> 范圍。這表示,接下來的命令會使用之前選取的行號作為范圍。但如果后續(xù)命令不支持范圍,Vim 就會報錯。為了避免這樣的情況發(fā)生,有些人會設(shè)置這樣的按鍵映射::vnoremap foo :<c-u>command,組合鍵 Ctrl + u 可以清除當(dāng)前命令行中的內(nèi)容。
另一個例子是在普通模式中按下 !!,命令行中會出現(xiàn) :.!。如果這時你如果輸入一個外部命令,那么當(dāng)前行的內(nèi)容就會被這個外部命令的輸出替換。你也可以通過命令 :?^$?+1,/^$/-1!ls 把當(dāng)前段落的內(nèi)容替換成外部命令 ls 的輸出,原理是向前和向后各搜索一個空白行,刪除這兩個空白行之間的內(nèi)容,并將外部命令 ls 的輸出放到這兩個空白行之間。
請參閱以下兩個命令來獲取更多幫助:
:h cmdline-ranges
:h 10.3
返回主目錄 ??
標(biāo)注
你可以使用標(biāo)注功能來標(biāo)記一個位置,也就是記錄文件某行的某個位置。
| 標(biāo)注 | 設(shè)置者 | 使用 |
|---|---|---|
a-z |
用戶 | 僅對當(dāng)前的一個文件生效,也就意味著只可以在當(dāng)前文件中跳轉(zhuǎn) |
A-Z |
用戶 | 全局標(biāo)注,可以作用于不同文件。大寫標(biāo)注也稱為「文件標(biāo)注」。跳轉(zhuǎn)時有可能會切換到另一個緩沖區(qū) |
0-9 |
viminfo | 0 代表 viminfo 最后一次被寫入的位置。實(shí)際使用中,就代表 Vim 進(jìn)程最后一次結(jié)束的位置。1 代表 Vim 進(jìn)程倒數(shù)第二次結(jié)束的位置,以此類推 |
如果想跳轉(zhuǎn)到指定的標(biāo)注,你可以先按下 ' / g' 或者 ` / g` 然后按下標(biāo)注名。
如果你想定義當(dāng)前文件中的標(biāo)注,可以先按下 m 再按下標(biāo)注名。比如,按下 mm 就可以把當(dāng)前位置標(biāo)注為 m。在這之后,如果你的光標(biāo)切換到了文件的其他位置,只需要通過 'm 或者 `m即可回到剛才標(biāo)注的行。區(qū)別在于,'m會跳轉(zhuǎn)回被標(biāo)記行的第一個非空字符,而`m會跳轉(zhuǎn)回被標(biāo)記行的被標(biāo)記列。根據(jù) viminfo 的設(shè)置,你可以在退出 Vim 的時候保留小寫字符標(biāo)注。請參閱:h viminfo-' 來獲取更多幫助。
如果你想定義全局的標(biāo)注,可以先按下 m 再按下大寫英文字符。比如,按下 mM 就可以把當(dāng)前文件的當(dāng)前位置標(biāo)注為 M。在這之后,就算你切換到其他的緩沖區(qū),依然可以通過 'M 或 `M 跳轉(zhuǎn)回來。
關(guān)于跳轉(zhuǎn),還有以下的方式:
| 按鍵 | 跳轉(zhuǎn)至 |
|---|---|
'[ 與 `[ |
上一次修改或復(fù)制的第一行或第一個字符 |
'] 與 `] |
上一次修改或復(fù)制的最后一行或最后一個字符 |
'< 與 `< |
上一次在可視模式下選取的第一行或第一個字符 |
'> 與 `> |
上一次在可視模式下選取的最后一行或最后一個字符 |
'' 與 `' |
上一次跳轉(zhuǎn)之前的光標(biāo)位置 |
'" 與 `" |
上一次關(guān)閉當(dāng)前緩沖區(qū)時的光標(biāo)位置 |
'^ 與 `^ |
上一次插入字符后的光標(biāo)位置 |
'. 與 `. |
上一次修改文本后的光標(biāo)位置 |
'( 與 `( |
當(dāng)前句子的開頭 |
') 與 `) |
當(dāng)前句子的結(jié)尾 |
'{ 與 `{ |
當(dāng)前段落的開頭 |
'} 與 `} |
當(dāng)前段落的結(jié)尾 |
標(biāo)注也可以搭配 范圍 一起使用。前面提到過,如果你在可視模式下選取一些文本,然后按下 :,這時候你會發(fā)現(xiàn)命令行已經(jīng)被填充了 :'<,'>。對照上面的表格,現(xiàn)在你應(yīng)該明白了,這段代表的就是可視模式下選取的范圍。
請使用 :marks 命令來顯示所有的標(biāo)注,參閱 :h mark-motions 來獲取關(guān)于標(biāo)注的更多幫助。
返回主目錄 ??
補(bǔ)全
Vim 在插入模式中為我們提供了多種補(bǔ)全方案。如果有多個補(bǔ)全結(jié)果,Vim 會彈出一個菜單供你選擇。
常見的補(bǔ)全有標(biāo)簽、項(xiàng)目中引入的模塊或庫中的方法名、文件名、字典及當(dāng)前緩沖區(qū)的字段。
針對不同的補(bǔ)全方案,Vim 為我們提供了不同的按鍵映射。這些映射都是在插入模式中通過 Ctrl + x 來觸發(fā):
| 映射 | 類型 | 幫助文檔 |
|---|---|---|
<c-x><c-l> |
整行 | :h i^x^l |
<c-x><c-n> |
當(dāng)前緩沖區(qū)中的關(guān)鍵字 | :h i^x^n |
<c-x><c-k> |
字典(請參閱 :h 'dictionary')中的關(guān)鍵字 |
:h i^x^k |
<c-x><c-t> |
同義詞字典(請參閱 :h 'thesaurus')中的關(guān)鍵字 |
:h i^x^t |
<c-x><c-i> |
當(dāng)前文件以及包含的文件中的關(guān)鍵字 | :h i^x^i |
<c-x><c-]> |
標(biāo)簽 | :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> |
用戶自定義補(bǔ)全(通過 'completefunc' 定義) |
:h i^x^u |
<c-x><c-o> |
Omni Completion(通過 'omnifunc' 定義) |
:h i^x^o |
<c-x>s |
拼寫建議 | :h i^Xs |
盡管用戶自定義補(bǔ)全與 Omni Completion 是不同的,但他們做的事情基本一致。共同點(diǎn)在于,他們都是一個監(jiān)聽當(dāng)前光標(biāo)位置的函數(shù),返回值為一系列的補(bǔ)全建議。用戶自定義補(bǔ)全是由用戶定義的,基于用戶的個人用途,因此你可以根據(jù)自己的喜好和需求隨意定制。而 Omni Completion 是針對文件類型的補(bǔ)全,比如在 C 語言中補(bǔ)全一個結(jié)構(gòu)體(struct)的成員(members),或者補(bǔ)全一個類的方法,因而它通常都是由文件類型插件設(shè)置和調(diào)用的。
如果你設(shè)置了 'complete' 選項(xiàng),那么你就可以在一次操作中采用多種補(bǔ)全方案。這個選項(xiàng)默認(rèn)包含了多種可能性,因此請按照自己的需求來配置。你可以通過 <c-n> 來調(diào)用下一個補(bǔ)全建議,或通過 <c-p> 來調(diào)用上一個補(bǔ)全建議。當(dāng)然,這兩個映射同樣可以直接調(diào)用補(bǔ)全函數(shù)。請參閱 :h i^n 與 :h 'complete' 來獲得更多幫助。
如果你想配置彈出菜單的行為,請一定要看一看 :h 'completeopt' 這篇幫助文檔。默認(rèn)的配置已經(jīng)不錯了,但我個人(原作者)更傾向于把 "noselect" 加上。
請參閱以下文檔獲取更多幫助:
:h ins-completion
:h popupmenu-keys
:h new-omni-completion
返回主目錄 ??
動作,操作符,文本對象
動作也就是指移動光標(biāo)的操作,你肯定很熟悉 h、j、k 和 l,以及 w 和 b。但其實(shí),/ 也是一個動作。他們都可以搭配數(shù)字使用,比如 2?the<cr> 可以將光標(biāo)移動到倒數(shù)第二個 "the" 出現(xiàn)的位置。
以下會列出一些常用的動作。你也可以通過 :h navigation 來獲取更多的幫助。
操作符是對某個區(qū)域文本執(zhí)行的操作。比如,d、~、gU 和 > 都是操作符。這些操作符既可以在普通模式下使用,也可以在可視模式下使用。在普通模式中,順序是先按操作符,再按動作指令,比如 >j。在可視模式中,選中區(qū)域后直接按操作符就可以,比如 Vjd。
與動作一樣,操作符也可以搭配數(shù)字使用,比如 2gUw 可以將當(dāng)前單詞以及下一個單詞轉(zhuǎn)成大寫。由于動作和操作符都可以搭配數(shù)字使用,因此 2gU2w 與執(zhí)行兩次 gU2w 效果是相同的。
請參閱 :h operator 來查看所有的操作符。你也可以通過 :set tildeop 命令把 ~ 也變成一個操作符
值得注意的是,動作是單向的,而文本對象是雙向的。文本對象不僅作用于符號(比如括號、中括號和大括號等)標(biāo)記的范圍內(nèi),也作用于整個單詞、整個句子等其他情況。
文本對象不能用于普通模式中移動光標(biāo)的操作,因?yàn)楣鈽?biāo)還沒有智能到可以向兩個方向同時跳轉(zhuǎn)。但這個功能可以在可視模式中實(shí)現(xiàn),因?yàn)樵趯ο蟮囊欢诉x中的情況下,光標(biāo)只需要跳轉(zhuǎn)到另一端就可以了。
文本對象操作一般用 i 或 a 加上對象標(biāo)識符操作,其中 i 表示在對象內(nèi)(英文 inner)操作,a 表示對整個對象(英文 around)操作,這時開頭和結(jié)尾的空格都會被考慮進(jìn)來。舉個例子,diw 可以刪除當(dāng)前單詞,ci( 可以改變括號中的內(nèi)容。
文本對象同樣可以與數(shù)字搭配使用。比如,像 ((( ))) 這樣的文本,假如光標(biāo)位于最內(nèi)層的括號上或最內(nèi)層的括號內(nèi),那么 d2a( 將會刪除從最內(nèi)層開始的兩對括號,以及他們之間的所有內(nèi)容。其實(shí),d2a( 這個操作等同于 2da(。在 Vim 的命令中,如果有兩處都可以接收數(shù)字作為參數(shù),那么最終結(jié)果就等同于兩個數(shù)字相乘。在這里,d 與 a( 都是可以接收參數(shù)的,一個參數(shù)是 1,另一個是 2,我們可以把它們相乘然后放到最前面。
請參閱 :h text-objects 來獲取更多關(guān)于文本對象的幫助。
返回主目錄 ??
自動命令
在特定的情況下,Vim 會傳出事件。如果你想針對這些事件執(zhí)行回調(diào)方法,那么就需要用到自動命令這個功能。
如果沒有了自動命令,那你基本上是用不了 Vim 的。自動命令一直都在執(zhí)行,只是很多時候你沒有注意到。不信的話,可以執(zhí)行命令 :au ,不要被結(jié)果嚇到,這些是當(dāng)前有效的所有自動命令。
請使用 :h {event} 來查看 Vim 中所有事件的列表,你也可以參考 :h autocmd-events-abc 來獲取關(guān)于事件的更多幫助。
一個很常用的例子,就是針對文件類型執(zhí)行某些設(shè)置:
autocmd FileType ruby setlocal shiftwidth=2 softtabstop=2 comments-=:#
但是緩沖區(qū)是如何知道當(dāng)前的文件中包含 Ruby 代碼呢?這其實(shí)是另一個自動命令檢測的到的,然后把文件類型設(shè)置成為 Ruby,這樣就觸發(fā)了上面的 FileType 事件。
在配置 vimrc 的時候,一般第一行加進(jìn)去的就是 filetype on。這就意味著,Vim 啟動時會讀取 filetype.vim 文件,然后根據(jù)文件類型來觸發(fā)相應(yīng)的自動命令。
如果你勇于嘗試,可以查看下 :e $VIMRUNTIME/filetype.vim,然后在輸出中搜索 "Ruby"。這樣,你就會發(fā)現(xiàn)其實(shí) Vim 只是通過文件擴(kuò)展名 .rb 判斷某個文件是不是 Ruby 的。
注意:對于相同事件,如果有多個自動命令,那么自動命令會按照定義時的順序執(zhí)行。通過 :au 就可以查看它們的執(zhí)行順序。
au BufNewFile,BufRead *.rb,*.rbw setf ruby
BufNewFile 與 BufRead 事件是被寫在 Vim 源文件中的。因此,每當(dāng)你通過 :e 或者類似的命令打開文件,這兩個事件都會觸發(fā)。然后,就是讀取 filetype.vim 文件來判斷打開的文件類型。
簡單來說,事件和自動命令在 Vim 中的應(yīng)用十分廣泛。而且,Vim 為我們留出了一些易用的接口,方便用戶配置適合自己的事件驅(qū)動回調(diào)。
請參閱 :h autocommand 來獲取更多幫助
返回主目錄 ??
變更歷史,跳轉(zhuǎn)歷史
在 Vim 中,用戶最近 100 次的文字改動都會被保存在變更歷史中。如果在同一行有多個小改動,那么 Vim 會把它們合并成一個。盡管內(nèi)容改動會合并,但作用的位置還是會只記錄下最后一次改動的位置。
在你移動光標(biāo)或跳轉(zhuǎn)的時候,每一次的移動或跳轉(zhuǎn)前的位置會被記錄到跳轉(zhuǎn)歷史中。類似地,跳轉(zhuǎn)歷史也可以最多保存 100 條記錄。對于每個窗口,跳轉(zhuǎn)記錄是獨(dú)立的。但當(dāng)你分離窗口時(比如使用 :split 命令),跳轉(zhuǎn)歷史會被復(fù)制過去。
Vim 中的跳轉(zhuǎn)命令,包括 '、`、G、/、?、n、N、%、(、)、[[、]]、{、}、:s、:tag、L、M、H 以及開始編輯一個新文件的命令。
| 列表 | 顯示所有條目 | 跳轉(zhuǎn)到上一個位置 | 跳轉(zhuǎn)到下一個位置 |
|---|---|---|---|
| 跳轉(zhuǎn)歷史 | :jumps |
[count]<c-o> |
[count]<c-i> |
| 變更歷史 | :changes |
[count]g; |
[count]g, |
如果你執(zhí)行第二列的命令顯示所有條目,這時 Vim 會用 > 標(biāo)記來為你指示當(dāng)前位置。通常這個標(biāo)記位于 1 的下方,也就代表最后一次的位置。
如果你希望關(guān)閉 Vim 之后還保留這些條目,請參閱 :h viminfo-' 來獲取更多幫助。
注意:上面提到過,最后一次跳轉(zhuǎn)前的位置也會記錄在標(biāo)注中,也可以通過連按 `` 或 '' 跳轉(zhuǎn)到那個位置
請參閱以下兩個命令來獲取更多幫助:
:h changelist
:h jumplist
返回主目錄 ??
內(nèi)容變更歷史記錄
Vim 會記錄文本改變之前的狀態(tài)。因此,你可以使用「撤銷」操作 u 來取消更改,也可以通過「重做」操作 Ctrl + r 來恢復(fù)更改。
值得注意的是,Vim 采用 tree 數(shù)據(jù)結(jié)構(gòu)來存儲內(nèi)容變更的歷史記錄,而不是采用 queue。你的每次改動都會成為存儲為樹的節(jié)點(diǎn)。而且,除了第一次改動(根節(jié)點(diǎn)),之后的每次改動都可以找到一個對應(yīng)的父節(jié)點(diǎn)。每一個節(jié)點(diǎn)都會記錄改動的內(nèi)容和時間。其中,「分支」代表從任一節(jié)點(diǎn)到根節(jié)點(diǎn)的路徑。當(dāng)你進(jìn)行了撤銷操作,然后又輸入了新的內(nèi)容,這時候就相當(dāng)于創(chuàng)建了分支。這個原理和 git 中的 branch(分支)十分類似。
考慮以下這一系列按鍵操作:
ifoo<esc>
obar<esc>
obaz<esc>
u
oquux<exc>
那么現(xiàn)在,Vim 中會顯示三行文本,分別是 "foo"、"bar" 和 "quux"。這時候,存儲的樹形結(jié)構(gòu)如下:
foo(1)
/
bar(2)
/ \
baz(3) quux(4)
這個樹形結(jié)構(gòu)共包含四次改動,括號中的數(shù)字就代表時間順序。
現(xiàn)在,我們有兩種方式遍歷這個樹結(jié)構(gòu)。一種叫「按分支遍歷」,一種叫「按時間遍歷」。
撤銷 u 與重做 Ctrl + r 操作是按分支遍歷。對于上面的例子,現(xiàn)在我們有三行字符。這時候按 u 會回退到 "bar" 節(jié)點(diǎn),如果再按一次 u 則會回退到 "foo" 節(jié)點(diǎn)。這時,如果我們按下 Ctrl + r 就會前進(jìn)至 "bar" 節(jié)點(diǎn),再按一次就回前進(jìn)至 "quux" 節(jié)點(diǎn)。在這種方式下,我們無法訪問到兄弟節(jié)點(diǎn)(即 "baz" 節(jié)點(diǎn))。
與之對應(yīng)的是按時間遍歷,對應(yīng)的按鍵是 g- 和 g+。對于上面的例子,按下 g- 會首先回退到 "baz" 節(jié)點(diǎn)。再次按下 g- 會回退到 "bar" 節(jié)點(diǎn)。
| 命令/按鍵 | 執(zhí)行效果 |
|---|---|
[count]u 或 :undo [count] |
回退到 [count] 次改動之前 |
[count]<c-r> 或 :redo [count] |
重做 [count] 次改動 |
U |
回退至最新的改動 |
[count]g- 或 :earlier [count]? |
根據(jù)時間回退到 [count] 次改動之前。"?" 為 "s"、"m"、"h"、"d" 或 "f"之一。例如,:earlier 2d 會回退到兩天之前。:earlier 1f 則會回退到最近一次文件保存時的內(nèi)容 |
[count]g+ 或 :later [count]? |
類似 g-,但方向相反 |
內(nèi)容變更記錄會儲存在內(nèi)存中,當(dāng) Vim 退出時就會清空。如果需要持久化存儲內(nèi)容變更記錄,請參閱備份文件,交換文件,撤銷文件以及 viminfo 文件的處理章節(jié)的內(nèi)容。
如果你覺得這一部分的內(nèi)容難以理解,請參閱 undotree,這是一個可視化管理內(nèi)容變更歷史記錄的插件。類似的還有 vim-mundo。
請參閱以下鏈接獲取更多幫助:
:h undo.txt
:h usr_32
返回主目錄 ??
全局位置信息表,局部位置信息表
在某一個動作返回一系列「位置」的時候,我們可以利用「全局位置信息表」和「局部位置信息表」來存儲這些位置信息,方便以后跳轉(zhuǎn)回對應(yīng)的位置。每一個存儲的位置包括文件名、行號和列號。
比如,編譯代碼是出現(xiàn)錯誤,這時候我們就可以把錯誤的位置直接顯示在全局位置信息表,或者通過外部抓取工具使位置顯示在局部位置信息表中。
盡管我們也可以把這些信息顯示到一個空格緩沖區(qū)中,但用這兩個信息表顯示的好處在于接口調(diào)用很方便,而且也便于瀏覽輸出。
Vim 中,全局位置信息表只能有一個,但每一個窗口都可以有自己的局部位置信息表。這兩個信息表的外觀看上去很類似,但在操作上會稍有不同。
以下為兩者的操作比較:
| 動作 | 全局位置信息表 | 局部位置信息表 |
|---|---|---|
| 打開窗口 | :copen |
:lopen |
| 關(guān)閉窗口 | :cclose |
:lclose |
| 下一個條目 | :cnext |
:lnext |
| 上一個條目 | :cprevious |
:lprevious |
| 第一個條目 | :cfirst |
:lfirst |
| 最后一個條目 | :clast |
:llast |
請參閱 :h :cc 以及底下的內(nèi)容,來獲取更多命令的幫助。
應(yīng)用實(shí)例:
如果我們想用 grep 遞歸地在當(dāng)前文件夾中尋找某個關(guān)鍵詞,然后把輸出結(jié)果放到全局位置信息表中,只需要這樣:
:let &grepprg = 'grep -Rn $* .'
:grep! foo
<grep output - hit enter>
:copen
執(zhí)行了上面的代碼,你就能看到所有包含字符串 "foo" 的文件名以及匹配到的相關(guān)字段都會顯示在全局位置信息表中。
返回主目錄 ??
宏
你可以在 Vim 中錄制一系列按鍵,并把他們存儲到寄存器中。對于一些需要臨時使用多次的一系列操作,把它們作為宏保存起來會顯著地提升效率。對于一些復(fù)雜的操作,建議使用 Vim 腳本來實(shí)現(xiàn)。
- 首先,按下 q,然后按下你想要保存的寄存器,任何小寫字母都可以。比如我們來把它保存到
q這個寄存器中。按下qq,你會發(fā)現(xiàn)命令行里已經(jīng)顯示了 "recording @q"。 - 如果你已經(jīng)錄制完成,那么只需要再按一次 q 就可以結(jié)束錄制。
- 如果你想調(diào)用剛才錄制的宏,只需要
[count]@q - 如果你想調(diào)用上一次使用的宏,只需要
[count]@@
實(shí)例 1:
一個插入字符串 "abc" 后換行的宏,重復(fù)調(diào)用十次:
qq
iabc<cr><esc>
q
10@q
(對于上面這個功能,你同樣可以通過如下的按鍵: oabc 然后 ESC 然后 10. 來實(shí)現(xiàn))。
實(shí)例 2:
一個在每行前都加上行號的宏。從第一行開始,行號為 1,后面依次遞增。我們可以通過 Ctrl + a 來實(shí)現(xiàn)遞增的行號,在定義宏的時候,它會顯示成 ^A。
qq
0yf jP0^A
q
1000 @q
這里能實(shí)現(xiàn)功能,是因?yàn)槲覀兗俣宋募疃嘀挥?1000 行。但更好的方式是使用「遞歸」宏,它會一直執(zhí)行,知道不能執(zhí)行為止:
qq
0yf jP0^A@q
q
@q
(對于上面這個插入行號的功能,如果你不愿意使用宏,同樣可以通過這段按鍵操作來實(shí)現(xiàn)::%s/^/\=line('.') . '. ')。
這里向大家展示了如何不用宏來達(dá)到相應(yīng)的效果,但要注意,這些不用宏的實(shí)現(xiàn)方式只適用于這些簡單的示例。對于一些比較復(fù)雜的自動化操作,你確實(shí)應(yīng)該考慮使用宏。
請參閱以下文檔獲取更多幫助:
:h recording
:h 'lazyredraw'
返回主目錄 ??
顏色主題
顏色主題可以把你的 Vim 變得更漂亮。Vim 是由多個組件構(gòu)成的,我們可以給每一個組件都設(shè)置不同的文字顏色、背景顏色以及文字加粗等等。比如,我們可以通過這個命令來設(shè)置背景顏色:
:highlight Normal ctermbg=1 guibg=red
執(zhí)行后你會發(fā)現(xiàn),現(xiàn)在背景顏色變成紅色了。請參閱 :h :highlight 來獲取更多幫助。
其實(shí),顏色主題就是一系列的 :highlight 命令的集合。
事實(shí)上,大部分顏色主題都包含兩套配置。一套適用于例如 xterm 和 iTerm 這樣的終端環(huán)境(使用前綴 cterm),另一套適用于例如 gvim 和 MacVim 的圖形界面環(huán)境(使用前綴 gui)。對于上面的例子,ctermbg 就是針對終端環(huán)境的,而 guibg 就是針對圖形界面環(huán)境的。
如果你下載了一個顏色主題,并且在終端環(huán)境中打開了 Vim,然后發(fā)現(xiàn)顯示的顏色與主題截圖中差別很大,那很可能是配置文件只設(shè)置了圖形界面環(huán)境的顏色。反之同理,如果你使用的是圖形界面環(huán)境,發(fā)現(xiàn)顯示顏色有問題,那就很可能是配置文件只設(shè)置了終端環(huán)境的顏色。
第二種情況(圖形界面環(huán)境的顯示問題)其實(shí)不難解決。如果你使用的是 Neovim 或者 Vim 7.4.1830 的后續(xù)版本,可以通過打開真彩色設(shè)置來解決顯示問題。這就可以讓終端環(huán)境的 Vim 使用 GUI 的顏色定義,但首先,你要確認(rèn)一下你的終端環(huán)境和環(huán)境內(nèi)的組件(比如 tmux)是否都支持真彩色??梢钥匆幌?a target="_blank" rel="noopener nofollow">這篇文檔,描述的十分詳細(xì)。
請參閱以下文檔或鏈接來獲取更多幫助:
返回主目錄 ??
折疊
每一部分文字(或者代碼)都會有特定的結(jié)構(gòu)。對于存在結(jié)構(gòu)的文字和代碼,也就意味著它們可以按照一定的邏輯分割成不同區(qū)域。Vim 中的折疊功能,就是按照特定的邏輯把文字和代碼折疊成一行,并顯示一些簡短的描述。折疊功能涉及到很多操作,而且折疊功能可以嵌套使用。
在 Vim 中,有以下 6 中折疊類型:
| 折疊方式 | 概述 |
|---|---|
| diff | 在「比較窗口」中折疊未改變的文本 |
| expr | 使用 'foldexpr' 來創(chuàng)建新的折疊邏輯 |
| indent | 基于縮進(jìn)折疊 |
| manual | 使用 zf、zF 或 :fold 來自定義折疊 |
| marker | 根據(jù)特定的文本標(biāo)記折疊(通常用于代碼注釋) |
| syntax | 根據(jù)語法折疊,比如折疊 if 代碼塊 |
注意:折疊功能可能會顯著地影響性能。如果你在使用折疊功能的時候出現(xiàn)了打字卡頓之類的問題,請考慮使用 FastFold 插件。這個插件可以讓 Vim 按需更新折疊內(nèi)容,而不是一直調(diào)用。
請參閱以下文檔獲取更多幫助:
:h usr_28
:h folds
會話
如果你保存了當(dāng)前的「視圖」(請參閱 :h :mkview),那么當(dāng)前窗口、配置和按鍵映射都會被保存下來(請參閱 :h :loadview)。
「會話」就是存儲所有窗口的相關(guān)設(shè)置,以及全局設(shè)置。簡單來說,就是給當(dāng)前的 Vim 運(yùn)行實(shí)例拍個照,然后把相關(guān)信息存儲到會話文件中。存儲之后的改動就不會在會話文件中顯示,你只需要在改動后更新一下會話文件就可以了。
你可以把當(dāng)前工作的「項(xiàng)目」存儲起來,然后可以在不同的「項(xiàng)目」之間切換。
現(xiàn)在就來試試吧。打開幾個窗口和標(biāo)簽,然后執(zhí)行 :mksession Foo.vim。如果你沒有指定文件名,那就會默認(rèn)保存為 Session.vim。這個文件會保存在當(dāng)前的目錄下,你可以通過 :pwd 來顯示當(dāng)前路徑。重啟 Vim 之后,你只需要執(zhí)行 :source Foo.vim,就可以恢復(fù)剛才的會話了。所有的緩沖區(qū)、窗口布局、按鍵映射以及工作路徑都會恢復(fù)到保存時的狀態(tài)。
其實(shí) Vim 的會話文件就只是 Vim 命令的集合。你可以通過命令 :vs Foo.vim 來看看會話文件中究竟有什么。
你可以決定 Vim 會話中究竟要保存哪些配置,只需要設(shè)置一下 'sessionoptions' 就可以了。
為了方便開發(fā),Vim 把最后一次調(diào)用或?qū)懭氲臅捹x值給了一個內(nèi)部變量 v:this_session。
請參閱以下文檔來獲取更多幫助:
:h Session
:h 'sessionoptions'
:h v:this_session
局部化
以上提到的很多概念,都有一個局部化(非全局)的版本:
| 全局 | 局部 | 作用域 | 幫助文檔 |
|---|---|---|---|
:set |
:setlocal |
緩沖區(qū)或窗口 | :h local-options |
:map |
:map <buffer> |
緩沖區(qū) | :h :map-local |
:autocmd |
:autocmd * <buffer> |
緩沖區(qū) | :h autocmd-buflocal |
:cd |
:lcd |
窗口 | :h :lcd |
:<leader> |
:<localleader> |
緩沖區(qū) | :h maploacalleader |
變量也有不同的作用域,詳細(xì)內(nèi)容請參考 Vim scripting 的文檔。
用法
獲取離線幫助
Vim 自帶了一套很完善的幫助文檔,它們是一個個有固定排版格式的文本文件,通過標(biāo)簽可以訪問這些文件的特定位置。
在開始之前先讀一下這個章節(jié)::help :help。執(zhí)行這個命令以后會在新窗口打開 $VIMRUNTIME/doc/helphelp.txt 文件并跳轉(zhuǎn)到這個文件中 :help 標(biāo)簽的位置。
一些關(guān)于幫助主題的簡單規(guī)則:
- 用單引號把文本包起來表示選項(xiàng),如:
:h 'textwidth' - 以小括號結(jié)尾表示 VimL 函數(shù),如:
:h reverse() - 以英文冒號開頭表示命令,如:
:h :echo
使用快捷鍵 <c-d> (這是 ctrl+d)來列出所有包含你當(dāng)前輸入的內(nèi)容的幫助主題。如::h tab<c-d> 會列出所有包含 tab 主題,從 softtabstop 到 setting-guitablabel (譯者注:根據(jù)安裝的插件不同列出的選項(xiàng)也會不同)。
你想查看所有的 VimL 方法嗎?很簡單,只要輸入::h ()<c-d> 就可以了。你想查看所有與窗口相關(guān)的函數(shù)嗎?輸入 :h win*()<c-d>。
相信你很快就能掌握這些技巧,但是在剛開始的時候,你可能對于該通過什么進(jìn)行查找一點(diǎn)線索都沒有。這時你可以想象一些與要查找的內(nèi)容相關(guān)的關(guān)鍵字,再讓 :helpgrep 來幫忙。
:helpgrep backwards
上面的命令會在所有的幫助文件中搜索“backwards”,然后跳轉(zhuǎn)到第一個匹配的位置。所有的匹配位置都會被添加到全局位置信息表,用 :cp / :cn 可以在匹配位置之間進(jìn)行切換。或者用 :copen 命令來打開全局位置信息表,將光標(biāo)定位到你想要的位置,再按 回車就可以跳轉(zhuǎn)到該匹配項(xiàng)。詳細(xì)說明請參考 :h quickfix。
獲取離線幫助(補(bǔ)充)
這個列表最初發(fā)表在 vim_dev,由 @chrisbra 編輯的,他是 Vim 開發(fā)人員中最活躍的一個。
經(jīng)過一些微小的改動后,重新發(fā)布到了這里。
如果你知道你想要找什么,使用幫助系統(tǒng)的搜索會更簡單一些,因?yàn)樗阉鞒龅闹黝}都帶有固定的格式。
而且?guī)椭到y(tǒng)中的主題包含了你當(dāng)前使用的 Vim 版本的所特有特性,而網(wǎng)上那些已經(jīng)過時或者是早期發(fā)布的話題是不會包含這些的。
因此學(xué)習(xí)使用幫助系統(tǒng)以及它所用的語言是很有必要的。這里是一些例子(不一定全,我有可能忘了一些什么)。
(譯者注:下面列表中提及的都是如何指定搜索主題以便快速準(zhǔn)確的找到你想要的幫助)
-
選項(xiàng)要用單引號引起來。用
:h 'list'來查看列表選項(xiàng)幫助。只有你明確的知道你要找這么一個選項(xiàng)的時候才可以這么做,不然的話你可以用:h options.txt來打開所有選項(xiàng)的幫助頁面,再用正則表達(dá)式進(jìn)行搜索,如:/width。某些選項(xiàng)有它們自己的命名空間,如::h cpo-a,:h cpo-A,:h cpo-b等等。 -
普通模式的命令不能用冒號作為前綴。使用
:h gt來轉(zhuǎn)到“gt”命令的幫助頁面。 -
正則表達(dá)式以“/”開頭,所以
:h /\+會帶你到正則表達(dá)式中量詞“+”的幫助頁面。 -
組合鍵經(jīng)常以一個字母開頭表示它們可以在哪些模式中使用。如:
: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說的是在可見模式下把光標(biāo)所在處的數(shù)字加 1;:h g_CTRL-A則說的是 g 命令(你需要先按 "g" 的命令)。這里的 "g" 代表一個普通的命令,這個命令總是與其它的按鍵組合使用才生效,與 "z" 開始的命令相似。 -
寄存器是以 "quote" 開頭的。如:
:h quote:(譯者注:原文為:h quote,感覺作者想以":"來舉例)來查看關(guān)于":"寄存器的說明。 -
關(guān)于 Vim 腳本(VimL)的幫助都在
:h eval.txt里。而某些方面的語言可以使用:h expr-X獲取幫助,其中的 'X' 是一個特定的字符,如::h expr-!會跳轉(zhuǎn)到描述 VimL 中'!'(非)的章節(jié)。另外一個重要提示,可以使用:h function-list來查看所有函數(shù)的簡要描述,列表中包括函數(shù)名和一句話描述。 -
關(guān)于映射都可以在
:h map.txt中找到。通過:h mapmode-i來查找:imap命令的相關(guān)信息;通過:h map-topic來查找專門針對映射的幫助(譯者注:topic 為一個占位符,正如上面的字符 'X' 一樣,在實(shí)際使用中需要替換成相應(yīng)的單詞)(如::h :map-local查詢本地 buffer 的映射,:h map-bar查詢?nèi)绾卧谟成渲刑幚?|')。 -
命令定義用 "command-" 開頭,如用
:h command-bar來查看自定義命令中'!'的作用。 -
窗口管理類的命令是以 "CTRL-W" 開頭的,所以你可以用
:h CTRL-W_*來查找相應(yīng)的幫助(譯者注:'*'同樣為占位符)(如::h CTRL-W_p查看切換到之前訪問的窗口命令的解釋)。如果你想找窗口處理的命令,還可以通過訪問:h windows.txt并逐行向下瀏覽,所有窗口管理的命令都在這里了。 -
執(zhí)行類的命令以":"開頭,即:
:h :s講的是 "??" 命令。 -
在輸入某個話題時按 CTRL-D,讓 Vim 列出所有的近似項(xiàng)輔助你輸入。
-
用
:helpgrep在所有的幫助頁面(通常還包括了已安裝的插件的幫助頁面)中進(jìn)行搜索。參考:h :helpgrep來了解如何使用。當(dāng)你搜索了一個話題之后,所有的匹配結(jié)果都被保存到了全局位置信息表(或局部位置信息表)當(dāng)中,可以通過:copen或:lopen打開。在打開的窗口中可能通過/對搜索結(jié)果進(jìn)行進(jìn)一步的過濾。 -
:h helphelp里介紹了如何使用幫助系統(tǒng)。 -
用戶手冊。它采用了一種對初學(xué)者更加友好的方式來展示幫助話題。用
:h usr_toc.txt打開目錄(你可能已經(jīng)猜到這個命令的用處了)。瀏覽用戶手冊能幫助你找出某些你想了解的話題,如你可以在第 24 章看到關(guān)于“復(fù)合字符”以及“輸入特殊字符”的講解(用:h usr_24.txt可以快速打開相關(guān)章節(jié))。 -
高亮分組的幫助以
hl-開頭。如::h hl-WarningMsg說的是警告信息分組的高亮。 -
語法高亮以
:syc-開頭,如::h :syn-conceal講的是:syn命令的對于隱藏字符是如何顯示的。 -
快速修復(fù)命令以
:c開頭,而位置列表命令以:l開頭。 -
:h BufWinLeave講的是 BufWinLeave 自動命令。還有,:h autocommand-events(譯者注:原文是:h autocommands-events,但是沒有該幫助)講的是所有可用的事件。 -
啟動參數(shù)都以“-”開頭,如:
:h -f會告訴你 Vim 中 “-f” 參數(shù)的作用。 -
額外的特性都以“+”開頭,如:
:h +conceal講的是關(guān)于隱藏字符的支持。 -
錯誤代碼可以在幫助系統(tǒng)中直接查到。
:h E297會帶你到關(guān)于這一錯誤的詳細(xì)解釋。但是有時并沒有轉(zhuǎn)到錯誤描述,而是列出了經(jīng)常導(dǎo)出這一錯誤的 Vim 命令,如:h E128(譯者注:原文為:h hE128,但是并沒有該幫助)會直接跳轉(zhuǎn)到:function命令。 -
關(guān)于包含的語法文件的文檔的幫助話題格式是
:h ft-*-syntax。如::h ft-c-syntax說的就是 C 語言語法文件以及它所提供的選項(xiàng)。有的語法文件還會帶有自動完成(:h ft-php-omni)或文件類型插件(:h ft-tex-plugin)相關(guān)的章節(jié)可以查看。
另外在每個幫助頁的頂端通常會包含一個用戶文檔鏈接(更多的從從用戶的角度出發(fā)來主角命令的功能和用法,不涉及那么多細(xì)節(jié))。如::h pattern.txt 里包含了 :h 03.9 和 :h usr_27 兩個章節(jié)的鏈接。
獲取在線幫助
如果你遇到了無法解決的問題,或者需要指引的話,可以參考 Vim 使用郵件列表。 IRC 也是一個很不錯的資源。 Freenode 上的 #vim 頻道很龐大,并且里面有許多樂于助人的人。
如果你想給 Vim 提交 Bug 的話,可以使用 vim_dev 郵件列表。
執(zhí)行自動命令
你可以觸發(fā)任何事件,如::doautocmd BufRead。
用戶自定義事件
對于插件而言,創(chuàng)建你自己的自定義事件有時非常有用。
function! Chibby()
" A lot of stuff is happening here.
" And at last..
doautocmd User ChibbyExit
endfunction
現(xiàn)在你插件的用戶可以在 Chibby 執(zhí)行完成之后做任何他想做的事情:
autocmd User ChibbyExit call ChibbyCleanup()
順便提一句,如果在使用 :autocmd 或 :doautocmd 時沒有捕捉異常,那么會輸出 "No matching autocommands" 信息。這也是為什么許多插件用 silent doautocmd ... 的原因。但是這也會有不足,那就是你不能再在 :autocmd 中使用 echo "foo" 了,取而代之的是你要使用 unsilent echo "foo" 來輸出。
這就是為什么要在觸發(fā)事件之前先判斷事件是否存在的原因,
if exists('#User#ChibbyExit')
doautocmd User ChibbyExit
endif
幫助文檔::h User
事件嵌套
默認(rèn)情況下,自動命令不能嵌套!如果某個自動命令執(zhí)行了一個命令,這個命令再依次觸發(fā)其它的事件,這是不可能的。
例如你想在每次啟動 Vim 的時候自動打開你的 vimrc 文件:
autocmd VimEnter * edit $MYVIMRC
當(dāng)你啟動 Vim 的時候,它會幫你打開你的 vimrc 文件,但是你很快會注意到這個文件沒有任何的高亮,盡管平時它是正常可以高亮的。
問題在于你的非嵌套自動命令 :edit 不會觸發(fā)“BufRead”事件,所以并不會把文件類型設(shè)置成“vim”,進(jìn)而 $VIMRUNTIME/syntax/vim.vim 永遠(yuǎn)不會被引入。詳細(xì)信息請參考::au BufRead *.vim。要想完成上面所說的需求,使用下面這個命令:
autocmd VimEnter * nested edit $MYVIMRC
幫助文檔::h autocmd-nested
剪切板
如果你想在沒有 GUI 支持的 Unix 系統(tǒng)中使用 Vim 的 'clipboard' 選項(xiàng),則需要 +clipboard 以及可選的 +xterm_clipboard 兩個特性支持。
幫助文檔:
:h 'clipboard'
:h gui-clipboard
:h gui-selections
另外請參考:持續(xù)粘貼(為什么我每次都要設(shè)置 'paste' 模式
剪貼板的使用(Windows, OSX)
在這兩個系統(tǒng)中都可以用大家習(xí)慣用的 ctrl+c / cmd+c 復(fù)制選擇的文本,然后在另外一個應(yīng)用中用 ctrl+v / cmd+v 進(jìn)行粘貼。
需要注意的是復(fù)制的文本已經(jīng)被發(fā)送到了剪貼板,所以你在粘貼復(fù)制的內(nèi)容之前關(guān)閉這個應(yīng)用是沒有任何問題的。
每次復(fù)制的時候,都會向剪貼板寄存器 * 中寫入數(shù)據(jù)。 而在 Vim 中分別使用 "*y 和 "*p 來進(jìn)行復(fù)制(yank) 和 粘貼(paste)。
如果你不想每次操作都要指定 * 寄存器,可以在你的 vimrc 中添加如下配置:
set clipboard=unnamed
通常情況下復(fù)制/刪除/放入操作會往 " 寄存器中寫入數(shù)據(jù),而加上了上面的配置之后 * 寄存器也會被寫入同樣數(shù)據(jù),因此簡單的使用 y 和 p 就可以復(fù)制粘貼了。
我再說一遍:使用上面的選項(xiàng)意味著每一次的復(fù)制/粘貼,即使在同一個 Vim 窗口里,都會修改剪貼板的內(nèi)容。你自己決定上面的選項(xiàng)是否適合。
如果你覺得輸入 y 還是太麻煩的話,可以使用下面的設(shè)置把在可視模式下選擇的內(nèi)容發(fā)送到剪貼板:
set clipboard=unnamed,autoselect
set guioptions+=a
幫助文檔:
:h clipboard-unnamed
:h autoselect
:h 'go_a'
剪貼板的使用(Linux, BSD, ...)
如果你的系統(tǒng)使用了 X 圖形界面,事情會變得有一點(diǎn)不同。X 圖形界面實(shí)現(xiàn)了 X 窗口系統(tǒng)協(xié)議, 這個協(xié)議在 1987 年發(fā)布的主版本 11,因此 X 也通常被稱為 X11。
在 X10 版本中,剪貼緩沖區(qū)被用來實(shí)現(xiàn)像 clipboard 一樣由 X 來復(fù)制文本,并且可以被所有的程序訪問?,F(xiàn)在這個機(jī)制在 X 中還存在,但是已經(jīng)過時了,很多程序都不再使用這一機(jī)制。
近年來數(shù)據(jù)在程序之間是通過選擇進(jìn)行傳遞的。一共有三種選擇,經(jīng)常用到的有兩種:PRIMARY 和 CLIPBOARD。
選擇的工作工模大致是這樣的:
Program A:<ctrl+c>
Program A:聲稱對 CLIPBOARD 的所有權(quán)
Program B:<ctrl+v>
Program B:發(fā)現(xiàn)CLIPBOARD的所有權(quán)被Program A持有
Program B:從Program A請求數(shù)據(jù)
Program A:響應(yīng)這個請求并發(fā)送數(shù)據(jù)給Program B
Program B:從Program A接收數(shù)據(jù)并插入到窗口中
| 選擇 | 何時使用 | 如何粘貼 | 如何在 Vim 中訪問 |
|---|---|---|---|
| PRIMARY | 選擇文本 | 鼠標(biāo)中鍵, shift+insert | * 寄存器 |
| CLIPBOARD | 選擇文本并按 ctrl+c |
ctrl+v |
+寄存器 |
注意:X 服務(wù)器并不會保存選擇(不僅僅是 CLIPBOARD 選擇)!因此在關(guān)閉了相應(yīng)的程序后,你用 ctrl+c 復(fù)制的內(nèi)容將丟失。
使用 "*p 來貼粘 PRIMARY 選擇中的內(nèi)容,或者使用 "+y1G 來將整個文件的內(nèi)容復(fù)制到 CLIPBOARD 選擇。
如果你需要經(jīng)常訪問這兩個寄存器,可以考慮使用如下配置:
set clipboard^=unnamed " * 寄存器
" 或者
set clipboard^=unnamedplus " + 寄存器
(^= 用來將設(shè)置的值加到默認(rèn)值之前,詳見::h :set^=)
這會使得所有復(fù)制/刪除/放入操作使用 * 或 + 寄存器代替默認(rèn)的未命令寄存器 "。之后你就可以直接使用 y 或 p 訪問你的 X 選擇了。
幫助文檔:
:h clipboard-unnamed
:h clipboard-unnamedplus
打開文件時恢復(fù)光標(biāo)位置
如果沒有這個設(shè)置,每次打開文件時光標(biāo)都將定位在第一行。而加入了這個設(shè)置以后,你就可以恢復(fù)到上次關(guān)閉文件時光標(biāo)所在的位置了。
將下面的配置添加到你的 vimrc 文件:
autocmd BufReadPost *
\ if line("'\"") > 1 && line("'\"") <= line("$") |
\ exe "normal! g`\"" |
\ endif
這是通過判斷之前的光標(biāo)位置是否存在(文件可能被其它程序修改而導(dǎo)致所記錄的位置已經(jīng)不存在了),如果存在的話就執(zhí)行 g`" (轉(zhuǎn)到你離開時的光標(biāo)位置但是不更改跳轉(zhuǎn)列表)。
這需要使用 viminfo 文件::h viminfo-。
臨時文件
根據(jù)選項(xiàng)的不同, Vim 最多會創(chuàng)建 4 種工作文件。
備份文件
你可以讓 Vim 在將修改寫入到文件之前先備份原文件。默認(rèn)情況下, Vim 會保存一個備份文件但是當(dāng)修改成功寫入后會立即刪除它(:set writebackup)。如果你想一直保留這個備份文件的話,可以使用 :set backup。而如果你想禁用備份功能的話,可以使用 :set nobackup nowritebackup。
咱們來看一下上次我在 vimrc 中改了什么:
$ diff ~/.vim/vimrc ~/.vim/files/backup/vimrc-vimbackup
390d389
< command! -bar -nargs=* -complete=help H helpgrep <args>
幫助文檔::h backup
交換文件
假設(shè)你有一個非常棒的科幻小說的構(gòu)思。在按照故事情節(jié)已經(jīng)寫了好幾個小時幾十萬字的時候..忽然停電了!而那時你才想起來你上次保存 ~/來自外太空的邪惡入侵者.txt 是在.. 好吧,你從來沒有保存過。
但是并非沒有希望了!在編輯某個文件的時候, Vim 會創(chuàng)建一個交換文件,里面保存的是對當(dāng)前文件所有未保存的修改。自己試一下,打開任意的文件,并使用 :swapname 獲得當(dāng)前的交換文件的保存路徑。你也可以將 :set noswapfile 加入到 vimrc 中來禁用交換文件。
默認(rèn)情況下,交換文件會自動保存在被編輯文件所在的目錄下,文件名以 .file.swp 后綴結(jié)尾,每當(dāng)你修改了超過 200 個字符或是在之前 4 秒內(nèi)沒有任何動作時更新它的內(nèi)容,在你不再編輯這個文件的時候會被刪除。你可以自己修改這些數(shù)字,詳見::h 'updatecount' 和 :h 'updatetime'。
而在斷電時,交換文件并不會被刪除。當(dāng)你再次打開 vim ~/來自外太空的邪惡入侵者.txt 時, Vim 會提示你恢復(fù)這個文件。
幫助文檔::h swap-file 和 :h usr_11
撤銷文件
內(nèi)容變更歷史記錄是保存在內(nèi)存中的,并且會在 Vim 退出時清空。如果你想讓它持久化到磁盤中,可以設(shè)置 :set undofile。這會把文件 ~/foo.c 的撤銷文件保存在 ~/foo.c.un~。
幫助文檔::h 'undofile' 和 :h undo-persistence
viminfo 文件
備份文件、交換文件和撤銷文件都是與文本狀態(tài)相關(guān)的,而 viminfo 文件是用來保存在 Vim 退出時可能會丟失的其它的信息的。包括歷史記錄(命令歷史、搜索歷史、輸入歷史)、寄存器內(nèi)容、標(biāo)注、緩沖區(qū)列表、全局變量等等。
默認(rèn)情況下,viminfo 被保存在 ~/.viminfo。
幫助文檔::h viminfo 和 :h 'viminfo'
臨時文件管理設(shè)置示例
如果你跟我一樣,也喜歡把這些文件放到一個位置(如:~/.vim/files)的話,可以使用下面的配置:
" 如果文件夾不存在,則新建文件夾
if !isdirectory($HOME.'/.vim/files') && exists('*mkdir')
call mkdir($HOME.'/.vim/files')
endif
" 備份文件
set backup
set backupdir =$HOME/.vim/files/backup/
set backupext =-vimbackup
set backupskip =
" 交換文件
set directory =$HOME/.vim/files/swap//
set updatecount =100
" 撤銷文件
set undofile
set undodir =$HOME/.vim/files/undo/
" viminfo 文件
set viminfo ='100,n$HOME/.vim/files/info/viminfo
注意:如果你在一個多用戶系統(tǒng)中編輯某個文件時, Vim 提示你交換文件已經(jīng)存在的話,可能是因?yàn)橛衅渌挠脩舸藭r正在編輯這個文件。而如果將交換文件放到自己的 home 目錄的話,這個功能就失效了。因此服務(wù)器非常不建議將這些文件修改到 HOME 目錄,避免多人同時編輯一個文件,卻沒有任何警告。
編輯遠(yuǎn)程文件
Vim 自帶的 netrw 插件支持對遠(yuǎn)程文件的編輯。實(shí)際上它將遠(yuǎn)程的文件通過 scp 復(fù)制到本地的臨時文件中,再用那個文件打開一個緩沖區(qū),然后在保存時把文件再復(fù)制回遠(yuǎn)程位置。
下面的命令在你本地的 VIM 配置與 SSH 遠(yuǎn)程服務(wù)器上管理員想讓你使用的配置有沖突時尤其有用:
:e scp://bram@awesome.site.com/.vimrc
如果你已經(jīng)設(shè)置了 ~/.ssh/config,SSH 會自動讀取這里的配置:
Host awesome
HostName awesome.site.com
Port 1234
User bram
如果你的 ~/.ssh/config 中有以上的內(nèi)容,那么下面的命令就可以正常執(zhí)行了:
:e scp://awesome/.vimrc
可以用同樣的方法編輯 ~/.netrc, 詳見::h netrc-netrc。
確保你已經(jīng)看過了 :h netrw-ssh-hack 和 :h g:netrw_ssh_cmd。
另外一種編輯遠(yuǎn)程文件的方法是使用 sshfs,它會用 FUSE 來掛載遠(yuǎn)程的文件系統(tǒng)到你本地的系統(tǒng)當(dāng)中。
插件管理
Pathogen是第一個比較流行的插件管理工具。實(shí)際上它只是修改了 runtimepath (:h 'rtp') 來引入所有放到該目錄下的文件。你需要自己克隆插件的代碼倉庫到那個目錄。
真正的插件管理工具會在 Vim 中提供幫助你安裝或更新插件的命令。以下是一些常用的插件管理工具:
多行編輯
這是一種可以同時輸入多行連續(xù)文本的技術(shù)。參考這個示例。
用 <c-v> 切換到可視塊模式。然后向下選中幾行,按 I 或 A (譯者注:大寫字母,即 shift+i 或 shift+a)然后開始輸入你想要輸入的文本。
在剛開始的時候可能會有些迷惑,因?yàn)槲谋局怀霈F(xiàn)在了當(dāng)前編輯的行,只有在當(dāng)前的插入動作結(jié)束后,之前選中的其它行才會出現(xiàn)插入的文本。
舉一個簡單的例子:<c-v>3jItext<esc>。
如果你要編輯的行長度不同,但是你想在他們后面追加相同的內(nèi)容的話,可以試一下這個:<c-v>3j$Atext<esc>。
有時你可能需要把光標(biāo)放到當(dāng)前行末尾之后,默認(rèn)情況下你是不可能做到的,但是可能通過設(shè)置 virtualedit 選項(xiàng)達(dá)到目的:
set virtualedit=all
設(shè)置之后 $10l 或 90| 都會生效,即使超過了行尾的長度。
詳見 :h blockwise-examples。在開始的時候可能會覺得有些復(fù)雜,但是它很快就會成為你的第二天性的。
如果你想探索更有趣的事情,可以看看多光標(biāo)
使用外部程序和過濾器
免責(zé)聲明:Vim 是單線程的,因此在 Vim 中以前端進(jìn)程執(zhí)行其它的程序時會阻止其它的一切。當(dāng)然你可以使用 Vim 程序接口,如 Lua,并且使用它的多線程支持,但是在那期間, Vim 的處理還是被阻止了。Neovim 添加了任務(wù) API 解決了此問題。
(據(jù)說 Bram 正在考慮在 Vim 中也添加任務(wù)控制。如果你使用了較新版本的的 Vim ,可以看一下 :helpgrep startjob。)
使用 :! 啟動一個新任務(wù)。如果你想列出當(dāng)前工作目錄下的所有文件,可以使用 :!ls。 用 | 來將結(jié)果通過管道重定向,如::!ls -l | sort | tail -n5。
沒有使用范圍時(譯者注:范圍就是 : 和 ! 之間的內(nèi)容,. 表示當(dāng)前行,+4 表示向下偏移 4 行,$ 表示最末行等,多行時用 , 將它們分開,如 .,$ 表示從當(dāng)前行到末行),:! 會顯示在一個可滾動的窗口中(譯者注:在 GVim 和在終端里運(yùn)行的結(jié)果稍有不同)。相反的,如果指定了范圍,這些行會被過濾。這意味著它們會通過管道被重定向到過濾程序的 stdin,在處理后再通過過濾程序的 stdout 輸出,用輸出結(jié)果替換范圍內(nèi)的文本。例如:為接下來的 5 行文本添加行號,可以使用:
:.,+4!nl -ba -w1 -s' '
由于手動添加范圍很麻煩, Vim 提供了一些輔助方法以方便的添加范圍。如果需要經(jīng)常帶著范圍的話,你可以在可見模式中先選擇,然后再按 : (譯者注:選中后再按 ! 更方便)。還可以使用 ! 來取用一個 motion 的范圍,如 !ipsort (譯者注:原文為 !ip!sort ,但經(jīng)過實(shí)驗(yàn)發(fā)現(xiàn)該命令執(zhí)行報錯,可能是因?yàn)?Vim 版本的原因造成的,新版本使用 ip 選擇當(dāng)前段落后自動在命令后添加了 ! ,按照作者的寫法來看,可能之前的版本沒有自動添加 ! )可以將當(dāng)前段落的所有行按字母表順序進(jìn)行排序。
一個使用過濾器比較好的案例是Go 語言。它的縮進(jìn)語法非常個性,甚至還專門提供了一個名為 gofmt 的過濾器來對 Go 語言的源文件進(jìn)行正確的縮進(jìn)。Go 語言的插件通常會提供一個名為 :Fmt 的函數(shù),這個函數(shù)就是執(zhí)行了 :%!gofmt 來對整個文件進(jìn)行縮進(jìn)。
人們常用 :r !prog 將 prog 程序的插入放到當(dāng)前行的下面,這對于腳本來說是很不錯的選擇,但是在使用的過程中我發(fā)現(xiàn) !!ls 更加方便,它會用輸出結(jié)果替換當(dāng)前行的內(nèi)容。(譯者注:前面命令中的 prog 只是個占位符,在實(shí)際使用中需要替換成其它的程序,如 :r !ls,這就與后面的 !!ls 相對應(yīng)了,兩者唯一的不同是第一個命令不會覆蓋當(dāng)前行內(nèi)容,但是第二個命令會)
幫助文檔:
:h filter
:h :read!
Cscope
Cscope 的功能比 ctags 要完善,但是只支持 C(通過設(shè)置 cscope.files 后同樣支持 C++以及 Java)。
鑒于 Tag 文件只是知道某個符號是在哪里定義的,cscope 的數(shù)據(jù)庫里的數(shù)據(jù)信息就多的多了:
- 符號是在哪里定義的?
- 符號是在哪里被使用的?
- 這個全局符號定義了什么?
- 這個變量是在哪里被賦值的?
- 這個函數(shù)在源文件的哪個位置?
- 哪些函數(shù)調(diào)用了這個函數(shù)?
- 這個函數(shù)調(diào)用了哪些函數(shù)?
- "out of space"消息是從哪來的?
- 在目錄結(jié)構(gòu)中當(dāng)前的源文件在哪個位置?
- 哪些文件引用了這個頭文件?
1. 構(gòu)建數(shù)據(jù)庫
在你項(xiàng)目的根目錄執(zhí)行下面的命令:
$ cscope -bqR
這條命令會在當(dāng)前目錄下創(chuàng)建三個文件:cscope{,.in,.po}.out 。把它們想象成你的數(shù)據(jù)庫。
不幸的時 cscope 默認(rèn)只分析 *.[c|h|y|l] 文件。如果你想在 Java 項(xiàng)目中使用 cscope ,需要這樣做:
$ find . -name "*.java" > cscope.files
$ cscope -bq
2. 添加數(shù)據(jù)庫
打開你新創(chuàng)建的數(shù)據(jù)庫連接:
:cs add cscope.out
檢查連接已經(jīng)創(chuàng)建成功:
:cs show
(當(dāng)然你可以添加多個連接。)
3. 查詢數(shù)據(jù)庫
:cs find <kind> <query>
如::cs find d foo 會列出 foo(...) 調(diào)用的所有函數(shù)。
| Kind | 說明 |
|---|---|
| s | symbol:查找使用該符號的引用 |
| g | global:查找該全局符號的定義 |
| c | calls:查找調(diào)用當(dāng)前方法的位置 |
| t | text:查找出現(xiàn)該文本的位置 |
| e | egrep:使用 egrep 搜索當(dāng)前單詞 |
| f | file:打開文件名 |
| i | includes:查詢引入了當(dāng)前文件的文件 |
| d | depends:查找當(dāng)前方法調(diào)用的方法 |
推薦一些比較方便的映射,如:
nnoremap <buffer> <leader>cs :cscope find s <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>cg :cscope find g <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>cc :cscope find c <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>ct :cscope find t <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>ce :cscope find e <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>cf :cscope find f <c-r>=expand('<cfile>')<cr><cr>
nnoremap <buffer> <leader>ci :cscope find i ^<c-r>=expand('<cfile>')<cr>$<cr>
nnoremap <buffer> <leader>cd :cscope find d <c-r>=expand('<cword>')<cr><cr>
所以 :tag (或 <c-]>)跳轉(zhuǎn)到標(biāo)簽定義的文件,而 :cstag 可以達(dá)到同樣的目的,同時還會打開 cscope 的數(shù)據(jù)庫連接。'cscopetag' 選項(xiàng)使得 :tag 命令自動的像 :cstag 一樣工作。這在你已經(jīng)使用了基于標(biāo)簽的映射時會非常方便。
幫助文檔::h cscope
MatchIt
由于 Vim 是用 C 語言編寫的,因此許多功能都假設(shè)使用類似 C 語言的語法。默認(rèn)情況下,如果你的光標(biāo)在 { 或 #endif , 就可以使用 % 跳轉(zhuǎn)到與之匹配的 } 或 #ifdef。
Vim 自帶了一個名為 matchit.vim 的插件,但是默認(rèn)沒有啟用。啟用后可以用 % 在 HTML 相匹配的標(biāo)簽或 VimL 的 if/else/endif 塊之間進(jìn)行跳轉(zhuǎn),它還帶來了一些新的命令。
在 Vim 8 中安裝
" vimrc
packadd! matchit
在 Vim 7 或者更早的版本中安裝
"vimrc
runtime macros/matchit.vim
由于 matchit 的文檔很全面,我建議安裝以后執(zhí)行一次下面的命令:
:!mkdir -p ~/.vim/doc
:!cp $VIMRUNTIME/macros/matchit.vim ~/.vim/doc
:helptags ~/.vim/doc
簡短的介紹
至此這個插件已經(jīng)可以使用了。 參考 :h matchit-intro 來獲得支持的命令以及 :h matchit-languages 來獲得支持的語言。
你可以很方便的定義自己的匹配對,如:
autocmd FileType python let b:match_words = '\<if\>:\<elif\>:\<else\>'
之后你就可以在任何的 Python 文件中使用 % (向前)或 g% (向后)在這三個片斷之間跳轉(zhuǎn)了。
幫助文檔:
:h matchit-install
:h matchit
:h b:match_words
技巧
跳至選擇的區(qū)域另一端
在使用 v 或者 V 選擇某段文字后,可以用 o 或者 O 按鍵跳至選擇區(qū)域的開頭或者結(jié)尾。
:h v_o
:h v_O
聰明地使用 n 和 N
n 與 N 的實(shí)際跳轉(zhuǎn)方向取決于使用 / 還是 ? 來執(zhí)行搜索,其中 / 是向后搜索,? 是向前搜索。一開始我(原作者)覺得這里很難理解。
如果你希望 n 始終為向后搜索,N 始終為向前搜索,那么只需要這樣設(shè)置:
nnoremap <expr> n 'Nn'[v:searchforward]
nnoremap <expr> N 'nN'[v:searchforward]
聰明地使用命令行歷史
我(原作者)習(xí)慣用 Ctrl + p 和 Ctrl + n 來跳轉(zhuǎn)到上一個/下一個條目。其實(shí)這個操作也可以用在命令行中,快速調(diào)出之前執(zhí)行過的命令。
不僅如此,你會發(fā)現(xiàn) 上 和 下 其實(shí)更智能。如果命令行中已經(jīng)存在了一些文字,我們可以通過按方向鍵來匹配已經(jīng)存在的內(nèi)容。比如,命令行中現(xiàn)在是 :echo,這時候我們按 上,就會幫我們補(bǔ)全成 :echo "Vim rocks!"(前提是,之前輸入過這段命令)。
當(dāng)然,Vim 用戶都不愿意去按方向鍵,事實(shí)上我們也不需要去按,只需要設(shè)置這樣的映射:
cnoremap <c-n> <down>
cnoremap <c-p> <up>
這個功能,我(原作者)每天都要用很多次。
智能 Ctrl-l
Ctrl + l 的默認(rèn)功能是清空并「重新繪制」當(dāng)前的屏幕,就和 :redraw! 的功能一樣。下面的這個映射就是執(zhí)行重新繪制,并且取消通過 / 和 ? 匹配字符的高亮,而且還可以修復(fù)代碼高亮問題(有時候,由于多個代碼高亮的腳本重疊,或者規(guī)則過于復(fù)雜,Vim 的代碼高亮顯示會出現(xiàn)問題)。不僅如此,還可以刷新「比較模式」(請參閱 :help diff-mode)的代碼高亮:
nnoremap <leader>l :nohlsearch<cr>:diffupdate<cr>:syntax sync fromstart<cr><c-l>
禁用錯誤報警聲音和圖標(biāo)
set noerrorbells
set novisualbell
set t_vb=
請參閱 Vim Wiki: Disable beeping。
快速移動當(dāng)前行
有時,我(原作者)想要快速把當(dāng)前行上移或下移一行,只需要這樣設(shè)置映射:
nnoremap [e :<c-u>execute 'move -1-'. v:count1<cr>
nnoremap ]e :<c-u>execute 'move +'. v:count1<cr>
這個映射,同樣可以搭配數(shù)字使用,比如連續(xù)按下 2 ] e 就可以把當(dāng)前行向下移動兩行。
快速添加空行
nnoremap [<space> :<c-u>put! =repeat(nr2char(10), v:count1)<cr>'[
nnoremap ]<space> :<c-u>put =repeat(nr2char(10), v:count1)<cr>
設(shè)置之后,連續(xù)按下 5 [ 空格 在當(dāng)前行上方插入 5 個空行。
運(yùn)行時檢測
需要的特性:+profile
Vim 提供了一個內(nèi)置的運(yùn)行時檢查功能,能夠找出運(yùn)行慢的代碼。
:profile 命令后面跟著子命令來確定要查看什么。
如果你想查看所有的:
:profile start /tmp/profile.log
:profile file *
:profile func *
<do something in Vim>
<quit Vim>
Vim 不斷地在內(nèi)存中檢查信息,只在退出的時候輸出出來。(Neovim 已經(jīng)解決了這個問題用 :profile dump 命令)
看一下 /tmp/profile.log 文件,檢查時運(yùn)行的所有代碼都會被顯示出來,包括每一行代碼運(yùn)行的頻率和時間。
大多數(shù)代碼都是用戶不熟悉的插件代碼,如果你是在解決一個確切的問題,
直接跳到這個日志文件的末尾,那里有 FUNCTIONS SORTED ON TOTAL TIME 和 FUNCTIONS SORTED ON SELF TIME 兩個部分,如果某個 function 運(yùn)行時間過長一眼就可以看到。
查看啟動時間
感覺 Vim 啟動的慢?到了研究幾個數(shù)字的時候了:
vim --startuptime /tmp/startup.log +q && vim /tmp/startup.log
第一欄是最重要的因?yàn)樗@示了絕對運(yùn)行時間,如果在前后兩行之間時間差有很大的跳躍,那么是第二個文件太大或者含有需要檢查的錯誤的 VimL 代碼。
NUL 符用新行表示
文件中的 NUL 符 (\0),在內(nèi)存中被以新行(\n)保存,在緩存空間中顯示為 ^@。
更多信息請參看 man 7 ascii 和 :h NL-used-for-Nul 。
快速編輯自定義宏
這個功能真的很實(shí)用!下面的映射,就是在一個新的命令行窗口中讀取某一個寄存器(默認(rèn)為 *)。當(dāng)你設(shè)置完成后,只需要按下 回車 即可讓它生效。
在錄制宏的時候,我經(jīng)常用這個來更改拼寫錯誤。
nnoremap <leader>m :<c-u><c-r><c-r>='let @'. v:register .' = '. string(getreg(v:register))<cr><c-f><left>
只需要連續(xù)按下 leader m 或者 " leader m 就可以調(diào)用了。
請注意,這里之所以要寫成 <c-r><c-r> 是為了確保 <c-r> 執(zhí)行了。請參閱 :h c_^R^R
快速跳轉(zhuǎn)到源(頭)文件
這個技巧可以用在多種文件類型中。當(dāng)你從源文件或者頭文件中切換到其他文件的時候,這個技巧可以設(shè)置「文件標(biāo)記」(請參閱 :h marks),然后你就可以通過連續(xù)按下 ' C 或者 ' H 快速跳轉(zhuǎn)回去(請參閱 :h 'A)。
autocmd BufLeave *.{c,cpp} mark C
autocmd BufLeave *.h mark H
注意:由于這個標(biāo)記是設(shè)置在 viminfo 文件中,因此請先確認(rèn) :set viminfo? 中包含了 :h viminfo-'。
在 GUI 中快速改變字體大小
印象中,我(原作者)記得一下代碼是來自 tpope's 的配置文件:
command! Bigger :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)+1', '')
command! Smaller :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)-1', '')
根據(jù)模式改變光標(biāo)類型
我(原作者)習(xí)慣在普通模式下用塊狀光標(biāo),在插入模式下用條狀光標(biāo)(形狀類似英文 "I" 的樣子),然后在替換模式中使用下劃線形狀的光標(biāo)。
if empty($TMUX)
let &t_SI = "\<Esc>]50;CursorShape=1\x7"
let &t_EI = "\<Esc>]50;CursorShape=0\x7"
let &t_SR = "\<Esc>]50;CursorShape=2\x7"
else
let &t_SI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=1\x7\<Esc>\\"
let &t_EI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=0\x7\<Esc>\\"
let &t_SR = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=2\x7\<Esc>\\"
endif
原理很簡單,就是讓 Vim 在進(jìn)入和離開插入模式的時候,輸出一些序列,請參考 escape sequence。Vim 與終端之間的中間層,比如 tmux 會處理并執(zhí)行上面的代碼。
但上面這個還是有一個缺點(diǎn)的。終端環(huán)境的內(nèi)部原理不盡相同,對于序列的處理方式也稍有不同。因此,上面的代碼可能無法在你的環(huán)境中運(yùn)行。甚至,你的運(yùn)行環(huán)境也有可能不支持其他光標(biāo)形狀,請參閱你的 Vim 運(yùn)行環(huán)境的文檔。
好消息是,上面這個代碼,可以在 iTerm2 中完美運(yùn)行。
防止水平滑動的時候失去選擇
如果你選中了一行或多行,那么你可以用 < 或 > 來調(diào)整他們的縮進(jìn)。但在調(diào)整之后就不會保持選中狀態(tài)了。
你可以連續(xù)按下 g v 來重新選中他們,請參考 :h gv。因此,你可以這樣來配置映射:
xnoremap < <gv
xnoremap > >gv
設(shè)置好之后,在可視模式中使用 >>>>> 就不會再出現(xiàn)上面提到的問題了。
選擇當(dāng)前行至結(jié)尾,排除換行符
在 Vim 里,我們可以同過 v$ 選擇當(dāng)前行至結(jié)尾,但此時會把最后一個換行符也選中,通常需要按額外的 h 來取消最后選中最后一個換行符號。
Vim 提供了一個 g_ 快捷鍵,可以移動光標(biāo)至最后一個非空字符。因此,為達(dá)到次效果,可以使用 vg_。當(dāng)然,如果覺得按三個鍵比較麻煩,
可以添加一個映射:
nnoremap L g_
這樣就可以通過 vL 達(dá)到一樣的效果了。
重新載入保存文件
通過自動命令,你可以在保存文件的同時觸發(fā)一些其他功能。比如,如果這個文件是一個配置文件,那么就重新載入;或者你還可以對這個文件進(jìn)行代碼風(fēng)格檢查。
autocmd BufWritePost $MYVIMRC source $MYVIMRC
autocmd BufWritePost ~/.Xdefaults call system('xrdb ~/.Xdefaults')
更加智能的當(dāng)前行高亮
我(原作者)很喜歡「當(dāng)前行高亮」(請參閱 :h cursorline)這個功能,但我只想讓這個效果出現(xiàn)在當(dāng)前窗口,而且在插入模式中關(guān)閉這個效果:
autocmd InsertLeave,WinEnter * set cursorline
autocmd InsertEnter,WinLeave * set nocursorline
更快的關(guān)鍵字補(bǔ)全
關(guān)鍵字補(bǔ)全(<c-n> 或 <c-p>)功能的工作方式是,無論 'complete' 設(shè)置中有什么,它都會嘗試著去補(bǔ)全。這樣,一些我們用不到的標(biāo)簽也會出現(xiàn)在補(bǔ)全列表中。而且,它會掃描很多文件,有時候運(yùn)行起來非常慢。如果你不需要這些,那么完全可以像這樣把它們禁用掉:
set complete-=i " disable scanning included files
set complete-=t " disable searching tags
改變顏色主題的默認(rèn)外觀
如果你想讓狀態(tài)欄在顏色主題更改后依然保持灰色,那么只需要這樣設(shè)置:
autocmd ColorScheme * highlight StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray gui=NONE
同理,如果你想讓某一個顏色主題(比如 "lucius")的狀態(tài)欄為灰色(請使用 :echo color_name 來查看當(dāng)前可用的所有顏色主題):
autocmd ColorScheme lucius highlight StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray gui=NONE
命令
下面的命令都比較有用,最好了解一下。用 :h :<command name> 來了解更多關(guān)于它們的信息,如::h :global。
:global 和 :vglobal - 在所有匹配行執(zhí)行命令
在所有符合條件的行上執(zhí)行某個命令。如: :global /regexp/ print 會在所有包含 "regexp" 的行上執(zhí)行 print 命令(譯者注:regexp 有正則表達(dá)式的意思,該命令同樣支持正則表達(dá)式,在所有符合正則表達(dá)式的行上執(zhí)行指定的命令)。
趣聞:你們可能都知道老牌的 grep 命令,一個由 Ken Thompson 編寫的過濾程序。它是干什么用的呢?它會輸出所有匹配指定正則表達(dá)式的行!現(xiàn)在猜一下 :global /regexp/ print 的簡寫形式是什么?沒錯!就是 :g/re/p 。 Ken Thompsom 在編寫 grep 程序的時候是受了 vi :global 的啟發(fā)。(譯者注: https://robots.thoughtbot.com/how-grep-got-its-name)
既然它的名字是 :global,理應(yīng)僅作用在所有行上,但是它也是可以帶范圍限制的。假設(shè)你想使用 :delete 命令刪除從當(dāng)前行到下一個空行(由正則表達(dá)式 ^$ 匹配)范圍內(nèi)所有包含 "foo" 的行:
:,/^$/g/foo/d
如果要在所有 不 匹配的行上執(zhí)行命令的話,可以使用 :global! 或是它的別名 :vglobal ( V 代表的是 inVerse )。
:normal 和 :execute - 腳本夢之隊(duì)
這兩個命令經(jīng)常在 Vim 的腳本里使用。
借助于 :normal 可以在命令行里進(jìn)行普通模式的映射。如::normal! 4j 會令光標(biāo)下移 4 行(由于加了"!",所以不會使用自定義的映射 "j")。
需要注意的是 :normal 同樣可以使用范圍數(shù)(譯者注:參考 :h range 和 :h :normal-range 了解更多),故 :%norm! Iabc 會在所有行前加上 "abc"。
借助于 :execute 可以將命令和表達(dá)式混合在一起使用。假設(shè)你正在編輯一個 C 語言的文件,想切換到它的頭文件:
:execute 'edit' fnamemodify(expand('%'), ':r') . '.h'
(譯者注:頭文件為與與源文件同名但是擴(kuò)展名為 .h 的文件。上面的命令中 expand 獲得當(dāng)前文件的名稱,fnamemodify 獲取不帶擴(kuò)展名的文件名,再連上 '.h' 就是頭文件的文件名了,最后在使用 edit 命令打開這個頭文件。)
這兩個命令經(jīng)常一起使用。假設(shè)你想讓光標(biāo)下移 n 行:
:let n = 4
:execute 'normal!' n . 'j'
重定向消息
許多命令都會輸出消息,:redir 用來重定向這些消息。它可以將消息輸出到文件、寄存器或是某個變量中。
" 將消息重定向到變量 `neatvar` 中
:redir => neatvar
" 打印所有寄存器的內(nèi)容
:reg
" 結(jié)束重定向
:redir END
" 輸出變量
:echo neatvar
" 惡搞一下,我們把它輸出到當(dāng)前緩沖區(qū)
:put =neatvar
再 Vim 8 中,可以更簡單的方式即位:
:put =execute('reg')
(譯者注:原文最后一條命令是 :put =nicevar 但是實(shí)際會報變量未定義的錯誤)
(實(shí)測 neovim/vim8 下沒問題)
幫助文檔::h :redir
調(diào)試
常規(guī)建議
如果你遇到了奇怪的行為,嘗試用這個命令重現(xiàn)它:
vim -u NONE -N
這樣會在不引用 vimrc(默認(rèn)設(shè)置)的情況下重啟 vim,并且在 nocompatible 模式下(使用 vim 默認(rèn)設(shè)置而不是 vi 的)。(搜索 :h --noplugin 命令了解更多啟動加載方式)
如果仍舊能夠出現(xiàn)該錯誤,那么這極有可能是 vim 本身的 bug,請給 vim_dev 發(fā)送郵件反饋錯誤,多數(shù)情況下問題不會立刻解決,你還需要進(jìn)一步研究
許多插件經(jīng)常會提供新的(默認(rèn)的/自動的)操作。如果在保存的時候發(fā)生了,那么請用 :verb au BufWritePost 命令檢查潛在的問題
如果你在使用一個插件管理工具,將插件行注釋調(diào),再進(jìn)行調(diào)試。
問題還沒有解決?如果不是插件的問題,那么肯定是你的自定義的設(shè)置的問題,可能是你的 options 或 autocmd 等等。
到了一行行代碼檢查的時候了,不斷地排除縮小檢查范圍知道你找出錯誤,根據(jù)二分法的原理你不會花費(fèi)太多時間的。
在實(shí)踐過程中,可能就是這樣,把 :finish 放在你的 vimrc 文件中間,Vim 會跳過它之后的設(shè)置。如果問題還在,那么問題就出在:finish之前的設(shè)置中,再把:finish放到前一部分設(shè)置的中間位置。否則問題就出現(xiàn)在它后面的半部分設(shè)置,那么就把:finish放到后半部分的中間位置。不斷的重復(fù)即可找到。
調(diào)整日志等級
Vim 現(xiàn)在正在使用的另一個比較有用的方法是增加 debug 信息輸出詳細(xì)等級?,F(xiàn)在 Vim 支持 9 個等級,可以用:h 'verbose'命令查看。
:e /tmp/foo
:set verbose=2
:w
:set verbose=0
這可以顯示出所有引用的文件、沒有變化的文件或者各種各樣的作用于保存的插件。
如果你只是想用簡單的命令來提高等級,也是用 :verbose ,放在其他命令之前,通過計(jì)數(shù)來指明等級,默認(rèn)是 1.
:verb set verbose
" verbose=1
:10verb set verbose
" verbose=10
通常用等級 1 來顯示上次從哪里設(shè)置的選項(xiàng)
:verb set ai?
" Last set from ~/.vim/vimrc
一般等級越高輸出信息月詳細(xì)。但是不要害怕,亦可以把輸出導(dǎo)入到文件中:
:set verbosefile=/tmp/foo | 15verbose echo "foo" | vsplit /tmp/foo
你可以一開始的時候就打開 verbosity,用 -V 選項(xiàng),它默認(rèn)設(shè)置調(diào)試等級為 10。 例如:vim -V5
查看啟動日志
查看運(yùn)行時日志
Vim 腳本調(diào)試
如果你以前使用過命令行調(diào)試器的話,對于:debug命令你很快就會感到熟悉。
只需要在任何其他命令之前加上:debug就會讓你進(jìn)入調(diào)試模式。也就是,被調(diào)試的 Vim 腳本會在第一行停止運(yùn)行,同時該行會被顯示出來。
想了解可用的 6 個調(diào)試命令,可以查閱:h >cont和閱讀下面內(nèi)容。需要指出的是,類似 gdb 和其他相似調(diào)試器,調(diào)試命令可以使用它們的簡短形式:c、 q、n、s、 i和 f。
除了上面的之外,你還可以自由地使用任何 Vim 的命令。比如,:echo myvar,該命令會在當(dāng)前的腳本代碼位置和上下文上被執(zhí)行。
只需要簡單使用:debug 1,你就獲得了REPL調(diào)試特性。
當(dāng)然,調(diào)試模式下是可以定義斷點(diǎn)的,不然的話每一行都去單步調(diào)試就會十分痛苦。(斷點(diǎn)之所以被叫做斷點(diǎn),是因?yàn)檫\(yùn)行到它們的時候,運(yùn)行就會停止下來。因此,你可以利用斷點(diǎn)跳過自己不感興趣的代碼區(qū)域)。請查閱:h :breakadd、 :h :breakdel和 :h :breaklist獲取更多細(xì)節(jié)。
假設(shè)你需要知道你每次在保存一個文件的時候有哪些代碼在運(yùn)行:
:au BufWritePost
" signify BufWritePost
" * call sy#start()
:breakadd func *start
:w
" Breakpoint in "sy#start" line 1
" Entering Debug mode. Type "cont" to continue.
" function sy#start
" line 1: if g:signify_locked
>s
" function sy#start
" line 3: endif
>
" function sy#start
" line 5: let sy_path = resolve(expand('%:p'))
>q
:breakdel *
正如你所見,使用<cr>命令會重復(fù)之前的調(diào)試命令,也就是在該例子中的s命令。
:debug命令可以和verbose選項(xiàng)一起使用。
語法文件調(diào)試
語法文件由于包含錯誤的或者復(fù)制的正則表達(dá)式,常常會使得 Vim 的運(yùn)行較慢。如果 Vim 在編譯的時候包含了+profile feature特性,就可以給用戶提供一個超級好用的:syntime命令。
:syntime on
" 多次敲擊<c-l>來重繪窗口,這樣的話就會使得相應(yīng)的語法規(guī)則被重新應(yīng)用一次
:syntime off
:syntime report
輸出結(jié)果包含了很多的度量維度。比如,你可以通過結(jié)果知道哪些正則表達(dá)式耗時太久需要被優(yōu)化;哪些正則表達(dá)式一直在別使用但重來沒有一次成功匹配。
請查閱:h :syntime。
雜項(xiàng)
附加資源
| 資源名稱 | 簡介 |
|---|---|
| 七個高效的文本編輯習(xí)慣 | 作者:Bram Moolenaar(即 Vim 的作者) |
| 七個高效的文本編輯習(xí)慣 2.0(PDF 版) | 同上 |
| IBM DeveloperWorks: 使用腳本編寫 Vim 編輯器 | Vim 腳本編寫五輯 |
| 《漫漫 Vim 路》 | 使用魔抓定制 Vim 插件 |
| 《 Vim 實(shí)踐 (第 2 版)》 | 輕取 Vim 最佳書籍 |
| Vimcasts.org | Vim 錄屏演示 |
| 為什么是個腳本都用 vi? | 常見誤區(qū)釋疑 |
| 你不愛 vi,所以你不懂 Vim | 簡明,扼要,準(zhǔn)確的干貨 |
Vim 配置集合
目前,網(wǎng)上有很多流行 Vim 配置集合,對于 Vim 配置集合,個人認(rèn)為有利有弊。
對于維護(hù)的比較好的配置,比如 SpaceVim 還是值得嘗試的,可以節(jié)省很多自行配置的時間。
當(dāng)然,網(wǎng)上還有很多其他很流行的配置,比如:
常見問題
編輯小文件時很慢
有兩個因素對性能影響非常大:
-
過于復(fù)雜的 正則表達(dá)式 。尤其是 Ruby 的語法文件,以前會造成性能下降。(見調(diào)試語法文件)
-
屏幕重繪 。有一些功能會強(qiáng)制重繪所有行。
| 典型肇事者 | 原因 | 解決方案 |
|---|---|---|
:set cursorline |
會導(dǎo)致所有行重繪 | :set nocursorline |
:set cursorcolumn |
會導(dǎo)致所有行重繪 | :set nocursorcolumn |
:set relativenumber |
會導(dǎo)致所有行重繪 | :set norelativenumber |
:set foldmethod=syntax |
如果語法文件已經(jīng)很慢了,這只會變得更慢 | :set foldmethod=manual,:set foldmethod=marker 或者使用快速折疊插件 |
:set synmaxcol=3000 |
由于內(nèi)部表示法,Vim 處理比較長的行時會有問題。讓它高亮到 3000 列…… | :set synmaxcol=200 |
| matchparen.vim | Vim 默認(rèn)加載的插件,用正則表達(dá)式查找配對的括號 | 禁用插件::h matchparen |
注意:只有在你真正遇到性能問題的時候才需要做上面的調(diào)整。在大多數(shù)情況下使用上面提到的選項(xiàng)是完全沒有問題的。
編輯大文件的時候很慢
Vim 處理大文件最大的問題就是它會一次性讀取整個文件。這么做是由于緩沖區(qū)的內(nèi)部機(jī)理導(dǎo)致的(在 vim_dev 中討論)。
如果只是想查看的話,tail hugefile | vim - 是一個不錯的選擇。
如果你能接受沒有語法高亮,并且禁用所有插件和設(shè)置的話,使用:
$ vim -u NONE -N
這將會使得跳轉(zhuǎn)變快很多,尤其是省去了基于很耗費(fèi)資源的正則表達(dá)式的語法高亮。你還可以告訴 Vim 不要使用交換文件和 viminfo 文件,以避免由于寫這些文件而造成的延時:
$ vim -n -u NONE -i NONE -N
簡而言之,盡量避免使用 Vim 寫過大的文件。
持續(xù)粘貼(為什么我每次都要設(shè)置 'paste' 模式)
持續(xù)粘貼模式讓終端模擬器可以區(qū)分輸入內(nèi)容與粘貼內(nèi)容。
你有沒有遇到過往 Vim 里粘貼代碼之后被搞的一團(tuán)糟?
這在你使用 cmd+v、shirt-insert、middle-click 等進(jìn)行粘貼的時候才會發(fā)生。
因?yàn)槟菢拥脑捘阒皇窍蚪K端模擬器扔了一大堆的文本。
Vim 并不知道你剛剛是粘貼的文本,它以為你在飛速的輸入。
于是它想縮進(jìn)這些行但是失敗了。
這明顯不是個問題,如果你用 Vim 的寄存器粘貼,如:"+p ,這時 Vim 就知道了你在粘貼,就不會導(dǎo)致格式錯亂了。
使用 :set paste 就可以解決這個問題正常進(jìn)行粘貼。見 :h 'paste' 和 :h 'pastetoggle' 獲取更多信息。
如果你受夠了每次都要設(shè)置 'paste' 的話,看看這個能幫你自動設(shè)置的插件:bracketed-paste。
點(diǎn)此查看該作者對于這個插件的更多描述。
Neovim 嘗試把這些變得更順暢,如果終端支持的話,它會自動開啟持續(xù)粘貼模式,無須再手動進(jìn)行切換。
在終端中按 ESC 后有延時
如果你經(jīng)常使用命令行,那么肯定要接觸 終端模擬器 ,如 xterm、gnome-terminal、iTerm2 等等(與實(shí)際的終端不同)。
終端模擬器與他們的祖輩一樣,使用 轉(zhuǎn)義序列 (也叫 控制序列 )來控制光標(biāo)移動、改變文本顏色等。轉(zhuǎn)義序列就是以轉(zhuǎn)義字符開頭的 ASCII 字符串(用脫字符表示法表示成 ^[ )。當(dāng)遇到這樣的字符串后,終端模擬器會從終端信息數(shù)據(jù)庫中查找對應(yīng)的動作。
為了使用問題更加清晰,我會先來解釋一下什么是映射超時。在映射存在歧義的時候就會產(chǎn)生映射超時:
:nnoremap ,a :echo 'foo'<cr>
:nnoremap ,ab :echo 'bar'<cr>
上面的例子中兩個映射都能正常工作,但是當(dāng)輸入 ,a 之后,Vim 會延時 1 秒,因?yàn)樗_認(rèn)用戶是否還要輸入那個 b。
轉(zhuǎn)義序列會產(chǎn)生同樣的問題:
<esc>作為返回普通模式或取消某個動作的按鍵而被大量使用- 光標(biāo)鍵使用轉(zhuǎn)義序列進(jìn)行的編碼
- Vim 期望 Alt (也叫作 Mate Key )會發(fā)送一個正確的 8-bit 編碼的高位,但是許多終端模擬器并不支持這個(也可能默認(rèn)沒有啟用),而只是發(fā)送一個轉(zhuǎn)義序列作為代替。
你可以這樣測試上面所提到的事情: vim -u NONE -N 然后輸入 i<c-v><left> ,你會看到一個以 ^[ 開頭的字符串,表明這是一個轉(zhuǎn)義序列,^[ 就是轉(zhuǎn)義字符。
簡而言之,Vim 在區(qū)分錄入的 <esc> 和轉(zhuǎn)義序列的時候需要一定的時間。
默認(rèn)情況下,Vim 用 :set timeout timeoutlen=1000,就是說它會用 1 秒的時間來區(qū)分有歧義的映射 以及 按鍵編碼。這對于映射來說是一個比較合理的值,但是你可以自行定義按鍵延時的長短,這是解決該問題最根本的辦法:
set timeout " for mappings
set timeoutlen=1000 " default value
set ttimeout " for key codes
set ttimeoutlen=10 " unnoticeable small value
在 :h ttimeout 里你可以找到一個關(guān)于這些選項(xiàng)之間關(guān)系的小表格。
而如果你在 tmux 中使用 Vim 的話,別忘了把下面的配置加入到你的 ~/.tmux.conf文件中:
set -sg escape-time 0
無法重復(fù)函數(shù)中執(zhí)行的搜索
- 在命令中的搜索(
/、:substitute等)內(nèi)容會改變“上次使用的搜索內(nèi)容”。(它保存在/寄存器中,用:echo @/可以輸出它里面的內(nèi)容) - 簡單的文本變化可以通過
.重做。(它保存在.寄存器,用:echo @.可以輸出它的內(nèi)容)
而在你在函數(shù)中進(jìn)行這些操作的時候,一切就會變得不同。因此你不能用 N/n 查找某個函數(shù)剛剛查找的內(nèi)容,也不能重做函數(shù)中對文本的修改。
幫助文檔::h function-search-undo。

浙公網(wǎng)安備 33010602011771號